MSC39-C. 値が不定の va_list に対して va_arg() を呼び出さない
可変引数関数はその可変引数にアクセスするとき、va_start() を使って va_list 型のオブジェクトを初期化し、va_arg() マクロを反復的に呼び出す。そして最後に va_end() を呼び出す。va_list は別の関数に引数として渡してもよいが、別の関数の中で va_arg() が呼び出されると、呼出し側の関数において va_list の値が 不定となる。そのため、va_list を再初期化せずに可変引数を読み取ろうとすると、予期せぬ動作を引き起こす可能性がある。C 標準、7.16節、第3パラグラフには次のように規定されている[ISO/IEC 9899:2011]。
可変個数の実引数へのアクセスを必要とするとき、呼び出された関数は、型
va_listをもつオブジェクト(この箇条ではapとして参照する。)を宣言しなければならない。オブジェクトapは、他の関数への実引数として渡してもよい。その関数が仮引数apを用いてva_argマクロを呼び出した場合、呼出し側関数でのapの値は、不定となり、apに対するすべての参照に先立ってva_endマクロに渡さなければならない。253253)
va_listへのポインタを作り、そのポインタを他の関数に渡してもよい。その場合、元の関数は、va_listへのポインタを渡した関数から復帰した後でも元のva_listを使い続けてもよい。
違反コード
次の違反コード例では、可変引数がいずれも 0 でないことを検査するために、va_list をヘルパー関数である contains_zero() に渡している。contains_zero() を呼び出した後、ap の値は 不定となる。
#include <stdarg.h>
#include <stdio.h>
int contains_zero(size_t count, va_list ap) {
for (size_t i = 1; i < count; ++i) {
if (va_arg(ap, double) == 0.0) {
return 1;
}
}
return 0;
}
int print_reciprocals(size_t count, ...) {
va_list ap;
va_start(ap, count);
if (contains_zero(count, ap)) {
va_end(ap);
return 1;
}
for (size_t i = 0; i < count; ++i) {
printf("%f ", 1.0 / va_arg(ap, double));
}
va_end(ap);
return 0;
}
適合コード
次の適合コードでは、va_list へのポインタを引数として取るように contains_zero() を修正している。その上で、va_copy マクロを使用して va_list のコピーを作成し、コピーに対して処理を行っている。処理が終ったら、作成したコピーに対して va_end マクロによる後処理を行っている。こうすることで、print_reciprocals 関数は、元の va_list に自由にアクセスすることができる。
#include <stdarg.h>
#include <stdio.h>
int contains_zero(size_t count, va_list *ap) {
va_list ap1;
va_copy(ap1, *ap);
for (size_t i = 1; i < count; ++i) {
if (va_arg(ap1, double) == 0.0) {
return 1;
}
}
va_end(ap1);
return 0;
}
int print_reciprocals(size_t count, ...) {
int status;
va_list ap;
va_start(ap, count);
if (contains_zero(count, &ap)) {
printf("0 in arguments!\n");
status = 1;
} else {
for (size_t i = 0; i < count; i++) {
printf("%f ", 1.0 / va_arg(ap, double));
}
printf("\n");
status = 0;
}
va_end(ap);
return status;
}
リスク評価
値が不定の va_list を使って可変引数の読み取りを行うと、予期せぬ結果が生じる可能性がある。
|
ルール |
深刻度 |
可能性 |
修正コスト |
優先度 |
レベル |
|---|---|---|---|---|---|
|
MSC39-C |
低 |
低 |
低 |
P3 |
L3 |
参考資料
| [ISO/IEC 9899:2011] | Subclause 7.16, "Variable Arguments <stdarg.h>" |
翻訳元
これは以下のページを翻訳したものです。
MSC39-C. Do not call va_arg() on a va_list that has an indeterminate value (revision 27)



