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)