TL;DR
Attaching GDB to a process after it has dropped privileges requires careful setup. This guide explains how to do this, focusing on using a shared memory region for communication and setting up the correct permissions.
Steps
- Shared Memory Setup in the Target Process (Before Privilege Drop)
- Allocate a shared memory region. This will be used to signal GDB when it’s safe to attach.
- Create a named semaphore or file lock associated with the shared memory. This prevents race conditions during attachment.
- Write a known value (e.g., 0x42424242) into the shared memory region as an initial signal.
- Privilege Drop
- GDB Script Setup (On the Debugging Machine)
- Create a GDB script that waits for the signal in the shared memory region before attaching.
- The script should also handle potential race conditions by using a semaphore or file lock.
- Attach with GDB
- Run GDB on the target process, loading the script:
- Verify Attachment
- Cleanup (Important)
- After debugging, detach from the process.
- Unmap the shared memory region in the target process.
- Remove the named semaphore or file lock.
- Close and unlink the shared memory segment.
#include <sys/mman.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>>
int main() {
const char *shmem_name = "/tmp/my_shared_memory";
size_t shmem_size = 4096; // Example size
int fd = shm_open(shmem_name, O_CREAT | O_RDWR, 0666);
if (fd == -1) { perror("shm_open failed"); return 1; }
ftruncate(fd, shmem_size); // Set size
void *shmem = mmap(NULL, shmem_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (shmem == MAP_FAILED) { perror("mmap failed"); return 1; }
*((int *)shmem) = 0x42424242; // Initial signal
printf("Shared memory created at %pn", shmem);
// ... rest of your program, including privilege drop ...
}
Perform the privilege drop using methods like setuid(), seteuid(), or by switching to a less privileged user. Ensure this happens *after* setting up the shared memory.
# gdb script (wait_for_attach.gdb)
set $shmem_name = "/tmp/my_shared_memory"
set $shmem_size = 4096
while true {
file /proc/[pid]/mem # Replace [pid] with the target process's PID
p *((int *)$shmem_name)
if ($result == 0x42424242) {
break
}
sleep 1
}
attach [pid]
gdb -x wait_for_attach.gdb [target_process]
Once attached, verify that you can inspect the process’s memory and state within GDB.
Important Considerations
- Permissions: Ensure the user running GDB has read/write access to the shared memory region.
- Race Conditions: The semaphore or file lock is crucial to prevent GDB from attaching before the shared memory is initialized.
- PID Stability: If the target process’s PID changes, you will need to update the GDB script accordingly.
- Security: Shared memory can be a security risk if not handled carefully. Limit access and ensure proper cleanup.

