メインコンテンツ

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

CERT C++: ARR39-C

Do not add or subtract a scaled integer to a pointer

説明

ルール定義

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

Polyspace 実装

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

すべて展開する

問題

"ポインターのスケーリングが無効です" は、ポインター演算での暗黙的なスケーリングが無視される場合に発生します。

たとえば、この欠陥は次の状態で発生します。

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

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

ポインターに対する算術演算の実行時に、参照される変数のデータ型のサイズによって、引数が暗黙的にスケーリングされている。ポインター演算で sizeof を使用すると想定外の結果が生成される。

ポインター演算で sizeof 演算子を使用しない。
ポインターに対し算術演算を実行してから、キャストを適用する。ポインター演算での暗黙的なスケーリングは、オブジェクトの型に応じて異なる。これらのスケーリングされた演算を実行した後に、キャストによってポインターの型を変更すると、想定外の結果が発生することがある。キャスト操作の後でポイント演算を実行する。

修正方法

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

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

例 — sizeof 演算子の使用
#include <string.h>
#include <stddef.h>
#include <stdlib.h>
#include <wchar.h>
#include <stdio.h>



enum { INTBUFSIZE = 80 };
extern int getdata (void);
int buf[INTBUFSIZE];
void foo (void)
{
  int *buf_ptr = buf;

  while (buf_ptr < (buf + sizeof (buf))) {  //Noncompliant
    *buf_ptr++ = getdata ();
  }
}

この例では、配列 buf の最後へのポインターを取得するために演算 sizeof(buf) が使用されます。sizeof(buf) の出力は int によってスケーリングされます。ポインター演算は暗黙的にスケーリングされるため、sizeof(buf) の出力が buf に追加されるときに int によってもう一度スケーリングされ、その結果、予期せぬ動作が発生します。Polyspace® は、sizeof 演算子の使用にフラグを設定します。

修正 — sizeof 演算子を削除

1 つの修正方法として、スケーリングされていない数値をオフセットとして使用します。

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



enum { INTBUFSIZE = 80 };
extern int getdata (void);
int buf[INTBUFSIZE];
void foo (void)
{
  int *buf_ptr = buf;

  while (buf_ptr < (buf + INTBUFSIZE)) {  
    *buf_ptr++ = getdata ();
  }
}
例 — ポインター演算後のキャスト
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;
}

チェック情報

グループ: 04.コンテナー (CTR)

バージョン履歴

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.