Search code examples
clanginteger-overflowubsan

Confusing UBSan results for integer overflows


bool addSigned(int a)            { return a + 10 > a; }
bool addUnsigned(unsigned int a) { return a + 10 > a; }

int main() {
    // UB reported only with -fsanitize=undefined:
    bool res1 = addSigned  (0x7ffffffe); 
    // UB reported only with -fsanitize=unsigned-integer-overflow:
    bool res2 = addUnsigned(0xfffffffe); 
}

Why? I'd expect res1 to trigger UB report under both fsanitize=undefined/unsigned-integer-overflow, and res2 to trigger no UB under either. What am I missing?

godbolt link for convenience


Solution

  • -fsanitize=undefined traps on res1 because it causes undefined behavior. It doesn't trap on res2 because that does not cause undefined behavior.

    According to https://source.android.com/docs/security/test/intsan, the -fsanitize=[[un]signed-]integer-overflow sanitizers are to enable particular subsets of UBSan functionality. UBSan does contain a mode, which is disabled by default, in which it traps on unsigned integer overflow. Even though that is not undefined behavior, there could still be cases where the programmer wants to avoid having unsigned overflow occur, and this option would help them detect it.

    So -fsanitize=unsigned-integer-overflow enables only that trap. It will trap on all instances of unsigned integer overflow, such as res2. It will not trap on anything else, such as res1.

    The "undefined behavior" in the error message is misleading. Probably UBSan prefixes all its messages with the string "undefined behavior", even for these obscure modes which detect something that's not actually UB.