【C言語】
オーバーフローと
ラップアラウンドの違い
(overflow vs wrap around)

ラップアラウンドと オーバーフロー wrap around vs overflow

(error): Signed integer overflow for expression ‘y+2

警告:’y+2’の計算で符号付き整数オーバーフロー[(error)integerOverflow]

■overflowとwrap aroundの単純な例

// gcc -Wall -Wextra -O2 -fsanitize=undefined
#include <stdio.h>
int main(void){
    //ラップアラウンド
    unsigned int x = 0xffffFFFF ;
    printf("%x\n",x+2);//合法 

    //オーバーフロー
    signed int y = 0x7fffFFFF ;
    printf("%x\n",y+2);//未定義動作
}

簡単に言うと
符号なし計算の桁溢れをラップアラウンドと言い合法。
・符号付き計算の桁溢れをオーバーフローと言い未定義動作。

gcc -Wall -Wextra -O2 -fsanitize=undefined 
でコンパイル&実行すると10行目で以下の警告が出ます。

x.c:10:5: runtime error: signed integer overflow: 2147483647 + 2 cannot be represented in type ‘int’


■オーバーフローによる未定義動作の例

#include <stdio.h>
int f(int x,int y) {
    if((x+y) < x) {
        printf("%x < %x\n",(x+y),x);
        return  1;
    } else{
        printf("%x >= %x\n",(x+y),x);
        return  0;
    }
}
int main(void) {
    printf("%d\n",f(0x7fffFFFF,2));
}

このコードはgcc11 とclangで動作結果が違います。


■符号付きの修正例(オーバーフロー)

#include    <stdint.h>
#include    <limits.h>
#include    <stdio.h>
int main(void){
    int32_t     x32 = 0x7FFFffff ;
    int32_t     y32 = 2;
    int64_t     z64 ;
    z64 = x32 + y32 ;//オーバーフロー(未定義動作)
    printf("%lx\n",z64);

    z64 = (int64_t)x32 + y32;//修正例
    printf("%lx\n",z64);
}

clang -Weverything -fsanitize=undefined
でコンパイルすると、
走行時
UndefinedBehaviorSanitizer: undefined-behavior
と教えてくれます。
※コンパイル時は警告は出ません。

■符号無しの修正例(ラップアラウンド)

#include    <stdint.h>
#include    <limits.h>
#include    <stdio.h>
int main(void){
    uint32_t     x32 = 0xFFFFffff ;
    uint32_t     y32 = 2;
    uint64_t     z64 ;
    z64 = x32 + y32 ;//ラップアラウンド         
    printf("%lx\n",z64);

    z64 = (uint64_t)x32 + y32;//修正例
    printf("%lx\n",z64);
}

8行目でラップアラウンドが発生しますが、
gccもclangも警告を出してくれません。
32bit同士の計算ではラップアラウンドしてしまうので
11行目のようにキャストして64bitの計算を行う必要があります。

補足:

C言語の世界でオーバーフローと言うと
バッファオーバーフロー
(主に配列の範囲外アクセス)と
整数オーバーフロー
(桁あふれ)
がありますが、
この記事では整数オーバーフローを扱いました。

参考:

CWE-190: Integer Overflow or Wraparound

MSC15-C. 未定義の動作に依存しない

INT30-C. 符号無し整数の演算結果がラップアラウンドしないようにする

INT32-C. 符号付き整数演算がオーバーフローを引き起こさないことを保証する