c++pointersgdbdereference

GDB pointer dereference gives wrong address


I'm using GDB and I have a pointer to a c-style string in one of the registers. I want to display that string. However when I try to dereference the pointer in the register, or even manually, it gives me the wrong address. Why is that? How do I actually do it? Here RDX probably points into the environment variables. I'm using a unix system (Ubuntu 22.04). GDB version is GNU gdb (Ubuntu 12.0.90-0ubuntu1) 12.0.90 I loaded it with --nx just to make sure that it's not one of my plugins.

(gdb) info registers
[..snip..]
rdx            0x7fffffffd8e8      140737488345320
[..snip..]

(gdb) x/1s *$rdx
0xffffffffffffdd6a: <error: Cannot access memory at address 0xffffffffffffdd6a>
(gdb) x/1xg $rdx
0x7fffffffd8e8: 0x00007fffffffdd6a
(gdb) x/1s 0x00007fffffffdd6a
0x7fffffffdd6a: "QT_SCALE_FACTOR=1"
(gdb) x/1s *0x7fffffffd8e8
0xffffffffffffdd6a: <error: Cannot access memory at address 0xffffffffffffdd6a>

Edit:

I wrote a test program, and with that the dereferencing works fine.

#include <cstdint>
#include <print>

int main()
{
    char   cstr[] = "Testing.";
    char*  ptr1   = reinterpret_cast<char*>(cstr);
    char** ptr2   = &ptr1;

    std::print("String: {0}, ptr1: {1:x}, ptr2: {2:x}", cstr, (uint64_t)ptr1, (uint64_t)ptr2);
    return 0;
}
pwndbg> print ptr2
$3 = (char **) 0x7fffffffd7e8
pwndbg> print ptr1
$4 = 0x7fffffffd7f0 "Testing."
pwndbg> x/1s ptr2
0x7fffffffd7e8: "\360\327\377\377\377\177"
pwndbg> x/1s *ptr2
0x7fffffffd7f0: "Testing."

It seems that only dereferencing $registers gives bad values?

That other program was

int main()
{
  __builtin_trap();
  return 0;
}

by the way, compiled with clang++ -g -O0 trap.cpp -o trap


Solution

  • The error message hints at what is going wrong here:

    (gdb) x/1s *$rdx
    0xffffffffffffdd6a: <error: Cannot access memory at address 0xffffffffffffdd6a>
    

    Notice the address 0xffffffffffffdd6a and then compare this to the output you saw when you did this:

    (gdb) x/1xg $rdx
    0x7fffffffd8e8: 0x00007fffffffdd6a
    

    Notice that GDB tried to access a different address. What we see is that GDB has truncated the result of *$rdx to 32-bits, and then sign extended this to 64-bits: 0x00007fffffffdd6a -> 0xffffdd6a -> 0xffffffffffffdd6a.

    The reason for this is because:

    (gdb) ptype $rdx
    type = int64_t
    

    The register is an integer, not a pointer. So, when you dereference it, GDB doesn't know the size of the object being pointed to.

    Due to the way GDB's expression evaluation works, there's currently no way to forward the information about how the result of the dereference is going to be used -- if we know at the time of the dereference that the result would be treated as a char * then we might infer that we should treat $rdx as char **, but that would be a pretty big change to GDB I think.

    So, for now, GDB just defaults to assuming that $rdx is of type int *, which gives us a 32-bit value.

    Still, you can convince GDB to perform a 64-bit access, like:

    (gdb) p/1s *(char **)$rdx
    

    which I think should do what you want.