メインコンテンツ

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

CERT C: Rule EXP39-C

Do not access a variable through a pointer of an incompatible type

説明

ルール定義

互換性のない型のポインターを通じて変数にアクセスしないようにします。1

Polyspace 実装

ルール チェッカーは以下の問題をチェックします。

  • 異なる型のオブジェクトを指すポインターへのキャスト

  • 最初に再初期化せずに、再割り当てされたメモリを別の型のオブジェクトから読み取る

.

すべて展開する

問題

この問題は、あるオブジェクト型へのポインターと別のオブジェクト型へのポインターの間でキャストを実行する場合に発生します。

リスク

オブジェクトへのポインターが別のオブジェクトへのポインターにキャストされる場合、結果のポインターは正しく配置されない可能性があります。不適切なアライメントは未定義の動作を発生させます。

変換によって正しく配置されたポインターが生成されたとしても、そのポインターがオブジェクトへのアクセスに使用される場合、動作が未定義となることがあります。

例外:オブジェクト型を指すポインターは、以下のいずれかの型を指すポインターに変換できます。

  • char

  • signed char

  • unsigned char

例 - 非準拠: 範囲のより広いオブジェクト型を指すポインターへのキャスト
signed   char *p1;
unsigned int *p2;

void foo(void){ 
  p2 = ( unsigned int * ) p1;     /* Non-compliant */				
}

この例では、p1signed char オブジェクトを指します。しかしながら、p1 は範囲のより広いオブジェクト型 unsigned int を指すポインターにキャストされています。

例 - 非準拠: 範囲のより狭いオブジェクト型を指すポインターへのキャスト
extern unsigned int read_value ( void );
extern void display ( unsigned int n );

void foo ( void ){
  unsigned int u = read_value ( );
  unsigned short *hi_p = ( unsigned short * ) &u;    /* Non-compliant  */	
  *hi_p = 0;                                         
  display ( u );                                     
}

この例では、uunsigned int 変数です。&u は、範囲のより狭いオブジェクト型 unsigned short を指すポインターにキャストされています。

ビッグエンディアン マシンでは、ステートメント *hi_p = 0&u が指すメモリ位置の高位ビットをクリアしようと試みます。しかし、display(u) の結果を見ると、その高位ビットがクリアされていないことがあります。

例 - 準拠: 型修飾子を追加するキャスト
const short *p;
const volatile short *q;
void foo (void){
  q = ( const volatile short * ) p;  /* Compliant */								
}

この例では、pq のいずれも short オブジェクトを指します。この両者間のキャストは volatile 修飾子のみを追加し、したがって準拠性をもちます。

問題

この問題は、以下の操作を順に実行すると発生します。

  1. 元の割り当てとは異なる型のオブジェクトにメモリを再割り当てする。

    たとえばこのコードの抜粋では、本来 struct A* 型のポインターに割り当てられていたメモリが、struct B* 型のポインターに再割り当てされます。

    struct A;
    struct B;
    
    struct A *Aptr = (struct A*) malloc(sizeof(struct A));
    struct B *Bptr = (struct B*) realloc(Aptr, sizeof(struct B));

  2. 再割り当てされたメモリを最初に再初期化せずに、このメモリから読み取る。

    再割り当てされたメモリを指すポインターでの読み取りアクセスは、ポインター デリファレンスまたは配列のインデックス付けにおいて発生する可能性があります。const 修飾子付きオブジェクトへのポインターを受け取る関数に、対応するパラメーターとしてこのポインターを渡す操作も、読み取りアクセスとしてカウントされます。

リスク

再初期化されていない再割り当てメモリからの読み取りを行うと、未定義の動作につながります。

修正方法

再割り当ての後、最初の読み取りアクセスの前にメモリを再初期化します。

チェッカーでは、再割り当てされたメモリを指すポインターでの書き込みアクセスすべてが (オブジェクトの再初期化が部分的である場合でも)、再初期化の要件を満たしているものと解釈されます。再割り当てされたメモリを指すポインターでの書き込みアクセスは、ポインター デリファレンスと配列のインデックス付けにおいて発生する可能性があります。const 修飾子付きでないオブジェクトへのポインターを受け取る関数に、対応するパラメーターとしてこのポインターを渡す操作も、書き込みアクセスとしてカウントされます。

例 - 非準拠:最初に再初期化を行わずに、再割り当てされたメモリから読み取る
#include<stdlib.h>

struct group {
    char *groupFirst;
    int groupSize;
};

struct groupWithID {
    int groupID;
    char *groupFirst;
    int groupSize;
};

char* readName();
int readSize();

void createGroup(int nextAvailableID) {
    struct group *aGroup;
    struct groupWithID *aGroupWithID;
    
    aGroup = (struct group*) malloc(sizeof(struct group));
    
    if(!aGroup) {
        /*Handle error*/
    }
    
    aGroup->groupFirst = readName();
    aGroup->groupSize  = readSize();
    
    /* Reassign to group with ID */
    aGroupWithID = (struct groupWithID*) realloc(aGroup, sizeof(struct groupWithID));
    if(!aGroupWithID) {
        free(aGroup);
        /*Handle error*/
    }
    
    if(aGroupWithID -> groupSize > 0) { /* Noncompliant */
        /* ... */
    }
    
    /* ...*/
    free(aGroupWithID);
}

この例では、関数 malloc を使用して group* ポインターに割り当てられたメモリを、関数 realloc を使用して、groupWithID* ポインターに再割り当てします。再割り当てされたメモリを再初期化する前に、このメモリへの読み取りアクセスが行われます。

修正 – 再初期化の後、最初の読み取りの前にメモリを再割り当てする

最初の読み取りアクセスの前に、groupWithID* ポインターに割り当てられているメモリを再初期化します。メモリのすべてのビットは、関数 memset を使用して再初期化できます。

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

struct group {
    char *groupFirst;
    int groupSize;
};

struct groupWithID {
    int groupID;
    char *groupFirst;
    int groupSize;
};

char* readName();
int readSize();

void createGroup(int nextAvailableID) {
    struct group *aGroup;
    struct groupWithID *aGroupWithID;
    
    aGroup = (struct group*) malloc(sizeof(struct group));
    
    if(!aGroup) {
        /*Handle error*/
    }
    
    aGroup->groupFirst = readName();
    aGroup->groupSize  = readSize();
    
    /* Reassign to group with ID */
    aGroupWithID = (struct groupWithID*) realloc(aGroup, sizeof(struct groupWithID));
    if(!aGroupWithID) {
        free(aGroup);
        /*Handle error*/
    }
    
    memset(aGroupWithID, 0 , sizeof(struct groupWithID));
    /* Reinitialize group */
    if(aGroupWithID -> groupSize > 0) { 
        /* ... */
    }
    
    /* ...*/
    free(aGroupWithID);
}

チェック情報

グループ: Rule 03.式 (EXP)

バージョン履歴

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.