メインコンテンツ

CWE Rule 191

Integer Underflow (Wrap or Wraparound)

R2023a 以降

説明

ルールの説明

The product subtracts one value from another, such that the result is less than the minimum allowable integer value, which produces a value that is not equal to the correct result.

Polyspace 実装

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

  • 整数定数のオーバーフロー

  • 整数のオーバーフロー

  • 符号なし整数定数のオーバーフロー

  • 符号なし整数の変換のオーバーフロー

  • 符号なし整数のオーバーフロー

すべて展開する

問題

この問題は、次の場合に発生します。

  • コンパイル時の定数が、その値を格納できないデータ型の符号付き整数変数に代入されている。

    代入による定数オーバーフローを参照してください。

  • enum の潜在型に格納できない enum 値が使用されている (かつ、潜在型が符号付きである)。ほとんどの C コンパイラでは、既定で潜在型は signed int です (C 標準に基づく)。

    enum 値による定数オーバーフローを参照してください。

  • オーバーフローを発生させる 2 つの整数定数 (つまり、演算で使用されているデータ型の許容範囲外の値) を含む二項演算が実行されている。整数定数を使用する二項演算では signed int データ型が使用されます (ただし、uL などの修飾子を使用する場合は、その限りではありません)。

    二項演算による定数オーバーフローを参照してください。

n ビット符号付き整数は、範囲 [-2n-1, 2n-1-1] 内の値を保持します。たとえば、c は 8 ビットの符号付き char 型の変数であるため、値 255 を保持できません。

signed char c = 255;

この欠陥チェッカーは、次のオプションに依存します。

この欠陥は、次の状況では発生しません。

  • const 変数から新しい定数を作成する (特定のコンパイラのみ)。

    コンパイラによって、コンパイル時定数の定義方法は異なります。次のコードに含まれる c+1 は、GCC コンパイラではコンパイル時定数と見なされますが、標準 C コンパイラではそのように見なされません。

    const int16_t c = 32767;
    int16_t y = c + 1;
    y でのこのチェックの違反が検出されるかどうかは、コンパイラによって異なります。

  • ビット NOT 演算。

    Polyspace® では、ビット NOT 演算の実行時にこの違反が報告されません。

リスク

定数のオーバーフローの既定の動作は、コンパイルやプラットフォームによって異なる場合があります。定数のオーバーフローを維持するとコードの移植性が低くなる可能性があります。

コンパイラでオーバーフローした定数が警告を伴ってラップ アラウンドされる場合でも、ラップ アラウンドの動作は意図されたものではなく、予期しない結果を引き起こす可能性があります。

修正方法

定数値が意図したものかどうかをチェックします。値が正しい場合は、より大きい可能性のある別のデータ型をその変数に使用します。

例 — 代入による定数オーバーフロー
#define MAX_UNSIGNED_CHAR 255 //Noncompliant
#define MAX_SIGNED_CHAR 127 //Noncompliant

void main() {
    char c1 = MAX_UNSIGNED_CHAR;
    char c2 = MAX_SIGNED_CHAR+1;
}

この例では、1 つ以上のマクロを使用するとオーバーフローが起こるため、マクロに欠陥が表示されます。この欠陥を再現するには、[ターゲット プロセッサ タイプ] (-target) を使用します。ここで、char は、既定では符号付きになります。

修正 — 別のデータ型を使用

1 つの修正方法として、オーバーフローする変数に別のデータ型を使用します。

#define MAX_UNSIGNED_CHAR 255 
#define MAX_SIGNED_CHAR 127

void main() {
    unsigned char c1 = MAX_UNSIGNED_CHAR;
    unsigned char c2 = MAX_SIGNED_CHAR+1;
}
例 — enum 値による定数オーバーフロー

enum {
  a=0x7fffffff,
  b,  //Noncompliant
  c=0xffffffff  //Noncompliant
} MyEnumA;

この例では、enum の潜在型は int です。int 型では、範囲 [-231, 231-1] の値を格納できます。ただし、列挙の値 b は 0x80000000 または 231 (前の a 値に 1 を足した値) です。この値は int の許容範囲外です。

c の値 (0xffffffff または 232-1) はさらに大きく、これもオーバーフローを発生させます。

この欠陥を確認するには、次のようにします。

