Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Setting conditional breakpoint to catch an invalid pointer value

Tags:

c

linux

gdb

I'm debugging a big multi-threaded application and trying to catch a bug when an invalid pointer value is being assigned to a variable. I cannot simply backtrace from the point of failure because consumer(s) and producer(s) are different threads, so I'm trying to set up a conditional breakpoint on every assignment statement of that variable to test if assigned value is invalid.

Let's consider this example:

#include <stdio.h>

int main() {
    char* arr[] = {"foo", "bar", 0x1234, "baz"};

    for(int i=0; i<4; ++i) {
        char* str = arr[i];

        puts(str);
    }
}

I found that I can handle it like this:

break test.c:7 if !arr[i][0]

It works, however, I'm unsure if this approach is anyhow reliable because it actually tries to access (probably) invalid memory addr. Also, it has an obvious false-positive when string is empty.

Also, I came across this answer, but it looks like I'd have to somehow tell linker to add those functions into my executable to be available for gdb, and also it looks too complicated to use inside a one-liner condition.

So, my question is: is there any reliable way to check if pointer is invalid inside gdb, which is short enough to use inside a one-liner condition and does not depend on pointed value.

like image 243
Denis Sheremet Avatar asked Dec 20 '25 18:12

Denis Sheremet


1 Answers

Since you are on Linux, the best approach is most likely to use reverse debugger, such as rr.

You run the program with rr record ./a.out arg0 ..., and then, once it crashes, you can set a watch point on the "bad" variable, and reverse-continue right to the place where the variable was last changed. It works like magic!

Here is a session with your example:

$ rr record ./a.out
rr: Saving execution to trace directory `$HOME/.local/share/rr/a.out-5'.
foo
bar
Segmentation fault

$ rr replay
...
Program stopped.
0x00007f69546b4f30 in _start () from /lib64/ld-linux-x86-64.so.2
(rr) c
Continuing.
foo
bar

Program received signal SIGSEGV, Segmentation fault.
__strlen_avx2 () at ../sysdeps/x86_64/multiarch/strlen-avx2.S:96
96  ../sysdeps/x86_64/multiarch/strlen-avx2.S: No such file or directory.
(rr) bt
#0  __strlen_avx2 () at ../sysdeps/x86_64/multiarch/strlen-avx2.S:96
#1  0x00007f6954366040 in __GI__IO_puts (str=0x1234 <error: Cannot access memory at address 0x1234>) at ioputs.c:35
#2  0x000056227d5ec189 in main () at foo.c:9

(rr) up
#1  0x00007f6954366040 in __GI__IO_puts (str=0x1234 <error: Cannot access memory at address 0x1234>) at ioputs.c:35
35  ioputs.c: No such file or directory.
(rr) up
#2  0x000056227d5ec189 in main () at foo.c:9
9           puts(str);
(rr) p str
$1 = 0x1234 <error: Cannot access memory at address 0x1234>
(rr) watch -l str
Hardware watchpoint 1: -location str
(rr) reverse-cont
Continuing.

Program received signal SIGSEGV, Segmentation fault.
__strlen_avx2 () at ../sysdeps/x86_64/multiarch/strlen-avx2.S:96
96  ../sysdeps/x86_64/multiarch/strlen-avx2.S: No such file or directory.
(rr) reverse-cont
Continuing.

Hardware watchpoint 1: -location str

Old value = 0x1234 <error: Cannot access memory at address 0x1234>
New value = 0x56227d5ed008 "bar"
0x000056227d5ec179 in main () at foo.c:7
7           char* str = arr[i];
(rr) p i
$2 = 2
(rr) p arr[i]
$3 = 0x1234 <error: Cannot access memory at address 0x1234>

like image 188
Employed Russian Avatar answered Dec 23 '25 06:12

Employed Russian