__chk_range_not_ok() in Linux x86
Linux(tag v5.4)のソースコードリーディングをしていたときに面白いと思った関数のメモ.
__chk_range_not_ok()
arch/x86/include/asm/uaccess.h
にて以下のように定義される.
static inline bool __chk_range_not_ok(unsigned long addr, unsigned long size, unsigned long limit) { /* * If we have used "sizeof()" for the size, * we know it won't overflow the limit (but * it might overflow the 'addr', so it's * important to subtract the size from the * limit, not add it to the address). */ if (__builtin_constant_p(size)) [1] return unlikely(addr > limit - size); [2] /* Arbitrary sizes? Be careful about overflow */ addr += size; [3] if (unlikely(addr < size)) return true; return unlikely(addr > limit); }
この関数の動作は符号なし整数型のサイズを無視して記述すればreturn addr + size > limit
である.
しかし,Cコードの世界ではwrap aroundを考慮して記述しなければならない.(addr + size
がwrap aroundする可能性がある)
愚直に実装するのであれば[3]以下のようにaddrにsizeを加算しwrap aroundをチェック.
wrap aroundすれば0を返す.wrap aroundしなければreturn addrにsizeを加算した値 > limit
すればよい.
この関数ではコンパイラの組み込み関数である__builtin_constant_p()
を用いることで関数の高速化を図っている[1].
__builtin_constant_p()
は引数に数値を取り,引数がコンパイル時に定数であること(constant-flodingされた場合なども含む)を証明できれば1,出来なければ0を返す.(戻り値0は定数でないことを意味しない,定数と証明できないことを意味する)
その場合はsize > limit
とならないように実装されているため,limit - size
はwrap aroundを起こさない.
よって[2]のように比較を行うことが可能である.
定数であることを判別できない場合はオーソドックスにwrap aroundのチェックを行ってから比較を行う.