例 — 二項演算による定数オーバーフロー
const unsigned int K_ATM_Label_Ram_init_value [] = {
     0x06 | ( 3 << 29) ,
     0x80 | ( 9 << 29) , //Noncompliant
     ( 2 << 31 )  //Noncompliant
};

この例では、シフト演算のうちの 2 つの結果が、signed int データ型では格納できない値になります。signed int データ型では、範囲 [-231, 231-1] の値を格納できます。以下の演算を考えます。

  • 9 << 29 の結果の値は 232+536870912 になります。

  • 2 << 31 の結果の値は 232 になります。

結果が変数 unsigned int に代入されるとしても、オーバーフローの検出では二項演算の潜在型、つまり signed int が使用されることに注意してください。

問題

この問題は、整数変数に対する演算が、その演算が使用するデータ型では表せない値になる場合に発生します。このデータ型はオペランドの型に応じて異なります。このデータ型によって、結果を格納するために割り当てられるバイト数が決まり、許容される値の範囲が制限されます。

次の点に注意してください。

  • オーバーフローの判別に使用されるデータ型は、オペランドのデータ型に基づいています。演算結果を別の変数に割り当てると、別のチェッカー [整数変換のオーバーフロー] が、割り当てられた値によって割り当て先の変数がオーバーフローするかどうかを判別します。たとえば次のような演算があるとします。

    res = x + y;
    このチェッカーは res の型ではなく x および y の型に基づいて、オーバーフローをチェックします。整数変換のオーバーフローのチェッカーは、res の型に基づいてオーバーフローをチェックします。

  • 二項演算の 2 つのオペランドで、演算発生前にプロモーションが行われることがあります。暗黙的なデータ型の変換に関する Code Prover の仮定 (Polyspace Code Prover)も参照してください。

データ型に最適なストレージ割り当てはプロセッサによって異なります。ターゲット プロセッサ タイプ (-target)を参照してください。

リスク

符号付き整数のオーバーフローにより、未定義の動作が発生します。

修正方法

修正方法は欠陥の根本原因によって異なります。多くの場合、結果の詳細 (または Polyspace as You Code のソース コード ツールヒント) には欠陥につながる一連のイベントが表示されます。そのシーケンス内のどのイベントについても修正を実装できます。結果の詳細にイベント履歴が表示されない場合は、ソース コード内で右クリック オプションを使用して、欠陥に関連する変数のこれまでの参照を検索し、関連するイベントを検出できます。Polyspace デスクトップ ユーザー インターフェイスでの Bug Finder の結果の解釈またはPolyspace Access Web インターフェイスでの Bug Finder の結果の解釈 (Polyspace Access)も参照してください。

この欠陥は次のようにして修正できます。

  • すべての値に適応できるように、演算の結果に対してより大きいデータ型を使用。

  • オーバーフローにつながる値をチェックし、適切なエラー処理を実行。

通常、オーバーフローを回避するために、次のいずれかの手法を試します。

  • 整数変数値を符号付き整数の範囲の半分の範囲内に制限。

  • オーバーフローする可能性がある演算で、オーバーフローにつながる可能性がある条件をチェックし、演算の結果を使用する方法に応じてラップ アラウンド動作または飽和動作を実装。結果が予測可能になり、以降の計算で安全に使用できます。

以下の修正例を参照してください。

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

チェッカーの拡張

入力値が不明であり、入力のサブセットのみがエラーの原因となっている場合、既定の Bug Finder 解析ではこの欠陥が報告されない可能性があります。特定のシステム入力値を原因とする欠陥の有無をチェックするには、より厳密な Bug Finder 解析を実行してください。特定のシステム入力値から欠陥を見つけるための Bug Finder チェッカーの拡張を参照してください。

例 — 整数の最大値に加算する
#include <limits.h>

int plusplus(void) {

    int var = INT_MAX;
    var++;  //Noncompliant
    return var;
}

この関数の 3 番目のステートメントで、変数 var は 1 つ増加しています。しかし var の値は整数の最大値であるため、整数の最大値に 1 を加えると int では表現できません。

修正 — 異なるストレージ型

1 つの修正方法として、データ型を変更することができます。演算結果をより大きいデータ型に保存します (32 ビット マシンでは、intlong は同じサイズです)。この例では、32 ビット マシンで int ではなく long long を返すことでオーバーフロー エラーが修正されます。

