【C言語】
ゼロ除算の見つけ方
(Divide By Zero)

ゼロ除算検出 (Divide By Zero)

warning: division by zero

警告:ゼロ除算
[-Wdiv-by-zero]

■0で割り算すると大抵の環境では浮動小数点例外が発生

浮動小数点例外(Floating point exception)が
発生するとプログラムは強制終了します。
そしてお客様に呼び出され(以下略)

■ゼロ除算問題の例

#include    <stdio.h>
void    f1(void){
    int     x = 1;
    printf("%d\n",x/0) ;    //検出できる
}
void    f2(void){
    int     x = 1;
    int     y = 0;
    printf("%d\n",x/y) ;    //検出できない
}
int     f3(int x,int y){
    return  x/y;    //検出できない
}
int main(void){
    int     x = 1;
    int     y = 0;
    printf("%d\n",f3(x,y));
}

ツールでは ゼロ除算 問題を検出できない場合がほとんどです。
筆者環境下のgcc -Wall -Wextraでは
分母が定数0の時しか検出できません。
分母が変数の時は検出できませんでした。


■ゼロ除算を見つけるのは難しい

コンパイラの警告や静的解析ツールを使用してもゼロ除算を見つけるのは難しいです。

人海戦術で単純検索してもうまくいきません。

●除算命令(div)の見つけ方

ゼロ除算問題が発生してしまった時、
同様の問題がないか確認するために
割り算を行っている箇所をソースコードから
見つけ出す必要があります。


しかし ”/” や”%”を以下のように単純検索してもうまくいきません。

 $grep  ”/”  *.c

⇒コメントの”//” や ”/*” ばかり膨大な量が検索されます。


こんな時はCソースから割り算を検索するのではなくて
一度逆アセンブルして”div” 命令などを検索したほうが
早いかもしれません。

●”div”命令を検索するSHELL

gcc -c -g ./div.c

objdump div.o \
--disassemble \
--line-numbers \
--source | grep div 

➡実行結果

+ gcc -c -g ./div.c
./div.c: In function ‘f1’:
./div.c:4:20: warning: division by zero [-Wdiv-by-zero]
    4 |     printf("%d\n",x/0) ;    //検出できる
      |                    ^
+ objdump div.o --disassemble --line-numbers --source
+ grep div
div.o:     file format elf64-x86-64
/mnt/f/DivideByZero/./div.c:2
/mnt/f/DivideByZero/./div.c:3
/mnt/f/DivideByZero/./div.c:4
  1c:   f7 f9                   idiv   %ecx
/mnt/f/DivideByZero/./div.c:5
/mnt/f/DivideByZero/./div.c:6
/mnt/f/DivideByZero/./div.c:7
/mnt/f/DivideByZero/./div.c:8
/mnt/f/DivideByZero/./div.c:9
  55:   f7 7d fc                idivl  -0x4(%rbp)
/mnt/f/DivideByZero/./div.c:10
/mnt/f/DivideByZero/./div.c:11
/mnt/f/DivideByZero/./div.c:12
  83:   f7 7d f8                idivl  -0x8(%rbp)
/mnt/f/DivideByZero/./div.c:13

■不適切なゼロ除算回避処理例

(warning): Either the condition ‘y==0’ is redundant or there is division by zero at line 3.

警告:ゼロ除算後のガード処理は無駄

[(warning)zerodivcond]

#include    <stdio.h>
int div(int x,int y) {
    int     ret = x/y;
    if(y == 0){ //ゼロ除算回避?
        return  0;
    }
    return  ret;
}
int main(void) {
    printf("%d\n",div(3,0)) ;
}

x/yを実行する前に
if(y== 0)でゼロ除算回避してください。

参考:

CWE-369: Divide By Zero

INT33-C. 除算および剰余演算がゼロ除算エラーを引き起こさないことを保証する

https://www.jpcert.or.jp/sc-rules/c-int33-c.html