JPCERT コーディネーションセンター

STR32-C. 文字列を引数にとるライブラリ関数に null 終端されていない文字配列を渡さない

STR32-C. 文字列を引数にとるライブラリ関数に null 終端されていない文字配列を渡さない

多くのライブラリ関数は、文字列が適切に null 終端されているという前提で、文字列やワイド文字列を引数に取る。null 終端されていない文字配列をこれらの関数に渡すと、オブジェクトの境界外のメモリにアクセスしてしまう可能性がある。文字列もしくはワイド文字列を引数に取るライブラリ関数に、null 終端されていない文字配列を渡してはならない。

違反コード

下記のコード例はルールに違反している。理由は、printf() に引数として渡される文字配列 c_str が null 終端されていないからである。(文字配列の適切な初期化方法は「STR11-C. 文字列リテラルで初期化される文字配列のサイズを指定しない」を参照)。

#include <stdio.h>
 
void func(void) {
  char c_str[3] = "abc";
  printf("%s\n", c_str);
}
適合コード

下記の適合コードは配列宣言で文字配列のサイズを指定していない。配列のサイズを省略すると、コンパイラは、null 終端文字を含め文字列リテラルを格納するために十分なサイズのメモリ領域を割り当てる。

#include <stdio.h>
 
void func(void) {
  char c_str[] = "abc";
  printf("%s\n", c_str);
}
違反コード

下記のコード例は本ルールに違反している。wcslen() に渡されるワイド文字列 cur_msg が null 終端されていないからである。

#include <stdlib.h>
#include <wchar.h>
 
wchar_t *cur_msg = NULL;
size_t cur_msg_size = 1024;
size_t cur_msg_len = 0;
 
void lessen_memory_usage(void) {
  wchar_t *temp;
  size_t temp_size;
 
  /* ... */
 
  if (cur_msg != NULL) {
    temp_size = cur_msg_size / 2 + 1;
    temp = realloc(cur_msg, temp_size * sizeof(wchar_t));
    /* temp と cur_msg は null 終端されていないかもしれない */
    if (temp == NULL) {
      /* エラー処理 */
    }
 
    cur_msg = temp;
    cur_msg_size = temp_size;
    cur_msg_len = wcslen(cur_msg);
  }
}
適合コード

この適合コードでは、cur_msgwcslen() に渡されるときに必ず null 終端されている。

#include <stdlib.h>
#include <wchar.h>
 
wchar_t *cur_msg = NULL;
size_t cur_msg_size = 1024;
size_t cur_msg_len = 0;
 
void lessen_memory_usage(void) {
  wchar_t *temp;
  size_t temp_size;
 
  /* ... */
 
  if (cur_msg != NULL) {
    temp_size = cur_msg_size / 2 + 1;
    temp = realloc(cur_msg, temp_size * sizeof(wchar_t));
    /* temp と cur_msg は null 終端されていないかもしれない */
    if (temp == NULL) {
      /* エラー処理 */
    }
 
    cur_msg = temp;
    /* cur_msg を適切に null 終端する */
    cur_msg[temp_size - 1] = L'\0';
    cur_msg_size = temp_size;
    cur_msg_len = wcslen(cur_msg);
  }
}
違反コード (strncpy())

strncpy() 関数は文字列を入力としてとるが、処理結果が null 終端されることは保証しない。下記の違反コード例では、コピー元配列の最初の n 文字に null 文字が含まれていない場合、コピー先の文字列も null 終端されない。null 終端されていない文字列を strlen() に渡したときの動作は未定義である。

#include <string.h>
 
enum { STR_SIZE = 32 };
 
size_t func(const char *source) {
  char c_str[STR_SIZE];
 
  c_str[sizeof(c_str) - 1] = '\0';
  strncpy(c_str, source, sizeof(c_str));
  return strlen(c_str);
}
適合コード (切り捨て)

プログラマが文字列の切り捨てを意図しているのであれば、下記の適合コードは正しい。

#include <string.h>
 
enum { STR_SIZE = 32 };
 
size_t func(const char *source) {
  char c_str[STR_SIZE];
 
  strncpy(c_str, source, sizeof(c_str) - 1);
  c_str[sizeof(c_str) - 1] = '\0';
  return strlen(c_str);
}
適合コード (切り捨て, strncpy_s())

C言語規格、附属書 K の strncpy_s() 関数を使っても文字列の切り捨てコピーを行うことができる。strncpy_s() 関数はコピー元配列からコピー先配列へ最大 n 文字コピーする。コピー元配列から null 文字がコピーされない場合、コピー先配列の n 番目が null 文字に設定され、結果として得られる文字列が null 終端されることが保証される。

#define __STDC_WANT_LIB_EXT1__ 1
#include <string.h>
 
enum { STR_SIZE = 32 };
 
size_t func(const char *source) {
  char a[STR_SIZE];
 
  if (source) {
    errno_t err = strncpy_s(
      a, sizeof(a), source, strlen(source)
    );
    if (err != 0) {
      /* エラー処理 */
    }
  } else {
     /* null ポインタの処理 */
  }
  return strlen_s(s, sizeof(a));
}
適合コード (切り捨てずにコピー)

文字列を切り捨てずにコピーすることを意図している場合、この適合コードではデータをコピーし、コピー先配列が null 終端されることを保証している。文字列をコピーできないときはエラーとして処理される。

#include <string.h>
 
enum { STR_SIZE = 32 };
 
size_t func(const char *source) {
  char c_str[STR_SIZE];
 
  if (source) {
    if (strlen(source) < sizeof(c_str)) {
      strcpy(c_str, source);
    } else {
      /* コピー元の文字列が長過ぎる場合の処理 */
    }
  } else {
    /* null ポインタの処理 */
  }
  return strlen(c_str);
}
リスク評価

文字列を受け取るライブラリ関数に渡す文字列を適切に null 終端しないと、バッファオーバーフローが発生したり、脆弱なプロセスの権限で任意のコードを実行される可能性がある。null 終端エラーは、意図しない情報開示につながる可能性もある。

ルール

深刻度

可能性

修正コスト

優先度

レベル

STR32-C

P12

L1

自動検出(最新の情報はこちら

ツール

バージョン

チェッカー

説明

Compass/ROSE

 

 

このルールの違反を検出できる。

Coverity 6.5 STRING_NULL 実装済み

Klocwork

9.1

NNTS

 

LDRA tool suite

8.5.4

600 S

実装済み

関連するガイドライン
CERT C++ Secure Coding Standard STR32-CPP. Null-terminate character arrays as required
ISO/IEC TR 24772:2013 String Termination [CMJ]
ISO/IEC TS 17961:2013 Passing a non-null-terminated character sequence to a library function that expects a string [strmod]
MITRE CWE CWE-119, Improper Restriction of Operations with the Bounds of a Memory Buffer
CWE-170, Improper null termination
参考資料
[Seacord 2013] Chapter 2, "Strings"
[Viega 2005] Section 5.2.14, "Miscalculated NULL Termination"
翻訳元

これは以下のページを翻訳したものです。

STR32-C. Do not pass a non-null-terminated character sequence to a library function that expects a string (revision 113)

Top へ

Topへ
最新情報(RSSメーリングリストTwitter