メインコンテンツ

CWE Rule 843

Access of Resource Using Incompatible Type ('Type Confusion')

R2023a 以降

説明

ルールの説明

The program allocates or initializes a resource such as a pointer, object, or variable using one type, but it later accesses that resource using a type that is incompatible with the original type.

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);
}

チェック情報

カテゴリ: Type Errors

バージョン履歴

R2023a で導入

すべて展開する