__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のチェックを行ってから比較を行う.