#include <limits.h>

long long plusplus(void) {

    long long lvar = INT_MAX;
    lvar++;
    return lvar;
}
問題

この問題は、次の場合に発生します。

  • コンパイル時の定数がその値を格納できないデータ型の符号なし整数変数に代入された。

  • enum の潜在型に収まらない enum 値を使用した (かつ、潜在型が符号なしであった)。

n ビット符号なし整数は、範囲 [0, 2n-1] 内の値を保持します。たとえば、c は 8 ビットの符号なし char 型の変数であるため、値 256 を保持できません。

unsigned char c = 256;

この欠陥チェッカーは、次のオプションに依存します。

この欠陥は、次の状況では発生しません。

  • const 変数から新しい定数を作成する (特定のコンパイラのみ)。

    コンパイラによって、コンパイル時定数の定義方法は異なります。次のコードでは、c+1u は GCC コンパイラではコンパイル時定数とみなされますが、標準 C コンパイラでは見なされません。

    const uint16_t c = 0xffffu;
    uint16_t y = c + 1u;
    y でのこのチェックの違反が検出されるかどうかは、コンパイラによって異なります。

  • ビット NOT 演算。

    Polyspace では、ビット NOT 演算の実行時にこの違反が報告されません。

リスク

C 標準では、オーバーフローした符号なし整数はラップされなければなりません (たとえば、C11 規格の節 6.2.5 を参照)。ただし、そのラップ アラウンド動作は意図されていないことがあり、予期しない結果を引き起こす可能性があります。

修正方法

定数値が意図したものかどうかをチェックします。値が正しい場合は、より大きいデータ型をその変数に使用します。

例 — 代入によるオーバーフロー
#define MAX_UNSIGNED_CHAR 255  //Noncompliant
#define MAX_UNSIGNED_SHORT 65535  //Noncompliant

void main() {
    unsigned char c1 = MAX_UNSIGNED_CHAR + 1;
    unsigned short c2 = MAX_UNSIGNED_SHORT + 1;
}

この例では、1 つ以上のマクロを使用するとオーバーフローが起こるため、マクロに欠陥が表示されます。

修正 — より大きなデータ型を使用

1 つの修正方法として、オーバーフローする変数により大きいデータ型を使用します。

#define MAX_UNSIGNED_CHAR 255
#define MAX_UNSIGNED_SHORT 65535

void main() {
    unsigned short c1 = MAX_UNSIGNED_CHAR + 1;
    unsigned int c2 = MAX_UNSIGNED_SHORT + 1;
}
問題

この問題は、符号なし整数がより小さい符号なし整数型に変換された場合に発生します。変数に元の定数を表現するだけの十分なバイト数がない場合、変換はオーバーフローします。

異なる浮動小数点型への正確なストレージ割り当てはプロセッサに依存します。ターゲット プロセッサ タイプ (-target)を参照してください。

リスク

整数変換のオーバーフローにより、未定義の動作が発生します。

修正方法

修正方法は欠陥の根本原因によって異なります。多くの場合、結果の詳細 (または Polyspace as You Code のソース コード ツールヒント) には欠陥につながる一連のイベントが表示されます。そのシーケンス内のどのイベントについても修正を実装できます。結果の詳細にイベント履歴が表示されない場合は、ソース コード内で右クリック オプションを使用して、欠陥に関連する変数のこれまでの参照を検索し、関連するイベントを検出できます。Polyspace デスクトップ ユーザー インターフェイスでの Bug Finder の結果の解釈またはPolyspace Access Web インターフェイスでの Bug Finder の結果の解釈 (Polyspace Access)も参照してください。

この欠陥は次のようにして修正できます。

  • すべての値に適応できるように、変換の結果に対してより大きいデータ型を使用。

  • オーバーフローにつながる値をチェックし、適切なエラー処理を実行。

一般的に、より小さい整数型への変換は避けます。

以下の修正例を参照してください。

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

チェッカーの拡張

入力値が不明であり、入力のサブセットのみがエラーの原因となっている場合、既定の Bug Finder 解析ではこの欠陥が報告されない可能性があります。特定のシステム入力値を原因とする欠陥の有無をチェックするには、より厳密な Bug Finder 解析を実行してください。特定のシステム入力値から欠陥を見つけるための Bug Finder チェッカーの拡張を参照してください。

