メインコンテンツ

パディング データのメモリ比較

構造体のパディングで格納したデータを memcmp で比較している

説明

この欠陥は、関数 memcmp を使用して、2 つの構造体全体が比較された場合に発生します。この処理では、構造体のパディングで格納された無意味なデータが比較されます。

次に例を示します。

struct structType {
    char member1;
    int member2;
    .
    .
};

structType var1;
structType var2;
.
.
if(memcmp(&var1,&var2,sizeof(var1))) 
{...}

リスク

構造体のメンバーが異なるデータ型を持っている場合、コンパイラはメモリ内のデータ アライメントのために追加のパディングを導入します。パディングの例については、ローカル変数サイズのより高い推定値 (Polyspace Code Prover)を参照してください。

これらの追加のパディング バイトの内容に意味はありません。C 標準では、さまざまなコンパイラが独自のパディングを自由に実装できるように、それらのバイトのコンテンツが不確定になることを許容しています。memcmp で構造体のバイト単位の比較を実行する場合、パディングに格納された意味のないデータも比較されます。対応するメンバーが同じ値であっても、2 つのデータ構造体が等しくないという誤った結論になる可能性があります。

修正方法

2 つの構造体を 1 回の試行で比較するのではなく、構造体のメンバーごとに比較します。

効率的なコードにするには、メンバーごとに比較を行う関数を作成します。2 つの構造体を比較するには、この関数を使用します。

構造体がパディングを含まないことがわかっている場合のみ、構造体のバイト単位の比較に memcmp を使用します。通常、パディングが行われないようにするには、特定の属性または #pragma pack などのプラグマを使用します。ただし、このような属性やプラグマは、必ずしもすべてのコンパイラでサポートされているとは限らず、コードの実装はそれぞれ異なります。構造体にビット フィールドが含まれる場合、このような属性やプラグマを使用しても、パディングを防ぐことはできません。

すべて展開する

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

#define fatal_error() abort()

typedef struct s_padding
{
    char c;            
    int i;
    unsigned int bf1:1;    
    unsigned int bf2:2;
    unsigned char buffer[20];
} S_Padding ;

/* Function that guarantees safe access to the input memory */
extern int trusted_memory_zone(void *ptr, size_t sz); 

int func(const S_Padding *left, const S_Padding *right)
{

    if (!trusted_memory_zone((void *)left, sizeof(S_Padding)) ||
        !trusted_memory_zone((void *)right, sizeof(S_Padding))) {
        fatal_error();
    }

    if (0 == memcmp(left, right, sizeof(S_Padding))) 
    {
        return 1;
    }
    else
        return 0;
}

この例では、memcmp を使用して left および right が指す 2 つの構造体をバイト単位で比較しています。構造体の各メンバーに格納された値が同じでも、パディング バイトに使われる意味のない値が同じでなければ、この比較によって等しくないという結果になる可能性があります。

修正 — メンバーごとに構造体を比較

1 つの修正方法として、構造体のメンバーを個別に比較します。

メモ

memcmp を使用して、配列全体を比較できます。配列のメンバーはすべてデータ型が同じです。配列の格納にはパディング バイトは必要ありません。

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

#define fatal_error() abort()

typedef struct s_padding
{
    char c;         
    int i;
    unsigned int bf1:1;    
    unsigned int bf2:2;
    unsigned char buffer[20];
} S_Padding ;

/* Function that guarantees safe access to the input memory */
extern int trusted_memory_zone(void *ptr, size_t sz); 

int func(const S_Padding *left, const S_Padding *right)
{
    if (!trusted_memory_zone((void *)left, sizeof(S_Padding)) ||
        !trusted_memory_zone((void *)right, sizeof(S_Padding))) {
        fatal_error();
    }

    return ((left->c == right->c) &&                
            (left->i == right->i) &&
            (left->bf1 == right->bf1) &&
            (left->bf2 == right->bf2) &&
            (memcmp(left->buffer, right->buffer, 20) == 0)); 
}

結果情報

グループ: プログラミング
言語: C | C++
既定値: 手書きコードはオン、生成コードはオフ
コマンド ライン構文: MEMCMP_PADDING_DATA
影響度: Medium

バージョン履歴

R2017a で導入