INT15-C. プログラマ定義の整数型に対する書式付き入出力には、intmax_t もしくは uintmax_t を使用する
書式付き入出力関数と型定義にまつわる問題について認識しているプログラマはほとんどいない。プログラマ定義の整数型は処理系がサポートするどのような型にもなりえる。たとえば unsigned long long
型より大きな型かもしれない。
128ビット符号無し整数をサポートする処理系で uint_fast128_t
型が使える場合、プログラマは以下のような型を定義するかもしれない:
typedef uint_fast128_t mytypedef_t;
さらに、プログラマが定義した型はコードメンテナンスで変更されるかもしれない。これは、書式付き出力関数である printf()
や書式付き入力関数である scanf()
などを使用する際に問題になる。(「FIO47-C. 書式指定文字列を正しく使う」を参照。)
C の intmax_t
および uintmax_t
型はそれぞれ、符号付きの他の型、あるいは符号無しの他の型が表現できるいかなる値であっても表現できる(「INT00-C. 処理系のデータモデルについて理解する」を参照。) したがって、プログラマが定義した整数型(符号の有無が同じ)と intmax_t
もしくは uintmax_t
の間の型変換が可能である。
mytypedef_t x; uintmax_t temp; temp = x; /* 常に安全 */ /* ... temp の値を変更 ... */ if (temp <= MYTYPEDEF_MAX) { x = temp; }
書式付き入出力関数では、最大幅の整数型の値の入出力が可能である。書式指定文字列中の長さ修飾子 j
は、それに続く変換指定子 d
, i
, o
, u
, x
, X
, n
が、intmax_t
もしくは uintmax_t
型の引数に適用されることを示す。また C では、size_t
型引数に使うための長さ指定子 z
や ptrdiff_t
型の引数に使うための長さ指定子 t
も定義している。
プログラマ定義の型は言うにおよばず、処理系定義の整数型に対応する長さ修飾子を処理系が提供することは、言語規格上要求されてはいない。たとえば、処理系定義の48ビット整数型をもつマシンで、その型に対応する長さ修飾子は使えないかもしれない。しかしその場合でも、64ビット幅の long long
型と、それに対応できる intmax_t
型は提供されているはずである。
違反コード (printf()
)
以下のコード例は、プログラマ定義の整数型である x
の値を unsigned long long
型の値として出力する。
#include <stdio.h> mytypedef_t x; /* ... */ printf("%llu", (unsigned long long) x);
このコードが x
の値を正しく出力する保証はない。x
は unsigned long long
では表現できないくらい大きな値かもしれない。
適合コード (printf()
)
Cの intmax_t
型や uintmax_t
型を使えば、プログラマ定義の整数型を使った書式付き入出力を安全に行うことができる。出力の場合は、プログラマ定義の整数型を符号の有無に合わせて intmax_t
型か uintmax_t
型に変換し、その後長さ修飾子 j
を使用してこれらの値を出力すればよい。入力の場合は、まず intmax_t
型か uintmax_t
型の変数に入力を行い、適切な範囲チェックをした後でプログラマ定義の型の変数に代入すればよい。
以下の適合コードは、mytypedef_t
が符号無し型であることを前提に、その長さに関係なく x
の正しい値が出力されることを保証している。
#include <stdio.h> #include <inttypes.h> mytypedef_t x; /* ... */ printf("%ju", (uintmax_t) x);
適合コード (Microsoft printf()
)
Visual Studio 2012 とそれ以前のバージョンでは、j
長さ修飾子はサポートされていないか、非標準の拡張が提供されている。このため、Microsoft Visual Studio では、intmax_t
の代わりに int64_t
を使う、uintmax_t
の代わりに
を使う、といったコーディングを行う必要がある。uint64_t
#include <stdio.h> #include <inttypes.h> mytypedef_t x; /* ... */ #ifdef _MSC_VER printf("%llu", (uintmax_t) x); #else printf("%ju", (uintmax_t) x); #endif
Microsoft Visual Studio の将来のリリースで長さ修飾子 j
のサポートを追加してほしいというリクエストが、Microsoft に対して提出されている。
違反コード (scanf()
)
以下のコード例は、標準入力から unsigned long long
型の値を読み込み、その結果をプログラマ定義の整数型 x
に格納している。
#include <stdio.h> mytypedef_t x; /* ... */ if (scanf("%llu", &x) != 1) { /* エラー処理 */ }
mytypedef_t
のサイズが unsigned long long
より小さい場合、このコードはバッファオーバーフローにつがなるおそれがある。また、mytypedef_t
が unsigned long long
より大きい場合、間違った値になるおそれがある。
適合コード (scanf()
)
以下の適合コードでは、MYTYPEDEF_MAX
がmytypedef_t
型が表現できる最大値を正しく表していることを前提に、mytypedef_t
型の範囲にある正しい値が読み取られるか、エラー条件が検知されることを保証している。
#include <stdio.h> #include <inttypes.h> mytypedef_t x; uintmax_t temp; /* ... */ if (scanf("%ju", &temp) != 1) { /* エラー処理 */ } if (temp > MYTYPEDEF_MAX) { /* エラー処理 */ } else { x = temp; }
適合コード (Microsoft scanf()
)
Visual Studio 2012 とそれ以前のバージョンでは、j
長さ修飾子はサポートされていないか、非標準の拡張が提供されている。このため、Microsoft Visual Studio では、intmax_t
の代わりに int64_t
を使う、uintmax_t
の代わりに
を使う、といったコーディングを行う必要がある。uint64_t
#include <stdio.h> #include <inttypes.h> mytypedef_t x; uintmax_t temp; /* ... */ #ifdef _MSC_VER # define UINTMAX_CS "%llu" #else # define UINTMAX_CS "%ju" #endif if (scanf(UINTMAX_CS, &temp) != 1) { /* エラー処理 */ } if (temp > MYTYPEDEF_MAX) { /* エラー処理 */ } else { x = temp; }
Microsoft Visual Studio の将来のリリースで長さ修飾子 j
のサポートを追加してほしいというリクエストが、Microsoft に対して提出されている。
リスク評価
プログラマ定義の整数型を入出力する際に適切な変換指定子を使用しそこねると、バッファオーバーフローやデータの欠損、誤解釈につながるおそれがある。
レコメンデーション |
深刻度 |
可能性 |
修正コスト |
優先度 |
レベル |
---|---|---|---|---|---|
INT15-C |
高 |
低 |
中 |
P6 |
L2 |
自動検出
ツール |
バージョン |
チェッカー |
説明 |
---|---|---|---|
Compass/ROSE |
|
|
|
LDRA tool suite |
V. 8.5.4 |
439 S |
部分的に実装済み |
関連するガイドライン
CERT C++ Secure Coding Standard | INT15-CPP. Use intmax_t or uintmax_t for formatted IO on programmer-defined integer types |
MITRE CWE | CWE-681, Incorrect conversion between numeric types |
翻訳元
これは以下のページを翻訳したものです。
INT15-C. Use intmax_t or uintmax_t for formatted IO on programmer-defined integer types (revision 55)