STR06-C. strtok() が分割対象文字列を変更しないと想定しない
C の関数 strtok()
は次の 2 つの引数を取る文字列字句分割関数である。引数は、分割対象となる初期文字列、および const
修飾された区切り文字である。この関数は返り値として、字句の 1 文字目を指すポインタ、または字句がない場合は NULL ポインタを返す。
strtok()
の 1 回目の呼び出しで、文字列は字句と区切り文字に分割される。strtok()
関数は、区切り文字が最初に現れるところまでを解析し、区切り文字を null バイト('\0'
)に置き換え、その字句の 1 文字目のアドレスを返す。strtok()
の 2 回目以降の呼び出しでは、直前で呼び出しで置いた null 文字の直後から解析を開始する。
strtok()
は分割対象の元の文字列に変更を加えるため、2 回目以降の呼び出しの結果得られた文字列は安全ではなく、元の形のままとしては使用できない。元の文字列を保存する必要がある場合は、文字列をバッファにコピーし、そのバッファのアドレスを strtok()
に渡す。
違反コード
以下の例では、strtok()
関数を使って第 1 引数をコロンで区切られた字句に分割しているが、分割対象の文字列の各単語を新しい行に出力している。たとえば PATH
が "/usr/bin:/usr/sbin:/sbin"
であるとする。
char *token; char *path = getenv("PATH"); token = strtok(path, ":"); puts(token); while (token = strtok(0, ":")) { puts(token); } printf("PATH: %s\n", path); /* PATH はこの段階では "/usr/bin" となる */
ループが終了すると、path
は "/usr/bin\0/bin\0/usr/sbin\0/sbin\0"
に変更されている。ローカルの path
変数が /usr/bin
になり、環境変数 PATH
が意図せず変更されており、予期しない結果が生じる可能性があるため、これは問題である(「ENV30-C. 関数の返り値によって参照されるオブジェクトを変更しない」を参照)。
適合コード
以下の適合コードでは、字句分割する文字列は、strtok()
の呼び出し後には参照されない一時的な記憶域にコピーされる。
char *token; const char *path = getenv("PATH"); /* PATH は "/usr/bin:/bin:/usr/sbin:/sbin" のような文字列 */ char *copy = (char *)malloc(strlen(path) + 1); if (copy == NULL) { /* エラー処理 */ } strcpy(copy, path); token = strtok(copy, ":"); puts(token); while (token = strtok(0, ":")) { puts(token); } free(copy); copy = NULL; printf("PATH: %s\n", path); /* PATH は "/usr/bin:/bin:/usr/sbin:/sbin" のまま */
ほかの適合コードとして、渡された引数に変更を加えない strtok()
を独自に実装する方法もある。
リスク評価
Linux Programmer's Manual の strtok(3)
[Linux 2008] には次のように記載されている。
この関数を使用してはならない。この関数は第 1 引数に変更を加える。区切り文字自体は失われてしまう。この関数は定数文字列に対しては使用できない。
strtok()
を適切に使用しないと、データが切り捨てられ、以降のプログラム実行において予期しない結果が生じる可能性がある。
レコメンデーション |
深刻度 |
可能性 |
修正コスト |
優先度 |
レベル |
---|---|---|---|---|---|
STR06-C |
中 |
高 |
中 |
P12 |
L1 |
自動検出(最新の情報はこちら)
ツール |
バージョン |
チェッカー |
説明 |
---|---|---|---|
Compass/ROSE |
|
|
|
Fortify SCA |
5.0 |
|
CERT C Rule Pack を使ってこのルールの違反を検出できる。 |
LDRA tool suite |
V. 8.5.4 |
602 S |
実装済み |
関連するガイドライン
CERT C++ Secure Coding Standard | STR06-CPP. Do not assume that strtok() leaves the parse string unchanged |
MITRE CWE | CWE-464, Addition of data structure sentinel |
参考資料
[Linux 2008] | strtok(3) |
翻訳元
これは以下のページを翻訳したものです。
STR06-C. Do not assume that strtok() leaves the parse string unchanged (revision 90)