メインコンテンツ

このページの内容は最新ではありません。最新版の英語を参照するには、ここをクリックします。

CERT C: Rule ARR39-C

Do not add or subtract a scaled integer to a pointer

説明

ルール定義

スケーリングされている整数をポインターに対して加算したり減算したりしないようにします。1

Polyspace 実装

ルール チェッカーは、"ポインターのスケーリングが無効です" をチェックします。

すべて展開する

問題

ポインターのスケーリングが無効ですは、Polyspace® Bug Finder™ によって、ポインター演算での暗黙的なスケーリングが無視されているとみなされる場合に発生します。

たとえば、欠陥は次のような状況で発生します。

状態リスク考えられる解決方法
ポインターでの算術演算で sizeof 演算子を使用する。

sizeof 演算子が、データ型のサイズをバイト数で返す。

ポインター演算は既に、指されている変数のデータ型のサイズによって暗黙的にスケーリングされている。そのため、ポインター演算で sizeof を使用すると想定外の結果が生成される。

ポインター演算で sizeof 演算子を使用しない。
ポインターに対し算術演算を実行してから、キャストを適用する。ポインター演算は暗黙的にスケーリングされている。この暗黙的なスケーリングを考慮せずにポインター演算の結果をキャストすると、想定外の結果が生成される。キャストをポインター演算の前に適用する。

修正方法

修正方法は欠陥の根本原因によって異なります。多くの場合、結果の詳細には欠陥につながる一連のイベントが表示されます。そのシーケンス内のどのイベントについても修正を実装できます。結果の詳細にイベント履歴が表示されない場合は、ソース コード内で右クリック オプションを使用して逆のトレースを行い、これまでの関連するイベントを確認できます。Polyspace デスクトップ ユーザー インターフェイスでの Bug Finder の結果の解釈も参照してください。

以下の修正例を参照してください。

問題を修正しない場合は、改めてレビューされないように結果またはコードにコメントを追加します。詳細は、以下を参照してください。

例 — sizeof 演算子の使用
void func(void) {
    int arr[5] = {1,2,3,4,5};
    int *ptr = arr;

    int value_in_position_2 = *(ptr + 2*(sizeof(int)));//Noncompliant //Noncompliant
}

この例では、演算 2*(sizeof(int)) で変数 int の 2 倍のサイズ (バイト単位) が返されます。ただし、ポインター演算は暗黙的にスケーリングされているため、ptr がオフセットされるバイト数は 2*(sizeof(int))*(sizeof(int)) となります。

この例では、不正確なスケーリングにより、ptr が配列の境界外にシフトされます。したがって、[範囲外にアクセスするポインター] エラーが * 演算に表示されます。

修正 — sizeof 演算子を削除

1 つの修正方法として、sizeof 演算子を削除します。

void func(void) {
    int arr[5] = {1,2,3,4,5};
    int *ptr = arr;

    int value_in_position_2 = *(ptr + 2);
}
例 — ポインター演算後のキャスト
int func(void) {
    int x = 0;
    char r = *(char *)(&x + 1); //Noncompliant
    return r;
}

この例では、演算 &x + 1 により &xsizeof(int) の分オフセットされます。演算後、その結果であるポインターは、許容されているバッファーの外を指しています。ポインターをデリファレンスすると、[範囲外にアクセスするポインター] エラーが * 演算に表示されます。

修正 — キャストをポインター演算の前に適用

x の 2 番目のバイトにアクセスする場合は、まず &xchar* ポインターにキャストしてからポインター演算を実行します。その結果得られるポインターは、sizeof(char) バイト分オフセットされたうえで、サイズが sizeof(int) バイトの許容されているバッファー内を指しています。

int func(void) {
    int x = 0;
    char r = *((char *)(&x )+ 1);
    return r;
}
例 — 関数の引数での sizeof の使用
#include <stddef.h>
#include <stdlib.h>
#include <wchar.h>
enum { WCHAR_BUF = 128 };
FILE* pFile;
//...
void func2_ko (void)
{
	wchar_t error_msg[WCHAR_BUF];
	wcscpy (error_msg, L"Error: ");
	fgetws (error_msg + wcslen (error_msg)   //Noncompliant
	       * sizeof (wchar_t), WCHAR_BUF - 7, pFile);
}

この例では、エラー メッセージがファイル ポインター pFile ストリームから読み取られ、オフセットの後の error_msg にコピーされます。ここで意図されているオフセットは wcslen(error_msg) であり、これは wchar ポインター error_msg への追加時に既に暗黙的にスケーリングされています。オフセットはその後 sizeof を使用して再度明示的にスケーリングされているため、Polyspace はこの誤ったスケーリングにフラグを設定します。

