Wandering Thoughts archives

2011-04-02

How to use gdb to call getpeername()

A commenter on my entry about the process state observability gap suggested an evil hack to recover information about the destination of Unix domain sockets: use gdb on the process to call getpeername() on the socket and examine the result. Because I am sometimes a crazy person, I decided to work out how to do this in case I ever needed to do it for real.

Where this stuff hides in the GDB documentation was not obvious to me when I started. General information about gdb's expressions is in the section on printing and displaying data (section 10 in my GDB documentation, 'Examining data'). Information on calling functions in your program from gdb is in the section on altering execution (section 17 in my documentation, specifically 17.5).

First, getpeername() has one of those odd APIs. It takes three arguments; the file descriptor, a pointer to some sort of sockaddr structure to store the name of the peer, and a pointer to where it can read and write the length of the name buffer. Since we are trying to use this on a Unix domain socket, we'd normally use a sockaddr_un structure. You will probably not be so lucky as to be dealing with a program that already has an unused sockaddr_un sitting around ready to be hijacked for this, so we need to make our own. In fact, gdb probably won't even know what a struct sockaddr_un is, so we'll have to fake it.

(gdb takes its knowledge of structures from the program you're debugging, and most programs on most Linux systems don't have debugging information installed in the default configuration.)

This is the sort of situation where I present the gdb commands and then explain them, so:

print/x malloc(256)
This will be our sockaddr buffer. It's larger than a real struct sockaddr_un, but that's harmless and it saves us writing a program to determine exactly how big a sockaddr_un is. I'll assume that gdb calls what it prints here $1, which is true if you're starting from a clean new gdb session.
print/x malloc(4)
This is for the length of the sockaddr buffer (what the getpeername() manpage calls namelen).
print {int} $2 = 256
Set the length of our name buffer for the kernel.

print getpeername(<fd>, $1, $2)
If this prints anything besides zero, something went wrong. Print out errno and work out what.

x/s $1+2
Display the path part of our sockaddr_un buffer. I use x instead of print mostly because dumping memory is really what I'm doing and it feels right.

This is for a 32-bit Linux machine. If you're using a 64-bit machine you may need a different offset in the last command and maybe a different size for the second malloc.

This is not something you really want to do in a program that you want to keep running unperturbed. Even if all goes well we've leaked some memory, and there's a number of ways that something could go badly if your timing with gdb is just unlucky enough (for example, the program could be in the middle of a malloc() or a free() of its own; libc is probably not written to be safely reentrant in this particular situation).

(Having worked all of this out, I would feel very silly if I didn't write it down and then needed it later. I'm sure that it's all completely obvious to people who use gdb regularly.)

linux/GdbGetpeername written at 01:03:29; Add Comment


Page tools: See As Normal.
Search:
Login: Password:
Atom Syndication: Recent Pages, Recent Comments.

This dinky wiki is brought to you by the Insane Hackers Guild, Python sub-branch.