例 — int から char への変換
unsigned char convert(void) {
    unsigned int unum = 1000000U;

    return (unsigned char)unum;  //Noncompliant
}

return ステートメントで、符号なしの整数変数 unum が符号なしの文字タイプに変換されています。しかし、1000000 には少なくとも 20 ビットが必要なため、変換によりオーバーフローが生じます。C プログラミング言語の規格では、最大値に 1 を加えた値を法としてプログラムが結果を自動的に縮小するため、符号なしのオーバーフローはエラーとは見なされません。この例では、文字データ型は 2^8-1 しか表現できないため、unum2^8 を法として縮小されます。

修正 — 変換の種類を変更

1 つの修正方法として、数全体を表現できる別の整数型に変換することができます。たとえば、long です。

unsigned long convert(void) {
    unsigned int unum = 1000000U;

    return (unsigned long)unum;  
}
問題

この問題は、符号なし整数変数に対する演算が、結果のデータ型では表せない値になる可能性がある場合に発生します。変数のデータ型によって、変数ストレージに割り当てられるバイト数が決まり、許容される値の範囲が制限されます。

異なる浮動小数点型への正確なストレージ割り当てはプロセッサに依存します。ターゲット プロセッサ タイプ (-target)を参照してください。

リスク

C11 規格によると、符号なし整数のオーバーフローにより、ラップ アラウンド動作が発生します。ただし、ラップ アラウンド動作は常に望ましいとは限りません。たとえば、計算の結果を配列サイズとして使用し、計算がオーバーフローする場合、その配列サイズは想定よりはるかに小さい可能性があります。

修正方法

修正方法は欠陥の根本原因によって異なります。多くの場合、結果の詳細 (または Polyspace as You Code のソース コード ツールヒント) には欠陥につながる一連のイベントが表示されます。そのシーケンス内のどのイベントについても修正を実装できます。結果の詳細にイベント履歴が表示されない場合は、ソース コード内で右クリック オプションを使用して、欠陥に関連する変数のこれまでの参照を検索し、関連するイベントを検出できます。Polyspace デスクトップ ユーザー インターフェイスでの Bug Finder の結果の解釈またはPolyspace Access Web インターフェイスでの Bug Finder の結果の解釈 (Polyspace Access)も参照してください。

この欠陥は次のようにして修正できます。

  • すべての値に適応できるように、演算の結果に対してより大きいデータ型を使用。

  • オーバーフローにつながる値をチェックし、適切なエラー処理を実行。エラー処理コードでは、たとえば、オーバーフローに対する既定のラップ アラウンド動作をオーバーライドし、飽和動作を実装できます。

以下の修正例を参照してください。

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

チェッカーの拡張

入力値が不明であり、入力のサブセットのみがエラーの原因となっている場合、既定の Bug Finder 解析ではこの欠陥が報告されない可能性があります。特定のシステム入力値を原因とする欠陥の有無をチェックするには、より厳密な Bug Finder 解析を実行してください。特定のシステム入力値から欠陥を見つけるための Bug Finder チェッカーの拡張を参照してください。

例 — 符号なし整数の最大値に 1 を加算
#include <limits.h>

unsigned int plusplus(void) {

    unsigned uvar = UINT_MAX;
    uvar++;  //Noncompliant
    return uvar;
}

この関数の 3 番目のステートメントで、変数 uvar は 1 つ増加しています。しかし、uvar の値は符号なし整数の最大値であり、整数の最大値に 1 を加えた数は unsigned int では表現できません。C プログラミング言語の規格では、最大値に 1 を加えた値を法としてプログラムが結果を自動的に縮小するため、符号なしのオーバーフローはエラーとは見なされません。この例では、uvarUINT_MAX を法として縮小されます。結果は uvar = 1 です。

修正 — 異なるストレージ型

1 つの修正方法として、演算の結果をより大きいデータ型に保存することができます。この例では、unsigned int ではなく unsigned long long を返すことでオーバーフロー エラーが修正されます。

#include <limits.h>

unsigned long long plusplus(void) {

    unsigned long long ullvar = UINT_MAX;
    ullvar++;
    return ullvar;
}

チェック情報

カテゴリ: Numeric Errors

バージョン履歴

R2023a で導入