detly
detly

Reputation: 30342

How can I break on multiple clang/ubsan warnings in gdb?

Take the following test program (compiled with clang 3.4 and run under gdb 7.6.1):

#include <limits.h>
#include <stdio.h>

int main(void)
{
    int a = INT_MAX + 1;
    int b = INT_MAX + 2;
    printf("Result: a = %d, b = %d\n", a, b);
}

I would like to be able to use gdb to automatically stop at the second occurrence of undefined behaviour here (int b = ...).

If I compile with:

clang -fsanitize=undefined -O0 -ggdb3 -o test test.c

...then running the program under gdb just results in it running to completion:

test.c:6:21: runtime error: signed integer overflow: 2147483647 + 1 cannot be represented in type 'int'
test.c:7:21: runtime error: signed integer overflow: 2147483647 + 2 cannot be represented in type 'int'
Result: a = -2147483648, b = -2147483647
[Inferior 1 (process 24185) exited normally]

But if I use:

clang -fsanitize=undefined-trap -fsanitize-undefined-trap-on-error -O0 -ggdb3 -o test test.c

...then I can't continue past the first occurrence:

Program received signal SIGILL, Illegal instruction.
0x0000000000400556 in main () at test.c:6
6       int a = INT_MAX + 1;
(gdb) c
Continuing.

Program terminated with signal SIGILL, Illegal instruction.
The program no longer exists.

Is it possible to get gdb to break when (and only when) undefined behaviour is flagged, but to let the program continue otherwise? I'm after a technique that will work not just on this example, but in general, where the offending line might be inside a loop, the values may be determined at runtime, etc.

Upvotes: 2

Views: 782

Answers (1)

user184968
user184968

Reputation:

On x86-64 the instruction that causes SIGILL and stops a program is ud2 (http://asm.inightmare.org/opcodelst/index.php?op=UD2). In order to archive your goal you can change in gdb handling of SIGILL and use jumping (you need to add 2 to $pc on x86_64):

This is how the instruction ud2 is placed in the code of your test program on x86_64:

   0x00000000004004f0 <+32>:    0f 85 02 00 00 00       jne    0x4004f8 <main+40>
=> 0x00000000004004f6 <+38>:    0f 0b   ud2
   0x00000000004004f8 <+40>:    b8 ff ff ff 7f  mov    $0x7fffffff,%eax

These are gdb commands that is necessary to use:

handle SIGILL stop print nopass
set $pc = $pc + 2

This is an example for your test program:

$ gdb -q ./test
Reading symbols from /home/test...done.
(gdb) handle SIGILL stop print nopass
Signal        Stop      Print   Pass to program Description
SIGILL        Yes       Yes     No              Illegal instruction
(gdb) r
Starting program: /home/test

Program received signal SIGILL, Illegal instruction.
0x00000000004004f6 in main () at test.c:6
6         int a = INT_MAX + 1;
(gdb) set $pc = $pc + 2
(gdb) c
Continuing.

Program received signal SIGILL, Illegal instruction.
0x000000000040051f in main () at test.c:7
7         int b = INT_MAX + 2;
(gdb) set $pc = $pc + 2
(gdb) c
Continuing.
Result: a = -2147483648, b = -2147483647
[Inferior 1 (process 7898) exited normally]
(gdb)

Useful links:

Upvotes: 1

Related Questions