Debugging another user’s process

Sometimes it is necessary to debug a process belonging to another user. Usually, debuggers running with sufficient privileges can attach to another user’s processes. Under UDB this is not the case by default. With default settings, UDB may attach only to processes belonging to the same user as the debugger. It will refuse to attach to processes belonging to another user, even when the debugger is running with sufficient privileges.


Usually, a user with the CAP_PTRACE capability can attach to any other process in the system (so long as it is not already being debugged). In most configurations, this equates with the root user. These privileges are still required to attach UDB to another user’s process but are not, by themselves, adequate.

This behaviour can be overridden by activating permissive communications mode. This mode is disabled by default but can be enabled by setting an environment variable.


Permissive Communications Mode makes cross-user debugging possible but comes with security implications: it opens communications channels that any process on the system may connect to. It therefore carries a risk of local cross-user exploits.

It is therefore recommended that users avoid Permissive Communications Mode where possible. It is particularly desirable to avoid its use on shared systems. If permissive communications are specifically required (for instance, to fit within an existing debugging workflow on a dedicated system) they can be enabled via a environment variable:

UNDO_permissive_comms=true udb <args>

When starting with permissive comms mode, UDB will display a warning to remind the user of the setting currently in effect:

CAUTION: attaching with permissive comms mode

Record and replay of external device state

The Undo Engine works fine with programs that access external device state, and during replay the developer can see a copy of any external device state as far as it impacts the program being recorded. For example, when the program reads from a memory-mapped device, the values that are read are stored in our event log.

In general, a detailed view of the external device state is not necessary in order to be able to debug problems in the software that controls that external device. What matters is the inputs and outputs to the device, for which the Undo Engine gives full visibility.

It’s important to note that the Undo Engine don’t just make a recording of the system events (in the manner of a tool such as strace), but it records the program in a manner that allows the reconstruction of all of the state and events produced by the program at any time in its history, whether or not they interact with the underlying system.

Synchronized debugging

The Undo Engine does not allow synchronized debugging between multiple processes at present.

Kernel-space memory

The Undo Engine only records user-space memory.

Streamed recording

The Undo Engine records to an in-memory event log. Streaming the event log to an external location is not currently implemented, but we hope to address this in the future.

Performance analysis

The Undo Engine can tell you about what system calls were executed, and when they were executed. And of course you can set breakpoints on code that handles particular cases. However the Undo Engine only supports user-mode execution, and recording the kernel is not supported.

Finding memory leaks

The Undo Engine cannot perform memory leak detection automatically, but it can assist in the process of searching for memory leaks.

For example, you could set a watchpoint in the malloc_chunk structure describing an allocated block, and run backwards to find the point where that data structure was modified, and so discover which code allocated the block.

The Undo Engine also supports debugging code compiled with Clang’s sanitizers such as AddressSanitizer or LeakSanitizer.

Other limitations

CPU features

The x86-64 CPU features 3dnow, 3dnowext, 3dnowprefetch, clwb, hle, mpx, rdseed, and rtm are not supported.

Exec system call

The execve system call is not supported. This is used to implement the standard C library functions execl(), execlp(), execle(), execv(), execvp() and execve(). The program will be stopped if it attempts to issue such a system call.

Forked programs

When a recorded program executes the clone syscall to create a new process (for instance, via the fork() or vfork() C library functions), the Undo Engine keeps recording the parent process and the child process runs unimpeded without being recorded.

This means that, when using the LiveRecorder API, the child (forked) process must call undolr_start() to be recorded. live-record and UDB cannot record the child process automatically.


The Undo Engine cannot continue if the program is terminated by SIGKILL. The debugging session is terminated immediately in this case.

User-defined command hooks

udb uses “user-defined command hooks” to hook many of GDB’s commands, so these hooks are not available to the user.

Obscure system calls

Certain rarely used system calls are not supported. The following system calls are either esoteric, or obsolete, and only maintained in the kernel to maintain backwards compatibility with binaries written for early 2.x series kernels: modify_ldt, pivot_root, ssetmask, unshare, vm86.


If the program uses setrlimit() to reduce the amount of memory, processes, or other resources that it may consume, then the Undo Engine may not be able to operate properly due to lack of resources.

x86 inter-segment (“far”) jumps/calls

The Undo Engine does not support the use of “far” jumps and calls.

Cross-memory attach

The Undo Engine does not support programs whose address spaces are written to others using the cross-memory attach facility.

Self-modifying code

The Undo Engine supports self-modifying code, with two exceptions. First, the Undo Engine caches its translation of the currently executing BB and does not update it until the end of the BB. This means that it does not suport code that modifies instructions and then executes them without an intervening branch or system call. Second, execution of code in shared memory is not supported.

Shared memory accesses straddling valid and invalid pages

When run natively, an instruction with an operand that straddles a page boundary, such that the first part of the operand is in accessible shared memory, but the second part is in mapped shared memory which is not backed by a valid shared object (for example, because the file which is mapped has been truncated) will get a SIGBUS. Under the Undo Engine, such an instruction does not get a SIGBUS: instead, it reads zeros for the part of the operand in unbacked memory.

Breakpoints when reverse-stepping

The reverse-finish reverse-next, reverse-step and reverse-until commands in udb do not stop at breakpoints, unlike their forward-execution equivalents.

System call output buffers

When run natively, a system call with an operand that straddles a page boundary, such that the first part of the operand is in accessible memory, but the second part is in inaccessible memory, succeeds if the system call only needs to access the first part of the operand. For example, if a read() system call is passed an 8K buffer of which the first 4K is in writable memory, and the second 4K is in non-writable memory, then the system call succeeds if less than 4K is read. Under the Undo Engine, such a system call fails with EFAULT. The whole buffer must be accessible in order for the system call to succeed.

Adjust Flag

According to the Intel manuals, the state of the Adjust Flag (AF) after some instructions is “undefined”. On some processor models, different executions of the same code can produce different states of AF. If the behavior of a program depends on the state of AF when it is supposed to be undefined, the program may not replay correctly under udb.

SIGCHLD while attaching

If a SIGCHLD arrives for a process while the Undo Engine is in the middle of attaching to the process, the SIGCHLD may be silently lost. Once the process has been attached, SIGCHLD is handled normally.