メインコンテンツ

CWE Rule 188

Reliance on Data/Memory Layout

R2023a 以降

説明

ルールの説明

The software makes invalid assumptions about how protocol data or memory is organized at a lower level, resulting in unintended program behavior.

Polyspace 実装

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

  • メモリ編成の前提条件が無効です

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

  • データ転送時の欠落しているバイトの並べ替え

すべて展開する

問題

この問題は、スタック内の変数のアドレスが、別の非配列変数のアドレスに対して加算または減算することによって計算された場合に発生します。

リスク

スタック内の変数のアドレスが、別の変数のアドレスに対し加算または減算を行うことによって計算される場合は、特定のメモリ編成が仮定されています。仮定に誤りがある場合、計算されたアドレスへのアクセスが無効になることがあります。

修正方法

メモリ編成についての仮定に依存したアクセスは実行しないようにします。

例 — メモリ編成への依存
void func(void) {
    int var1 = 0x00000011, var2;
    *(&var1 + 1) = 0; //Noncompliant
}

この例で、プログラマは、&var1 + 1var2 のアドレスを提供するという仮定に依存しています。したがって、[メモリ編成の前提条件が無効です]+ 演算に表示されます。さらに、[範囲外にアクセスするポインター] エラーもデリファレンスに表示されます。

修正 — メモリ編成に依存しない

1 つの修正方法として、別々に宣言された変数にアクセスする目的で、アドレスについて直接計算を実行しないようにします。

問題

この問題は、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 などのプラグマを使用します。ただし、このような属性やプラグマは、必ずしもすべてのコンパイラでサポートされているとは限らず、コードの実装はそれぞれ異なります。構造体にビット フィールドが含まれる場合、このような属性やプラグマを使用しても、パディングを防ぐことはできません。

例 — 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();
    }

    if (0 == memcmp(left, right, sizeof(S_Padding)))  //Noncompliant
    {
        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)); 
}
問題

この問題は、以下のタイミングでバイト順関数が使用されなかった場合に発生します。

  • ネットワーク ソケットにデータを送信する前。

  • ネットワーク ソケットからデータを受信した後。

リスク

リトル エンディアン バイト順 (最下位バイトが先) を実装しているシステム アーキテクチャもあれば、ビッグ エンディアン バイト順 (最上位バイトが先) を実装しているシステム アーキテクチャもあります。送信データのエンディアンが受信システムのエンディアンと一致しない場合、データ読み取り時に返される値は正しくありません。

修正方法

ソケットからデータを受信した後に、ntohl() などのバイト順関数を使用します。ソケットにデータを送信する前に、htonl() などのバイト順関数を使用します。

例 — バイトの並べ替えなしで送信されるデータ
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <byteswap.h>
#include <unistd.h>
#include <string.h>


unsigned int func(int sock, int server)
{
    unsigned int num;   /* assume int is 32-bits */
    if (server)
    {
        /* Server side */
        num = 0x17;
		/* Endianness of server host may not match endianness of network. */
        if (send(sock, (void *)&num, sizeof(num), 0) < (int)sizeof(num))  //Noncompliant
        {
            /* Handle error */
        }
        return 0;
    }
    else {
        /* Endianness of client host may not match endianness of network. */
        if (recv (sock, (void *)&num, sizeof(num), 0) < (int) sizeof(num))  
        {
            /* Handle error */
        }
		
		/* Comparison may be inaccurate */
        if (num> 255)  //Noncompliant
        {
            return 255;
        }
        else
        {
            return num;
        }
    }
}
        
      

この例では、変数 num は 16 進数値 0x17 が代入され、ネットワークを経由してサーバーからクライアントに送信されます。サーバー ホストがリトル エンディアンで、ネットワークがビッグ エンディアンの場合、num0x17000000 として転送されます。その後、クライアントでは num の誤った値を読み取って、ローカルの数値と比較します。

修正 — バイト順関数を使用

サーバー ホストから num を送信する前に、htonl() を使用してホストからネットワークへのバイト順を変換します。同様に、クライアント ホストで num を読み取る前に、ntohl() を使用して、ネットワークからホストへのバイト順を変換します。

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <byteswap.h>
#include <unistd.h>
#include <string.h>

unsigned int func(int sock, int server)
{
    unsigned int num;   /* assume int is 32-bits */
    if (server)
    {
        /* Server side */
        num = 0x17;
		
		/* Convert to network byte order. */
        num = htonl(num); 
        if (send(sock, (void *)&num, sizeof(num), 0) < (int)sizeof(num)) 
        {
            /* Handle error */
        }
        return 0;
    }
    else {
        if (recv (sock, (void *)&num, sizeof(num), 0) < (int) sizeof(num)) 
        {
            /* Handle error */
        }
		
		/* Convert to host byte order. */
        num = ntohl(num); 
        if (num > 255) 
        {
            return 255;
        }
        else
        {
            return num;
        }
    }
} 

チェック情報

カテゴリ: Data Neutralization Issues

バージョン履歴

R2023a で導入