修正 — sizeof 演算子を削除

1 つの修正方法として、sizeof 演算子を削除します。

#include <stddef.h>
#include <stdlib.h>
#include <wchar.h>
enum { WCHAR_BUF = 128 };
const wchar_t ERROR_PREFIX[8] = L"Error: ";
FILE* pFile;
//...

void func2_ok (void)
{
  const size_t prefix_len = wcslen (ERROR_PREFIX);
  wchar_t error_msg[WCHAR_BUF];
  wcscpy (error_msg, ERROR_PREFIX);
  fgetws (error_msg + prefix_len, WCHAR_BUF - prefix_len, pFile);  //Compliant
  /* ... */
}
例 — memset の呼び出し時に暗黙的にスケーリングされるオフセット
#include <string.h>
#include <stdlib.h>
#include <stddef.h>

#define bigNum unsigned long long

struct Collection { 
	bigNum bn_A;
	bigNum bn_B;
	bigNum bn_C;
	int ci_1;
	int ci_2;
};

void foo(void) {
	size_t offset = offsetof(struct Collection, bn_B);
	struct Collection *s = (struct Collection *)malloc(sizeof(struct Collection));
	if (s == NULL) {
		/* Handle malloc() error */
	}

	memset(s + offset, 0, sizeof(struct Collection) - offset); //Noncompliant
	/* ... */
	free(s);
	s = NULL;
}

この例では、offset が、offsetof を呼び出すことによって計算されてから、s に追加されます。変数 offset は、struct Collection 内の bn_B のバイト オフセットです。memset を使用してメモリを設定すると、位置がバイト数によってオフセットされる代わりに、offset が暗黙的にサイズによってスケーリングされます。この暗黙的なスケーリングは、予期せぬ結果を招く可能性があります。Polyspace が違反を報告します。

修正 — unsigned char* を使用してオフセットを計算する

offsets の型によってスケーリングされることで違反が引き起こされます。s を、1 倍にスケーリングされる unsigned char* として宣言することによって、違反を回避してください。

#include <string.h>
#include <stdlib.h>
#include <stddef.h>

#define bigNum unsigned long long

struct Collection {
	bigNum bn_A;
	bigNum bn_B;
	bigNum bn_C;
	int ci_1;
	int ci_2;
};

void foo(void) {
	size_t offset = offsetof(struct Collection, bn_B);
	unsigned char *s = (unsigned char *)malloc(sizeof(struct Collection));
	if (s == NULL) {
		/* Handle malloc() error */
	}

	memset(s + offset, 0, sizeof(struct Collection) - offset); //Compliant
	/* ... */
	free(s);
	s = NULL;
}

チェック情報

グループ: Rule 06.配列 (ARR)

バージョン履歴

R2019a で導入

すべて展開する


1 This software has been created by MathWorks incorporating portions of: the “SEI CERT-C Website,” © 2017 Carnegie Mellon University, the SEI CERT-C++ Web site © 2017 Carnegie Mellon University, ”SEI CERT C Coding Standard – Rules for Developing safe, Reliable and Secure systems – 2016 Edition,” © 2016 Carnegie Mellon University, and “SEI CERT C++ Coding Standard – Rules for Developing safe, Reliable and Secure systems in C++ – 2016 Edition” © 2016 Carnegie Mellon University, with special permission from its Software Engineering Institute.

ANY MATERIAL OF CARNEGIE MELLON UNIVERSITY AND/OR ITS SOFTWARE ENGINEERING INSTITUTE CONTAINED HEREIN IS FURNISHED ON AN "AS-IS" BASIS. CARNEGIE MELLON UNIVERSITY MAKES NO WARRANTIES OF ANY KIND, EITHER EXPRESSED OR IMPLIED, AS TO ANY MATTER INCLUDING, BUT NOT LIMITED TO, WARRANTY OF FITNESS FOR PURPOSE OR MERCHANTABILITY, EXCLUSIVITY, OR RESULTS OBTAINED FROM USE OF THE MATERIAL. CARNEGIE MELLON UNIVERSITY DOES NOT MAKE ANY WARRANTY OF ANY KIND WITH RESPECT TO FREEDOM FROM PATENT, TRADEMARK, OR COPYRIGHT INFRINGEMENT.

This software and associated documentation has not been reviewed nor is it endorsed by Carnegie Mellon University or its Software Engineering Institute.