Repository navigation
Branches ready to propose upstream #1
Description
Branches on this fork that are reviewed and ready to propose upstream to rr-debugger/rr. They are opened gradually, so upstream can review and merge at its own pace. Each commit message is written to serve as the PR description. The "Open PR" links prefill the upstream PR form; nothing is opened until someone clicks through.
All branches are based on rr master 858a9fb unless noted.
Ready
| # | Branch | Head | Commit(s) | Test(s) | Notes |
|---|---|---|---|---|---|
| 2 | exec-from-thread-segv |
cabb7b4 | Fix exec handling in multithreaded processes | exec_killed_at_exec_stop, exec_killed_initial_process, exec_killed_traced, exec_killed_sibling_tracer, ptrace_write_at_exec_stop, ptrace_exec_stop_end, ptrace_exec_killed_before_scratch |
Stacked on #93, #67 and #68 (through merge commits). Restacked on 2026-10-28 onto the reworked #93 (master 16fa499) and re-tested, also as uid 1000 and with --chaos; adapted to the record_step() switch, and it now also drops a leader kept for a non-parent ptracer at its exec stop and handles a missing scratch mapping at a replayed exec stop (after rr-debugger#4137). The EV_EXEC commit (rr records a frame where a tracee's emulated exec stop starts and ends, so replay applies the exec's register reset at the right point; fixes what a ptracer saw around a non-leader exec). Its exec_after_waking_readers case still hangs until #94 lands. Conflicts with #15/#16 (whichever lands second reconciles). It still bundles two fixes; its description maps them. Open PR |
| 7 | seccomp-event-after-kill |
6215848 | Handle a seccomp stop that the task left before rr processed it | exit_group_restarted_syscalls (the race it tests only happens in some recordings; master fails 40/40 as root) and exit_group_after_syscall_interruption |
Stacked on #50. Your call whether to send it: with #82 merged its race can't happen on kernels from 4.8 on (what would remain is pre-4.8 kernels and one case argued from reading code); my recommendation is to hold it behind #82. On master c145f2f through #50; rebased and reviewed again on 2026-10-27, tests also as uid 1000 and with --chaos. The version you approved (no maybe_restart_blocked_syscalls_for_exit_group() hook and no extra kill point; the older hook version is kept locally). rr asserted in SeccompFilterRewriter when another thread's exit_group killed a thread at an unprocessed seccomp stop; rr now notes which thread enters a multithreaded exit_group() and treats the other threads' unprocessed seccomp stops as never having happened. The "Expected syscall-state" bug its description used to list is fixed by #82. Open PR |
| 9 | int80-syscall-args |
56535a4 | Use the i386 argument registers for int $0x80 syscalls of x86-64 tasks | x86/int80_args, x86/int80_fork |
On master c145f2f; base of the int80 series (reviewed (int80 series split on 2026-10-28), tests also as uid 1000 and with --chaos). rr took an x86-64 task's int $0x80 syscall arguments from the x86-64 argument registers instead of the i386 ones (rbx, rcx, rdx, rsi, rdi, rbp). #109–#113, #115 and #116 are stacked on it. Open PR |
| 10 | int80-ia32-signal-frames |
2160c3b | Stop recording at a signal for a handler that runs in 32-bit mode | x86/int80_sigframe |
Stacked on #115. Your call: it only stops recording cleanly (a refusal), which doesn't meet your PR bar; real support needs tasks that switch between 64-bit and 32-bit mode (its description lists what depends on that). Drop, keep for later, or go for real support? Open PR |
| 11 | replay-notify-syscall-hook-exit |
3e739b3 | Set notify_on_syscall_hook_exit when replaying an aborted commit | ptrace_singlestep_interrupted_syscallbuf |
On master c145f2f; reviewed after the rebase, also as uid 1000. Replay of a tracer that single-steps through rr's preload code failed: replay set this syscallbuf header flag later than recording. Open PR |
| 12 | ptrace-watchpoint-syscallbuf-copy |
89eb418 | Drop debug register SIGTRAPs that trigger in syscallbuf code | x86/ptrace_debug_regs_syscallbuf |
On master c145f2f; reviewed, tests also as uid 1000 and with --chaos. x86: a ptracer's hardware watchpoint fired in the syscall buffer's copy-out (natively the kernel's write doesn't trap) and aborted the recording ("Can't defer deterministic or internal signal"). An aarch64 follow-up (#20) is stacked on it. The draft has a placeholder for #20's PR number. Open PR |
| 13 | ptrace-singlestep-restarted-syscall |
cfc9ef6 | Don't single-step a tracee into a syscall that the kernel restarts | ptrace_singlestep_restarted_syscall |
On master c145f2f; rebased and reviewed again on 2026-10-27, tests also as uid 1000 and with --chaos (the review's blocker, a hang with PTRACE_SYSEMU_SINGLESTEP, is fixed: the change applies to PTRACE_SINGLESTEP only, and the test covers both). x86: a tracer single-stepping out of a signal stop inside a blocked buffered syscall made recording abort in compute_trap_reasons. Open PR |
| 15 | ptrace-interrupt-sigtrap |
180d9a1 | Remember which signal stopped a process; Report PTRACE_INTERRUPT stops with SIGTRAP unless group-stopped | ptrace_interrupt_syscall, ptrace_seize, ptrace_resume_signal_ignored |
On master c145f2f; rebased onto master c145f2f and reviewed on 2026-10-27, tests also as uid 1000 and with --chaos. Two commits; the first ("Remember which signal stopped a process", 7e04aae) is also the base of #25 and #26. Its ptrace_seize test still fails about 30% of --chaos runs until #16 lands (a master bug #16 fixes). Open PR |
| 16 | ptrace-interrupt-syscall-stop |
32c2328 | Report the stops Linux reports for a PTRACE_INTERRUPT in a syscall | ptrace_interrupt_syscall_stops, ptrace_sysemu_interrupt, ptrace_interrupt_restarted_syscall, x86/ptrace_interrupt_singlestep_syscall (recorded with --chaos) |
Stacked on #15. rebased onto master c145f2f and reviewed on 2026-10-27, tests also as uid 1000 and with --chaos. Also fixes rr's "Expected ENOSYS for 'restart_syscall'" assertion when a tracee interrupted in a restarted sleep is PTRACE_INTERRUPTed. Conflicts with #2 (whichever lands second reconciles). Open PR |
| 18 | ptrace-syscall-desched-exit |
4671a68 | Report the syscall-exit stop of a descheduled buffered syscall | ptrace_syscall_desched |
Stacked on #101 and #82 (through a merge commit; its PTRACE_SYSEMU subcase needs #82). rebased onto master c145f2f and reviewed on 2026-10-27, tests also as uid 1000 and with --chaos. The tracer's syscall-exit stop of a descheduled buffered syscall was missing, and PTRACE_SYSEMU / cancelled restarts at descheduled syscalls misbehaved. Replaces the old stack on #17. Open PR |
| 19 | ignored-signal-before-seize |
053faa8 | Don't report ignored signals from before an emulated ptrace attach | ptrace_seize_ignored_signals |
On master c145f2f; rebased and reviewed again on 2026-10-27, tests also as uid 1000 and with --chaos. An ignored, unblocked signal sent before a ptracer attached (natively discarded at generation) later produced a signal-delivery stop under rr. Open PR |
| 20 | ptrace-watchpoint-syscallbuf-copy-aarch64 |
7fe5d7e | Step over debug register traps in syscallbuf code on aarch64 | arm/ptrace_hw_watch_syscallbuf |
Stacked on #12 and #73 (on master c145f2f); reviewed, tests also as uid 1000 and with --chaos (x86 only). aarch64 counterpart of #12 (watchpoints trap before the access there, so rr steps over them; a task SIGKILLed during that step now has its exit handled first). Compiled but never run on aarch64 hardware: rr's arm64 CI on the PR will be its first real run. Open after #12 and #73; the draft has placeholders for their PR numbers. Open PR |
| 22 | tracee-trap-flag |
2013433 | Fail cleanly when a tracee sets the trap flag | x86/trap_flag, x86/trap_flag_once, x86/signal_at_popf, x86/ptrace_trap_flag, x86/ptrace_trap_flag_signal, x86/ptrace_singlestep_trap_flag |
On hold: only worth sending with proper EFLAGS.TF emulation, which is being investigated. rr doesn't support a tracee- or tracer-set EFLAGS.TF (the tracer saw 1 trap instead of N and replay failed); rr now ends the recording with a clear error, and the partial trace replays. Open PR |
| 25 | wait-continued |
69d234c | Emulate WCONTINUED waits and the SIGCHLD for a continued child | wait_continued, wait_continued_traced, wait_continued_two_waiters, wait_sigchld_blocked_thread, wait_stop_and_exit, sigchld_nocldstop |
Stacked on #15's first commit and its own copy of #107. Your call: rewrite it on top of the SIGCHLD stack (#30, #74–#76, #88–#92), which changed the same code, or keep it as is. rebased onto master c145f2f and reviewed on 2026-10-27, tests also as uid 1000 and with --chaos. Split into #107, this (WCONTINUED waits and CLD_CONTINUED SIGCHLDs) and #108. Open PR |
| 26 | ptracer-death-event-stop |
fe6d8bc | 3 commits (as #102, #103 and "Deliver a tracee's signal if its ptracer exits without waiting for it") |
ptracer_death_event_stop, ptracer_death_group_stop, ptracer_death_interrupt, ptracer_death_leader, ptracer_death_signal, ptracer_death_syscall_stop, ptrace_group_stop_real_parent, x86/ptrace (extended) |
Stacked on #103 (and through it on #68 and #15's first commit). rebased onto master c145f2f and reviewed on 2026-10-27, tests also as uid 1000 and with --chaos. Was 4 commits; its first one is replaced by #68, and it is now split into #102 → #103 → this. This one: deliver a tracee's pending signal if its ptracer exits without waiting for it. Its description still covers all three (needs splitting if you open them separately). Open PR |
| 27 | ptrace-exitkill |
3d322b8 | Emulate PTRACE_O_EXITKILL | ptrace_exitkill |
Stacked on #26. rr rejected PTRACE_O_EXITKILL (EINVAL from PTRACE_SEIZE/PTRACE_SETOPTIONS during recording); now emulated. Probes once whether the kernel drops the SIGKILL for a tracee at its exit event (Linux 6.1+ and some distribution backports). Open PR |
| 28 | ptrace-singlestep-over-syscall-hooks |
5325799 | Don't report a tracer's single-steps inside rr's syscall hooks | x86/ptrace_singlestep_syscallbuf, x86/ptrace_singlestep_sigreturn_restart |
On master c145f2f; rebased and reviewed again on 2026-10-27, tests also as uid 1000 and with --chaos. A tracer single-stepping over a patched syscall saw every instruction of rr's stub and syscall hook (e.g. 343 steps instead of 85 natively); now the step over the syscall instruction ends where it ends natively. Remaining differences are listed in the commit message. Open PR |
| 29 | ptrace-request-tracee-stopped |
f7a4d96 | Wait until a tracee is really stopped before a ptrace request uses it | ptrace_regs_blocked_tracee, ptrace_regs_tracer_sigcont (both deterministic; master fails 10/10) |
Stacked on #16. rebased onto master c145f2f and reviewed on 2026-10-27, tests also as uid 1000 and with --chaos. Its tests were rewritten to reach the group-stop path on top of #16 (the landing-order problem with #16 is resolved). Fixes the intermittent ptrace_group_stop CI failure: on master 29 of 120 --chaos runs fail with the regs() FATAL, 0 of 120 with this. Open PR |
| 30 | synthetic-sigchld-siginfo |
ddf9b77 | Fill in a synthetic SIGCHLD's siginfo as Linux does | sigchld_siginfo |
On master c145f2f; reviewed (5 rounds), tests also as uid 1000 and with --chaos. The SIGCHLD rr synthesizes for an emulated stop reached handlers with si_status 0 (or the whole ptrace stop code), and reached sigwaitinfo()/sigtimedwait()/signalfd readers unfilled (SI_QUEUE, si_pid 0). Its companion fixes are now three stacked branches, #74 to #76; this one doesn't depend on them, its description names #75 and #76, and it merges cleanly with #76. Open PR |
| 31 | singlestep-sigtrap-state |
81acafb | Keep track of what a single-step's SIGTRAP does to SIGTRAP's state | ptrace_singlestep_forced_sigtrap, perf_event_sigtrap_ignored |
On master c145f2f; rebased and reviewed again on 2026-10-27, tests also as uid 1000 and with --chaos. A tracer's single-step forces a SIGTRAP, which resets a blocked or ignored SIGTRAP handler to SIG_DFL and unblocks it. rr didn't track that (Debug: "SIGTRAP is not caught" assertion; Release: a tracer-delivered SIGTRAP the tracee natively dies from was dropped), and for a step over a syscall, which rr emulates, the effects didn't happen at all. Open PR |
| 32 | sigtimedwait-spurious-eintr |
4ac32bd | Restart waits woken only by ignored signals or rr's own signals | ignored_signals_eintr (master fails 10/10 per variant) |
Stacked on #99; must not land without it (alone it makes some recordings unreplayable that replay on master). On master c145f2f through #99; reviewed, tests also as uid 1000 and with --chaos (all runs in private PID namespaces). Also handles a pid-namespace init. Because rr traces the process, signals it ignores are queued, and rr's own signals (time slice, desched) are delivered; either woke a blocking sigwaitinfo()/sigtimedwait(), epoll_wait()/epoll_pwait(2) or semop()/semtimedop(), which then failed with EINTR where natively the thread keeps waiting. rr now has the kernel restart such a wait when nothing else could have woken it. The cases left out are listed in the commit message. A group-stop test case can be added once rr-debugger#4112 has merged (without it, master hangs in that scenario). Open PR |
| 36 | replay-unmap-local-mapping |
851a278 | 2 commits: "Don't munmap rr's memory below a local mapping", "Keep exec'd tasks' buffers pending unmap in checkpoints" | clone_vm_exec_map_over_buffers, vfork_exec_scratch_checkpoint |
On master c145f2f, reduced to what roc's cf60fc6 (rr-debugger#4124) doesn't cover; reviewed, also as uid 1000. Replay crashed (SIGSEGV reading a syscall buffer) when a tracee mapped memory over a range starting below a dead syscall buffer: the local munmap went below the mapping. Restarting from a checkpoint lost the note to unmap an exec'd task's buffers (EEXIST assertion). Left out: recording's unmap at the exec's syscall exit (it can unmap memory the tracee mapped at the exec stop); processing the exec at the exec stop in replay (#2's direction) is the fix there. Open PR |
| 38 | syscallbuf-efault |
c67ca96 | Check tracee memory before the syscall buffer copies to or from it (+2 more) | syscallbuf_efault, syscallbuf_efault_syscalls, traced_syscall_efault, syscall_args_efault, arm/tagged_addr_syscalls, arm/tagged_addr_ctrl (extended) |
Stacked on #104. rebased onto master c145f2f and reviewed on 2026-10-27, tests also as uid 1000 and with --chaos. Split into #104 (traced syscalls), this (syscall buffer) and #105 (argument reads). Buffered syscalls on bad or partly inaccessible memory made rr copy results into memory the kernel would have refused (SIGSEGV in the tracee, or "Can't defer"). Open PR |
| 39 | killed-in-signal-stop |
f6bb9c3 | Don't take a PTRACE_EVENT_EXIT stop's siginfo for a signal-stop's | killed_in_signal_stop, killed_injecting_signal, fake_ptrace_exit_siginfo (kill point RRCALL_KILL_BEFORE_GETSIGINFO of rr-debugger#4114) |
On master 50d54f2 (merges cleanly with c145f2f); one commit (the two earlier ones squashed, same tree); reviewed, also as uid 1000. Also fixes the rare killed_task_exit_order assertion failure on master (same compute_trap_reasons() assertion). When a SIGKILL ended a signal-stop before rr read its siginfo, rr got the PTRACE_EVENT_EXIT stop's siginfo instead and acted on a SIGTRAP nobody sent (compute_trap_reasons() assert, a stashed fake SIGTRAP rejected in prepare_exit(), or a fatal-signal injection that resumed the task out of its exit stop). Open PR |
| 40 | post-exec-dead-task |
3657c48 | Keep recording when a task is SIGKILLed while we process its exec | exec_killed_during_post_exec (sweeps every remote syscall with the RRCALL_KILL_BEFORE/AFTER_EXEC_REMOTE_SYSCALL kill points of rr-debugger#4114, with --checksum and --check-cached-mmaps) |
Needs a rebase onto master 16fa499 (it was stacked on rr-debugger#4137, which has merged; reviewed, tests also as uid 1000). Its description names rr-debugger#4137 as a stacked base. A SIGKILL (e.g. pid-namespace teardown) during rr's post-exec setup aborted recording at one of its infallible remote syscalls (15 of 16 kill points). Open PR |
| 43 | mem-fd-dead-task-warning |
b752cc8 | Don't reopen the mem fd through a running task | kill_threads_no_exit_events, exec_from_other_thread_warnings |
On master c145f2f; reviewed, tests also as uid 1000 and with --chaos. rr reopened /proc/<tid>/mem through a task that wasn't stopped or was already dead and warned "racing with exec?", which aborts recording with --fatal-errors (every recording of exec_from_other_thread with the syscall buffer, and SIGKILLs that take out a whole thread group); before Linux 6.16, reads through such a dead task retried forever. Now it reopens only through a stopped task, at most once per access, and otherwise fails the access. Open PR |
| 44 | adopt-fork-child-of-killed-parent |
591fabc | Adopt the child of a task killed in fork() or vfork() | fork_child_of_killed_parent (kill point RRCALL_KILL_AT_CLONE_EVENT of rr-debugger#4114; skipped without /proc/<pid>/task/<tid>/children) |
Stacked on #69 (on master c145f2f; reviewed, tests also as uid 1000; uses Scheduler::wait_for_killed_task()). Open together with #69. The test now blocks instead of polling with a 10 s cap. When a task was SIGKILLed in fork()/vfork() before rr saw the fork event, the new child stayed stopped forever. rr now finds it in the parent's /proc children list and adopts it. Remaining limits (CLONE_PARENT, kernels without the children file) are in the message. Open PR |
| 47 | exec-vdso-read-killed |
0e3fd63 | Read the new vdso through the mem fd at exec | exec_killed_before_post_exec (kill point RRCALL_KILL_BEFORE_POST_EXEC of rr-debugger#4114; the child execs the other architecture's simple) |
Stacked on #40 (on master c145f2f; rebase with #40; reviewed, tests also as uid 1000). At the first exec of each architecture, rr read the new vdso through ptrace before opening /proc/<tid>/mem; a SIGKILL there made the read come back short and recording aborted (40/40 without the fix). Open PR |
| 48 | os-exec-exact-size |
8782b5e | os_exec: save exactly the words setup_exec_args writes | vfork_checksum (existing) |
On hold: a cleanup, to go with other cleanups later. Follow-up to rr-debugger#4109: size the saved range with the tracee's word size instead of an upper bound. On current master. Open PR |
| 50 | killed-in-desched-handling |
e4899a8 | Handle a task that was killed while we handled its desched signal | killed_in_desched (kill point RRCALL_KILL_AFTER_DESCHED_STOP_WAIT of rr-debugger#4114; the race it tests only happens in some recordings, nearly all here) |
On master c145f2f, now standalone (no longer stacked on #7); reviewed, tests also as uid 1000 and with --chaos. A SIGKILL during rr's desched handling left the task unstopped and rr failed "handle_signal_event left us in a not-stopped state"; handle_ptrace_exit_event() now waits for such a task's PTRACE_EVENT_EXIT stop and handles its exit (moved there from record_step() as you asked) (master: 40/40 recordings as root, 40/40 as uid 1000). Neighbour of #73; merging the two only conflicts in CMakeLists.txt. #73's and #20's descriptions name this PR by title. #7's draft says it must land after this one. The old fork branch seccomp-event-after-kill-desched (cdc8260) is superseded and kept for reference. Open PR |
| 55 | test-fail-assertion-rrcall |
ae8f7dd | Add an rrcall that makes rr fail an assertion, for tests | rrcall_fail_assertion |
On master c145f2f; rebased onto master c145f2f and reviewed on 2026-10-27, tests also as uid 1000 and with --chaos. Test-only rrcall; #106 and #56 build on it. Open PR |
| 56 | emergency-debugger-threads |
6526ebf | Let the emergency debugger show tasks that aren't stopped | emergency_debugger_threads |
Stacked on #106. rebased onto master c145f2f and reviewed on 2026-10-27, tests also as uid 1000 and with --chaos. During recording the emergency debugger called Task::regs() on every task, which asserts for a task blocked in a syscall, so rr failed again as soon as gdb connected. Open PR |
| 59 | sigreturn-without-handler |
baef5cc | Don't assume a sigreturn ends a handler rr delivered | x86/sigreturn_without_handler (fails on master, 64- and 32-bit) |
Recording asserted (pending_events.back().type() == expected_type) at an rt_sigreturn on a signal frame that the program built or copied itself, which ends no handler rr delivered. DynamoRIO delivers signals to the application's handler that way, so recording any program under DynamoRIO failed at the end of its first signal handler. On current master. Open PR |
| 60 | syscallbuf-sigaltstack |
4d4c199 | Place signal frames as without rr when a task is in a syscallbuf stub | sigaltstack_blocking_syscall (10 of 10 recordings fail on master, 0 of 10 with the fix) |
On hold until the syscallbuf redesign (rr-debugger#3322) is rebased. A signal handler on the alternate signal stack that blocked in a buffered syscall had its frames overwritten by the next SA_ONSTACK signal: in the stub the thread's stack pointer is on rr's stack, so the kernel put the new frame at the top of the alternate stack. rr now injects such a signal with the application's stack pointer from the stub, so the kernel places the frame as without rr, and puts the stub's stack pointer in the frame for the sigreturn. Frames of other handlers interrupted in a stub move from rr's stack to the application's, as without rr. In rr's full suite here, the 92 of 3272 tests that fail with the fix (lldb scripts and nested recordings on this host) fail on master too. The aarch64 path has never run on aarch64. On current master. Open PR |
| 61 | software-ticks-hw-breakpoint |
ccdacca | Software ticks: disable the hardware breakpoint when the task stops | software_ticks_jit_loop |
A fix to rr-debugger#4104 (software ticks RFC), on its branch: fold it into that PR rather than opening one. With software ticks, PerfCounters::stop returned before disabling the hardware breakpoint that replay sets on a target in writable memory (a JIT's code), which then trapped every time the task ran that address again: the test's replay didn't finish in 5 minutes (0.3 s with the fix), and replay under DynamoRIO reported an unrecorded SIGTRAP. |
| 62 | software-ticks-syscall-entry |
29c815a | Software ticks: publish a syscall entry for code rr can't patch | software_ticks_syscall_entry |
On rr-debugger#4104 (software ticks RFC): an ABI addition to fold into it or to propose right after it. The ABI (include/rr/softticks.h) gets a pointer after the countdown (RR_SOFTTICKS_SYSCALL_OFFSET, 0x70001808 on x86) to long fn(long no, const long args[6]), which makes a system call through the syscall buffer; the preload library publishes it at process and thread init (0 without rr, with -n, and on aarch64 for now), and it leaves through _syscallbuf_final_exit_instruction like the stubs. For code that runs a program's system calls where rr can't patch them, such as a binary translator's code cache (a DynamoRIO client cut traced syscalls per recording 2.6-5.4x with it). The thread-locals shared memory now extends through the pointer (bytes past the end of the file were not kept; the pointer read as 0 in replay). |
| 63 | stashed-signal-sigmask |
b35fb14 | Don't leave a signal handler's frame with rr's stashed-signal mask | none yet | Needs a test before opening. When rr injects a stashed signal while other stashed signals remain, it blocks all signals for the injection, the kernel saves that mask in the frame, and after the handler returns the thread keeps everything blocked (and rr caches it as the application's mask). Now the application's mask goes in the frame and the handler gets its proper mask. Found recording Julia under DynamoRIO (a thread then never saw DynamoRIO's suspend signal: about one hang in three with -c 20000, 0 of 12 with the fix); small programs haven't produced two stashed signals at once yet. On current master. Open PR |
| 65 | ptracer-exit-singlestep |
6ea98e9 | Keep or drop a tracee's single-step and syscall emulation as Linux does when its ptracer exits | x86/ptracer_death_singlestep (deterministic; fails every run without the fix) |
On hold: part of the larger ptrace-emulation topic. When a recorded ptracer exited without detaching, rr kept resuming its tracee with the ptracer's last request. A tracee single-stepped into a signal handler died of SIGTRAP, where on x86 the handler entry ends the step natively; a tracee under PTRACE_SYSEMU had every later syscall skipped (Linux turns syscall emulation off since 5.3). Overlaps #26 in the ptracer-exit loop of ~RecordTask (resolution in the PR draft). On current master. Open PR |
| 67 | replay-exec-current-task |
7b312cb | Move a non-leader exec's task when replay reaches its exec | continue_exec_from_other_thread (gdb; master FATALs in every variant) |
On master c145f2f, standalone. Split out of #2; reviewed, tests also as uid 1000 and with --chaos. A gdb continue over a non-leader exec during replay crashed rr ("Expected current task but none found"). Reverse execution from the new exec stop still misses the exec'ing thread's earlier stops (new TaskUid); the description says so. Open PR |
| 68 | emulated-tracer-leader-exit |
6ba66d7 | Detach a ptracer's emulated tracees when the ptracer exits | ptracer_death_event_stop (master fails every run, "Unhandled ptrace event PTRACE_EVENT_CLONE") |
On master c145f2f, standalone. Split out of #2; reviewed, tests also as uid 1000 and with --chaos. When a recorded ptracer thread exited, its emulated tracees were detached only when the whole thread group went away, so a tracee held in an emulated stop hung. Replaces #26's first commit; #26 will be rebased onto it. Open PR |
| 69 | killed-at-clone-event |
b13e0e4 | Cope with a task killed in clone() before we get the new tid | clone_thread_killed_at_clone_event |
Piece 2 of the rr-debugger#4136 split. On master c145f2f; reviewed twice, tests also as uid 1000 and with --chaos. A task SIGKILLed while rr runs it into clone()/fork()/vfork(): rr resumed it out of its PTRACE_EVENT_EXIT stop, set up task -1, and left an unknown new thread stuck in its exit stop. Open together with #44, stacked on it: alone, it turns master's immediate FATAL for a fork() killed at its event stop into a hang if anything waits for the orphaned child; #44 adopts that child. Has the same small syscall_state_changed() hunk as rr-debugger#4139 (merges cleanly either way). Open PR |
| 72 | killed-during-scratch-mmap |
5ca7d1e | Find the scratch mapping of a task killed during its mmap | clone_killed_after_scratch |
Piece 5 of the rr-debugger#4136 split. Needs a rebase onto master 16fa499 (it was stacked on rr-debugger#4137, which has merged). reviewed twice, tests also as uid 1000 and with --chaos. When the scratch mmap took effect but the task died before reporting it, rr now finds the mapping in /proc instead of recording none. Open PR |
| 73 | killed-desched-exit-order |
78dfc2f | Handle a killed task's exit first after a desched'd syscall | killed_desched_exit_order (new kill point RRCALL_KILL_AFTER_DESCHED_DISARM_STOP_WAIT) |
On master c145f2f; reviewed, tests also as uid 1000 and with --chaos (Debug builds, as CI builds rr). A task SIGKILLed while rr ran it on to the desched disarm ioctl after a desched'd syscall: rr recorded other tasks' events before the task's exit, so replay hung (master with only the test fails every run with the syscall buffer). Split out of #20, which is stacked on it. Open PR |
| 74 | emulated-wait-options |
5e1c4f5 | Honour __WNOTHREAD, __WCLONE and __WALL when emulating waits | wait_options_emulated_stop (deterministic; master fails every part in 8 of 8 recordings) |
On master c145f2f; reviewed (6 rounds as part of the single synthetic-sigchld-wait-wakeup commit, which review asked to split into #74 to #76), tests also as uid 1000 and with --chaos. rr's emulated waits ignored __WNOTHREAD, __WCLONE and __WALL: a __WNOTHREAD wait returned other threads' children's and tracees' stops, clone children's stops reached waits without __WCLONE, and a waitpid() for a child returned a traced non-leader thread's ptrace stop. First of three stacked PRs; doesn't need the other two. Open PR |
| 75 | synthetic-sigchld-wait-wakeup |
305d51b | Interrupt every wait that an emulated stop satisfies | wait_for_child_stop_sigchld_pending (master: Timeout in record in all 4 variants) |
Stacked on #74. Reviewed with #74, tests also as uid 1000 and with --chaos. rr interrupted the wait of at most one thread for an emulated stop, and none for a child's stop while a tracee's SIGCHLD was owed, so other waits that the stop satisfies blocked forever. Open after #74; the draft has a placeholder for its PR number. Open PR |
| 76 | synthetic-sigchld-parent-thread |
a9f63c2 | Queue synthetic SIGCHLDs for the process like Linux | child_stop_sigchld_thread (master fails in all 4 variants; one part hangs) |
Stacked on #75. Reviewed with #74, tests also as uid 1000 and with --chaos; full suite on it merged with #30: only the known root failures. rr sent a synthetic SIGCHLD to the thread it woke up, where no other thread could take it, or else let the kernel try the main thread first. Now it is queued for the process with the ptracer thread or the child's parent thread tried first, as Linux does. Open after #75; the draft has placeholders for #74's and #75's PR numbers. Open PR |
| 78 | refuse-uprobes |
8bacc81 | Refuse to create uprobes in recorded processes | uprobe_create (master fails all 4 variants) |
Stacked on rr-debugger#4140 (it needs that PR to see creat() of tracefs files). Part 1 only, as you decided: perf uprobe PMU events, uprobe tracepoint events, writable opens of tracefs uprobe_events/dynamic_events and uprobe_multi/session BPF links now fail in recorded processes as on a kernel without the feature. On master a recorded program could create uprobes (a probe on a jne made replay fail with a ticks mismatch; a uprobe_multi link made rr assert). Uprobes created outside the recording, or an outside-defined uprobe event turned on through tracefs, aren't detected (the description says so). Reviewed, tests also as uid 1000 and with --chaos. Open PR |
| 79 | seccomp-veto-exit-replay |
7ea9643 | Keep the buffers in replay when a filter refuses exit/exit_group | seccomp_veto_exit (master fails both syscall-buffer variants every run) |
On master c145f2f; reviewed, tests also as uid 1000 and with --chaos. When a process's seccomp filter refused exit()/exit_group() and the process went on, replay had already destroyed its syscall buffer ("Replay got unrecorded signal SIGSEGV"). Split out of #82, which needs it. Open PR |
| 80 | seccomp-ip-high-bits |
269f506 | Compare the whole instruction pointer with seccomp call sites | x86/seccomp_ip_high_bits (master fails every run: wrong recording, ticks mismatch or hang) |
On master c145f2f; reviewed, tests also as uid 1000 and with --chaos. rr's seccomp call-site checks compared only the lower 32 bits of the instruction pointer: a syscall at e.g. 0x1_7000_0008 skipped rr's filter (unrecorded) or a process's own filter. Split out of #82, which needs it. Open PR |
| 81 | deferred-patch-changed-syscall |
30832ee | Don't retry a deferred syscall patch at the syscall instruction | x86/deferred_patch_changed_restart (master fails its 64-bit syscall-buffer variant every run) |
On master c145f2f; reviewed, tests also as uid 1000 and with --chaos. When an SA_RESTART handler changed an interrupted syscall's number at a mov $0xca,%eax; syscall site, rr patched the site with the task on the syscall instruction, so the new syscall silently returned its own number (recording and replay both "succeed"). Split out of #82, which extends the rule. Open PR |
| 82 | seccomp-stop-after-syscall-entry |
915fe0d | Handle syscall entry at the seccomp stop after PTRACE_SYSCALL | 7 new tests, including ptrace_skip_restarted_syscall and group_stop_restarted_syscall/_signalfd (on master 27 of the 32 new variants fail every run) |
Stacked on #79, #80 and #81; open after them; the draft has placeholders for their PR numbers. Replaces #64 and #17. Reviewed 4 rounds, tests also as uid 1000 and with --chaos; full suite: only known failures and load flakes. When an interrupted syscall restarted, rr handled its entry at the PTRACE_SYSCALL stop, before the process's own seccomp filters ran, leaving a seccomp stop ahead that rr misread. Seven failures on master ("Expected syscall-state" for refused syscalls, fork() assertion, refused execve() hangs, PTRACE_SYSEMU/skip by a tracer, group stops). rr now handles the entry at the seccomp stop, as on the PTRACE_CONT path. group_stop_restarted_signalfd fails 3 of 40 runs with --chaos (never without) from a separate race in signal_state_changed() that #99 fixes (with #99 merged: 240/240); better opened after #99. Open PR |
| 83 | wait-child-pid |
6283c1a | Return a child's pid for its stop in emulated waits | wait_sigstop_threads (its base fails 40/40) |
Stacked on #74. Piece of the old #14; reviewed, tests also as uid 1000 and with --chaos. waitpid()/wait4() returned a thread's tid instead of the child's pid for a multithreaded child's job-control stop. Open PR |
| 84 | wait-consume-ptrace-stop |
f6b0bb4 | Consume only the reported ptrace stop in emulated waits | ptrace_wait_one_stop (base 40/40) |
Stacked on #83. Piece of the old #14; reviewed, tests also as uid 1000 and with --chaos. A ptrace wait from another thread of the tracer consumed the stops of all the tracee's threads (master hangs), and a job-control wait took traced threads' ptrace stops. Open PR |
| 85 | waitid-ptrace-siginfo |
96bd42c | Report the tracee's tid and CLD_TRAPPED in emulated waitid() | ptrace_waitid_siginfo (base 40/40) |
Stacked on #83. Piece of the old #14; reviewed twice (its test is race-free: 1160/1160 native runs per bitness); tests also as uid 1000 and with --chaos. Emulated waitid() gave a traced thread's process pid as si_pid and CLD_STOPPED for a seized child's group stop. Open PR |
| 86 | wait-hide-traced-leader-stop |
3767a27 | Hide a child's job-control stop when we trace its leader | ptrace_leader_group_stop (base 40/40) |
Stacked on #74. Piece of the old #14; reviewed, tests also as uid 1000 and with --chaos. Linux hides the job-control stop of a child whose leader the waiter's process traces; rr reported it. Open PR |
| 87 | reparent-stop-sigchld |
f7c871f | Forget a reparented child's stop SIGCHLD | reparent_stop_sigchld (master 40/40) |
On master c145f2f, standalone. Piece of the old #14; reviewed, tests also as uid 1000 and with --chaos. A child reparented after a stop still owed its old parent a SIGCHLD, so the new parent's handler ran twice. Open PR |
| 88 | keep-gone-child-sigchld |
379972e | Keep a deleted child's SIGCHLD siginfo for its parent | sigchld_siginfo_after_reap (base 40/40) |
Stacked on #30. Piece of the old #14; reviewed, tests also as uid 1000 and with --chaos. When rr deleted a child that still owed its parent a SIGCHLD, the handler got rr's placeholder siginfo. Open PR |
| 89 | sigchld-tkill-carrier |
f58e468 | Ignore tkill()'s SIGCHLDs when forgetting owed SIGCHLDs | sigchld_sigwait_thread_pending (base 40/40) |
Stacked on #88 and #76 (through a merge commit of the two; becomes a plain rebase once they land). Piece of the old #14; re-reviewed after the fix round, tests also as uid 1000 and with --chaos. A SIGCHLD that pthread_kill() left pending on another thread kept stale owed SIGCHLDs alive forever. Open PR |
| 90 | sigchld-merged-drop |
a49dc1f | Forget SIGCHLDs that merged into one we didn't fill in | sigchld_merged_into_exit (base 40/40) |
Stacked on #89. Piece of the old #14; re-reviewed after the fix round, tests also as uid 1000 and with --chaos. A synthetic SIGCHLD that merged into one rr didn't fill in (e.g. a kernel exit SIGCHLD) stayed owed. Open PR |
| 91 | sigchld-merged-first |
7778f74 | Fill in a merged SIGCHLD from the first child | sigchld_merged_first_child (base 40/40) |
Stacked on #90 and #87 (through a merge commit; without #87 a reparented child's stale SIGCHLD could win the fill-in). Piece of the old #14; re-reviewed after the fix round, tests also as uid 1000 and with --chaos. A SIGCHLD covering several children got a later child's siginfo instead of the first one's, as Linux queues it. Open PR |
| 92 | end-killed-child-stop |
588f050 | End a killed child's emulated job-control stop | wait_killed_stopped_child (base 40/40) |
Stacked on #91; must stay last. Its test's last part had a race that could make it send SIGSTOP to an unrelated process (an uninitialized child pid); fixed in 7769b87, the forked child now passes its own pid. Piece of the old #14; reviewed three times; rr now drops a stopped child's emulated stop when SIGKILL is pending for its process (checked where the stop would be reported) or the killed thread hadn't started exiting, tests also as uid 1000 and with --chaos. After kill(child, SIGKILL), a WUNTRACED wait still returned the child's earlier job-control stop instead of its exit. Open PR |
| 93 | exec-new-address-space |
adcd317 | Switch to the new address space as soon as we see an exec | exec_rseq, exec_leader_exit_race, ptrace_exec_multithreaded (master fails all three in every variant) |
On master 16fa499; Part 1 of #2. Reworked on 2026-10-28 as you asked: the exec stop's switch to the new image (swapping in the thread that exec'd, then post_exec()) now happens in RecordSession::record_step() before will_schedule(), instead of a did_waitpid() hook; did_waitpid() and the Scheduler are as on master. Reviewed, tests also as uid 1000 and with --chaos. Root cause of the intermittent exec_from_other_thread-32 CI failure: after a non-leader thread exec'd, rr wrote the bound CPU through the old rseq address into the new image. Also fixes a ~Task() FATAL when the leader exits while another thread execs (new test) and an emulated ptracer's non-leader-then-leader exec. Open PR |
| 94 | execve-collected-exit-stop |
e31af29 | Run a task whose exit stop was already collected during an execve | exec_after_waking_readers (master hangs in record in every syscallbuf run) |
On master c145f2f, standalone (was #35, then part of #2); reviewed, tests also as uid 1000 and with --chaos. Recording hung when one of rr's internal waits had collected a thread's PTRACE_EVENT_EXIT stop while its thread group was in execve. Open PR |
| 95 | ptrace-syscall-stop-signal |
ca98159 | Send the signal a tracer passes when resuming from a syscall-stop | ptrace_syscall_stop_signal (master fails every run) |
On master c145f2f, standalone; found while working on #68, reviewed, tests also as uid 1000 and with --chaos. A signal a ptracer passed with PTRACE_CONT/SYSCALL/SINGLESTEP from a syscall-exit-stop was dropped (Linux delivers it). Open PR |
| 97 | restart-syscall-orig-syscallno |
232f12f | Let the kernel run restart_syscall for interrupted syscalls | restart_syscall_sleep, ptrace_restart_syscall (master fails every run) |
On master c145f2f, standalone; reviewed, tests also as uid 1000 and with --chaos (all runs in private PID namespaces). rr ran an interrupted sleep's restart itself instead of letting the kernel run restart_syscall: a cancelled restart returned 0 instead of EINTR, the remaining time was overwritten, and emulated ptracers saw the wrong syscall at the restart. Open PR |
| 99 | restart-timeslice-signal |
f125b47 | Set up syscall restarts at signal delivery ourselves on x86 | ignored_signal_restart_race, x86/handled_signal_restart_race (master fails 156/160 and 55/80 recordings) |
On master c145f2f, standalone; reviewed, tests also as uid 1000 and with --chaos (all runs in private PID namespaces). When a signal (e.g. rr's time-slice signal) arrived as rr restarted a syscall, rr recorded a signal delivery with faked post-restart registers and then a SCHED with the old ones, so replay failed. Also fixes #82's rare --chaos failure. #32 is stacked on it. Open PR |
| 100 | killed-in-prepare-clone |
c0fc523 | Test a task killed while rr prepares its clone(), fork() or vfork() | clone_killed_during_preparation (new kill point RRCALL_KILL_IN_CLONE_PREPARATION; master fails 40/40 as root, 20/20 as uid 1000, 20/20 with --chaos, all in recording) |
Stacked on #69, whose code is the fix: a task SIGKILLed while rr resumes it to its clone()/fork()/vfork() event was resumed out of its PTRACE_EVENT_EXIT stop (is_stopped_ assertion; one way pid_ns_kill_child_threads fails now and then). This adds the deterministic test #69 lacked. Reviewed; tests also as uid 1000 and with --chaos. Your call: fold it into #69 before opening (recommended; then #44 needs a rebase through a small, already-tested conflict), or open it stacked on #69. Open PR |
| 101 | ptrace-syscall-restart-cancelled |
ae09e54 | Forget a syscall restart that the tracer cancels | see its description | Stacked on #16. Split out of #18; rebased onto master c145f2f and reviewed on 2026-10-27, tests also as uid 1000 and with --chaos. A tracer that cancels a syscall restart at the syscall's exit (gdb writes -1 to orig_rax whenever it sets the pc) still got the restart under rr. Open PR |
| 102 | ptrace-group-stop-real-parent |
75b08fa | Let the real parent wait for a traced child's group-stop | ptrace_group_stop_real_parent |
Stacked on #68 and #15's first commit (merge). Split out of #26; rebased onto master c145f2f and reviewed on 2026-10-27, tests also as uid 1000 and with --chaos. Open PR |
| 103 | ptrace-detach-group-stop |
7ba1924 | Stop a tracee again when its ptracer detaches or exits during a group-stop | see #26's description |
Stacked on #102. Split out of #26; rebased onto master c145f2f and reviewed on 2026-10-27, tests also as uid 1000 and with --chaos. Open PR |
| 104 | traced-syscall-efault |
9c0be54 | Make traced syscalls on inaccessible memory fail with EFAULT | see its description | On master c145f2f (2 commits: an aarch64 address-untagging fix it needs, and the fix). Split out of #38; rebased onto master c145f2f and reviewed on 2026-10-27, tests also as uid 1000 and with --chaos. A blocking read() into a read-only buffer succeeded under rr (rr copies results back with ptrace, ignoring page protection) and consumed the data; natively it fails with EFAULT. Open PR |
| 105 | syscall-args-efault |
84f6984 | Don't assert when a syscall's memory arguments can't be read | see its description | Stacked on #38. Split out of #38; rebased onto master c145f2f and reviewed on 2026-10-27, tests also as uid 1000 and with --chaos. rr asserted instead of returning EFAULT when it couldn't read a traced syscall's memory arguments. Open PR |
| 106 | emergency-debugger-auxv |
ec2936d | Save the auxv at exec during recording too, as during replay | see its description | Stacked on #55. Split out of #56; rebased onto master c145f2f and reviewed on 2026-10-27, tests also as uid 1000 and with --chaos. rr's emergency debugger couldn't give gdb the auxv during recording, so every backtrace frame was ?? (). Open PR |
| 107 | wait-keep-real-result |
e477e86 | Don't replace a wait's real result with an emulated stop | see its description | On master c145f2f, standalone. Split out of #25; rebased onto master c145f2f and reviewed on 2026-10-27, tests also as uid 1000 and with --chaos. A wait that reported a real child (e.g. a reaped exit) could be overwritten by an emulated stop, losing the exit status. Open PR |
| 108 | sigchld-nocldstop |
0eadfa8 | Honor SIG_IGN and SA_NOCLDSTOP for the SIGCHLDs of emulated stops | sigchld_nocldstop |
Stacked on #25. Split out of #25; rebased onto master c145f2f and reviewed on 2026-10-27, tests also as uid 1000 and with --chaos. rr sent its synthetic SIGCHLD for emulated stops even when the parent ignores SIGCHLD or uses SA_NOCLDSTOP. Open PR |
| 109 | int80-restart |
d201664 | Restart int $0x80 syscalls of x86-64 tasks as i386 syscalls | x86/int80_restart |
Stacked on #9. reviewed (int80 series split on 2026-10-28), tests also as uid 1000 and with --chaos. A signal interrupting an x86-64 task's int $0x80 syscall broke its restart (e.g. a restarted futex() wait failed with ENOSYS). Conflicts with #99 in maybe_restart_syscall() (resolution tested). Open PR |
| 110 | int80-seccomp |
940b41b | Handle seccomp filters of int $0x80 syscalls of x86-64 tasks | see its description | Stacked on #9. reviewed (int80 series split on 2026-10-28), tests also as uid 1000 and with --chaos. seccomp SIGSYS for an int $0x80 syscall had si_arch AUDIT_ARCH_X86_64 (the kernel reports AUDIT_ARCH_I386), and related filter handling used the task's arch. Open PR |
| 111 | int80-ptrace-syscall-info |
9fdcfcd | Report int $0x80 syscalls of x86-64 tracees in PTRACE_GET_SYSCALL_INFO | x86/int80_ptrace_syscall_info |
Stacked on #9. reviewed (int80 series split on 2026-10-28), tests also as uid 1000 and with --chaos. rr's emulated PTRACE_GET_SYSCALL_INFO reported AUDIT_ARCH_X86_64 and the x86-64 argument registers for an int $0x80 syscall. Open PR |
| 112 | int80-robust-list |
57fa80f | Track the i386 robust futex lists of x86-64 tasks | x86/int80_robust_list |
Stacked on #9. reviewed (int80 series split on 2026-10-28), tests also as uid 1000 and with --chaos. rr treated an x86-64 task's i386 robust list (set with int $0x80) as its only one. Open PR |
| 113 | int80-replay-restart |
7e8d1b7 | Restart int $0x80 syscalls with i386 restart_syscall during replay | x86/int80_restart_replay |
Stacked on #9. reviewed (int80 series split on 2026-10-28), tests also as uid 1000 and with --chaos. Replay restarted an interrupted int $0x80 syscall with x86-64's restart_syscall number. Open PR |
| 114 | int80-keep-r8-r11 |
8a7352f | Keep r8-r11 at int $0x80 syscalls of x86-64 tasks | x86/int80_regs |
On master c145f2f, standalone; reviewed (int80 series split on 2026-10-28), tests also as uid 1000 and with --chaos. rr cleared r8-r11 at int $0x80 syscalls as Linux did before 4.18. Open PR |
| 115 | int80-sigaction |
c87649d | Read the struct of an int $0x80 rt_sigaction() with the i386 layout | x86/int80_sigaction |
Stacked on #9 and #118 (it carries a copy of #118's commit, dropped on rebase once that lands). reviewed (int80 series split on 2026-10-28), tests also as uid 1000 and with --chaos. rr read an int $0x80 rt_sigaction()'s 20-byte i386 struct as a 32-byte x86-64 one. Open PR |
| 116 | int80-remote-syscall-at-seccomp-stop |
c31ac6d | Leave an int $0x80 syscall's seccomp stop before rr's own syscalls | see its description | Stacked on #9 and #119 (carries a copy of #119's commit). New, found in review; reviewed (int80 series split on 2026-10-28), tests also as uid 1000 and with --chaos. rr's remote syscalls at the seccomp stop of an int $0x80 syscall ran as i386 syscalls. Open PR |
| 117 | robust-futex-clone |
83024ba | Don't pass robust futex lists on to new tasks | robust_futex_clone |
On master c145f2f, standalone; reviewed (int80 series split on 2026-10-28), tests also as uid 1000 and with --chaos. rr gave a new task its creator's robust futex list and marked futexes OWNER_DIED at the new task's exit. Open PR |
| 118 | i386-old-sigaction |
9ab9aee | Read the struct of the i386 sigaction() as a struct old_sigaction | old_sigaction |
On master c145f2f, standalone (new, split out); reviewed (int80 series split on 2026-10-28), tests also as uid 1000 and with --chaos. rr read the old i386 sigaction()'s 16-byte struct as rt_sigaction()'s 20-byte one (32-bit programs, not only int $0x80). Open PR |
| 119 | proc-mem-write-once |
05d746e | Don't let the kernel repeat a /proc//mem write that rr emulated | ptrace_proc_mem_write |
On master c145f2f, standalone (new, found in review; also affects plain x86-64); reviewed (int80 series split on 2026-10-28), tests also as uid 1000 and with --chaos. After rr emulated a tracer's write to its tracee's /proc//mem, the kernel could perform the write again. Open PR |
| 120 | robust-futex-list-limit |
9ddc460 | Stop walking a robust futex list after ROBUST_LIST_LIMIT entries | robust_futex_cycle |
On master c145f2f, standalone (new, split out of #117); reviewed (int80 series split on 2026-10-28), tests also as uid 1000 and with --chaos. rr walked a robust futex list with a cycle that doesn't lead back to its head forever (the kernel stops after 2048 entries). Open PR |
| 121 | shared-mapping-truncate |
6372203 | Replay truncation of files that are mapped shared | mmap_shared_truncate (master fails all 4 variants every run) |
On master c145f2f, standalone (a bug master has, found during #77's review); reviewed, tests also as uid 1000 and with --chaos. Truncating a file that is mapped MAP_SHARED (O_TRUNC open, ftruncate, truncate) and growing it again: the mapping reads zeroes natively, but replay kept the old bytes and diverged. rr now records the truncated part of each shared mapping as zeroes; the syscall buffer no longer handles truncate() and O_TRUNC opens itself. A creat() made with -n also needs rr-debugger#4140. Not covered (in its Notes): growth by write() past the old end, fallocate hole punching. Open PR |
| 123 | traced-zombie-leader-stop |
3b5a113 | Don't emulate group stops of exiting tracees | ptrace_zombie_leader_group_stop (fails 40/40 on master) |
On master c145f2f (merges cleanly with 16fa499), standalone (a bug master has, found during the review of the old #14); reviewed, tests also as uid 1000 and with --chaos. When a process stopped while its traced leader was a zombie, the tracer's wait reported a group stop of the leader (also after PTRACE_INTERRUPT of a zombie tracee); Linux doesn't stop exiting tasks. Independent of #86; cherry-picks cleanly onto it. A tracer in a sibling process still sees different results from Linux, with and without this (in its Notes). Open PR |
| 124 | ptrace-vfork-done-exit |
a799fc7 | Let the resume from PTRACE_EVENT_VFORK_DONE decide the vfork's exit | vfork_done_syscall_exit (fails every run on #29) |
Stacked on #29. New (found reviewing #125); reviewed, tests also as uid 1000 and with --chaos. rr applied the tracer's resume command from before PTRACE_EVENT_VFORK_DONE to the vfork's exit: with PTRACE_SYSCALL or a single-step before that stop, recording failed emulated_stop_type == NOT_STOPPED; with PTRACE_CONT before and PTRACE_SYSCALL or a single-step from it, the tracer got no exit stop or SIGTRAP. Linux reports VFORK_DONE inside the syscall, so the command that resumes it decides. It applies cleanly to #16 alone (not built there), so it could be restacked on #16 if #29 lags. Open PR |
| 125 | ptrace-syscall-group-stop |
e0d3b41 | Report a syscall's exit stop after a group stop in the syscall | ptrace_syscall_group_stop, x86/ptrace_singlestep_group_stop (fail every run on #124) |
Stacked on #124 (and so #29). A bug master has, found during the review of #29; reviewed, tests also as uid 1000 and with --chaos. A tracer following a thread with PTRACE_SYSCALL or single-steps, while the thread is blocked in a traced syscall when its process group-stops, crashed recording (emulate_ptrace_stop NOT_STOPPED assertion; master also at the scheduler's GROUP_STOP assertion for an attached tracee). rr still reports the group stop before the syscall-exit stop (Linux: exit stop first); the description says so. Open PR |
Suggested order: 93, 94, 67, 68, 95, 50, 73, 104, 107, 55, 114, 117, 118, 119, 120, 121, 123, 9, 11, 12, 13, 19, 28, 29, 30, 74, 79, 80, 81, 87, 97, 99, 31, 36, 59, then the stacked ones once their base has merged: 2 after 93, 94, 67 and 68; 7 after 50 (and only if still wanted after 82); 109, 110, 111, 112, 113 after 9; 115 after 9 and 118; 116 after 9 and 119; 10 after 115 (if at all); 20 after 12 and 73; 75 after 74, 76 after 75; 78 after rr-debugger#4140; 82 after 79, 80, 81 (and preferably 99); 32 after 99; 100 after 69 (or folded into it); 83 and 86 after 74, 84 and 85 after 83; 88 after 30; 89 after 88 and 76; 90 after 89, 91 after 90 and 87, 92 after 91; 15, then 16, then 101 and 29; 124 after 29, 125 after 124; 18 after 101 and 82; 106 then 56 after 55; 38 then 105 after 104; 102 then 103 then 26 after 68 and 15; 25 then 108 after 15; 27 after 26; 40 and 72 now (after a rebase; rr-debugger#4137 has merged); 44 with 69; 47 after 40; 56 after 55; 61 goes into rr-debugger#4104, 62 into it or right after it; 63 once it has a test. Everything else is independent.
Already open upstream
- Handle waitid(P_PGID, 0) as a wait for the caller's process group rr-debugger/rr#4143
waitid-pgid-pid(was#122), opened 2026-10-28; CI green. - Handle files opened by creat() like those opened by open() rr-debugger/rr#4140
creat-like-open(was#77), futex_restart_clone: have the child publish its pid rr-debugger/rr#4141futex-restart-clone-child-pid(was#96) and Copy back the remaining time of interrupted waits as the kernel does rr-debugger/rr#4142interrupted-wait-remaining-time(was#98), opened 2026-10-27/28. futex_restart_clone: have the child publish its pid rr-debugger/rr#4141's CI didn't run: "Setup runners" failed with "Insufficient AWS EC2 instance capacity" (needs a re-run). Copy back the remaining time of interrupted waits as the kernel does rr-debugger/rr#4142's branch was force-pushed on 2026-10-28 (dbd40b9 -> ff93f50) so its test uses atomics for the flags its threads share; the commit message and the fix are unchanged.
Merged upstream
- Don't record scratch memory for a task killed before we map it rr-debugger/rr#4137
killed-before-scratch-mmap(was#37; merged 2026-10-28 as 46f1dd9), Don't replay a clone() whose task was killed at its entry rr-debugger/rr#4138killed-at-clone-entry(was#70; merged 2026-10-28 as 1005e4a) and Don't resume a task killed while we prepare its exit() rr-debugger/rr#4139killed-in-exit-setup(was#71; merged 2026-10-28 as 16fa499). They replaced Cope with tasks killed while we set up a clone(), exec or exit() rr-debugger/rr#4136; its other pieces (#69with#44,#72,#40,#47) are still to open. - End a single-step that delivers a signal at the handler's entry rr-debugger/rr#4133
ptrace-singlestep-into-signal-handler(was#21on this list; merged 2026-10-22 as 1e42eed; its 32-bit test failed in CI as a non-root user, which roc fixed in rr's siginfo conversion, c145f2f) - Don't record where a task killed before its first exec was rr-debugger/rr#4135
killed-before-initial-exec(was#45on this list; merged 2026-10-22 as 9eb1bbd) - Don't resume a task killed while we patch its syscall rr-debugger/rr#4134
killed-while-patching-syscall(was#42on this list; merged 2026-10-22 as a1fdd43) - Don't allow switching before recording a negative syscall's entry rr-debugger/rr#4132
negative-syscall-switch(was#66on this list; merged 2026-10-22 as 0f4d976) - Don't copy more of a recvmsg() sender address than msg_namelen allows rr-debugger/rr#4110
recvmsg-short-namelen(was#24on this list; merged 2026-10-22 as 4655437) - Handle a killed task's exit before other tasks' events rr-debugger/rr#4129
killed-task-exit-first(was#41on this list) - ptrace_attach_thread_running: wait for the threads instead of sleeping rr-debugger/rr#4130
ptrace-attach-thread-running-wait-for-threads(was#54on this list) - Don't hang tests when rr fails in its emergency debugger rr-debugger/rr#4131
emergency-debugger-hang(was#57on this list) - Preserve fd flags in Task::dup_from rr-debugger/rr#4102
fix-dup-from-cloexec - Restart interrupted syscalls when replay resumes from a syscall stop rr-debugger/rr#4103
replay-restart-without-breakpoint - Return 0 from a WNOHANG wait for a ptracee that is not a child rr-debugger/rr#4105
ptrace-wnohang-wait-for-non-child - mount_ns_exec2: read /proc/self/exe before unsharing the mount namespace rr-debugger/rr#4106
mount-ns-exec2-unreachable-exe - Defer debugger interrupts that arrive while the debuggee is stopped rr-debugger/rr#4107
gdbserver-exited-state-requests - Give a task cloned while its parent has stashed signals the right mask rr-debugger/rr#4108
clone-child-sigmask - Restore all of the stack that Task::os_exec writes rr-debugger/rr#4109
os-exec-restore-args - Don't resume a task from an unprocessed stop when emulating SIGCONT rr-debugger/rr#4112
sigcont-unprocessed-stop - ptrace emulation: Match Linux behavior for PTRACE_GET_SYSCALL_INFO at other stops rr-debugger/rr#4113
ptrace-get-syscall-info-none - Checksum a task's own thread-locals rr-debugger/rr#4115
checksum-own-thread-locals - Record the frame record above an aarch64 signal frame rr-debugger/rr#4116
aarch64-sigframe-frame-record - End an emulated group stop when a tracee sends SIGCONT rr-debugger/rr#4117
sigcont-discarded-by-sigstop(was#5on this list) - Fix accidental negative random numbers in chaos mode rr-debugger/rr#4118
chaos-mode-traced-syscall-count(was#49on this list) - Record events at an interrupted aarch64 syscall's exit as restarted rr-debugger/rr#4119
aarch64-sched-at-interrupted-syscall-exit - Add a mechanism to test SIGKILL at various inconvenient places rr-debugger/rr#4114
test-kill-rrcall(the kill rrcall; the rows that use it can now be rebased onto master) - Remove only the syscallbuf breakpoints a task set when it stops rr-debugger/rr#4120
syscallbuf-breakpoint-refcount(was#58on this list) - Restore registers after getting back to a seccomp stop rr-debugger/rr#4121
seccomp-stop-remote-syscall-regs(was#23on this list) - futex_requeue: wait for the threads instead of sleeping rr-debugger/rr#4123
futex-requeue-test-wait-for-waiters(was#53on this list) - Replay frames whose extra registers weren't recorded rr-debugger/rr#4128
exec-frame-no-extra-regs(was#46on this list; merged 2026-10-20 as e872d9c) - Don't resume a tracee in prepare_ptrace_cont unless rr holds it rr-debugger/rr#4122
ptrace-cont-running-tracee(was#6on this list; merged 2026-10-16 as 7fd71b7 and e42b9b0; rows 15-18 and 21 stacked on it can now be rebased onto master)
Notes
-
The kill-race fixes on this list (Don't record scratch memory for a task killed before we map it rr-debugger/rr#4137 and rows
#39,#40,#44,#47and#69to#73) test with the kill rrcall of Add a mechanism to test SIGKILL at various inconvenient places rr-debugger/rr#4114, which has merged. Rows Don't record scratch memory for a task killed before we map it rr-debugger/rr#4137 to Don't resume a task killed while we prepare its exit() rr-debugger/rr#4139 (merged),#69and#72are the pieces of Cope with tasks killed while we set up a clone(), exec or exit() rr-debugger/rr#4136;#40and#72were stacked on Don't record scratch memory for a task killed before we map it rr-debugger/rr#4137 and need a rebase,#47is on#40, and#44on#69. -
roc's cf60fc6 (Replaying a Wine recording triggers
Syscall mmap2 failed with errno EEXISTrr-debugger/rr#4124) unmaps an exec'd vfork child's syscallbuf and scratch in replay, which is what#36's old second commit did;#36now has only what cf60fc6 doesn't cover. -
#16also fixes a rare unreplayable recording on master: with--chaos,ptracer_death_interrupt-32-no-syscallbufrecords a syscall-exit stop at aread()that hasn't run yet (3 of 600 runs fail to replay on master, 0 of 600 with#16). -
#25and#26conflict with#74to#76in the wait emulation and SIGCHLD code; they are on an older master and will be rebased. -
#25,#26and#30conflict textually (SIGCHLD notification,set_siginfo_for_synthetic_SIGCHLDandemulate_SIGCONT); whichever is opened later will be rebased.#25also conflicts with End an emulated group stop when a tracee sends SIGCONT rr-debugger/rr#4117, which has merged, so it needs a rebase onto master. -
Several of these touch
RecordTask::emulate_SIGCONTand the emulated-ptrace paths. Later branches will be rebased onto whatever lands first. -
#2's new frame events are namedEV_PTRACE_EVENT_STOP/EV_PTRACE_EVENT_STOP_CONCLUDED, the same spelling as the kernel'sPTRACE_EVENT_STOP(group-stop/PTRACE_INTERRUPTtrap) that rr already uses elsewhere; review suggests e.g.EV_PTRACE_EXEC_STOP/EV_PTRACE_EXEC_STOP_ENDbefore it goes upstream. -
Follow-up for
#28/#31: rr's emulated single-step trap after a syscall (maybe_trigger_emulated_ptrace_syscall_exit_stop()) reportsSI_KERNELwithsi_addr0; natively x86 givesTRAP_BRKPTwithsi_addr= ip and aarch64 givesSI_USER.#2uses the same function for the trap after its exec stop. -
#3(timer-signal-starvation) is dropped: only a synthetic stress program with sub-millisecond interval timers needed it, which doesn't justify the complexity. The stress program backs its timers off instead. -
#64(group-stop-pending-seccomp-stop) and#17(sysemu-restarted-syscall) were replaced by#82, which fixes their bugs at the cause (their tests pass on it). Their branches stay on the fork for reference. -
#14(wait-stopped-child-pid) was split into#83to#92on top of#30and#74to#76, which already cover one of its fixes. Its branch stays on the fork. -
On 2026-10-27 a test on one of these branches (
#92's, before its fix) called kill() with an uninitialized pid and stopped an unrelated process on the machine running it. All tests on these branches were then audited: every fork() result is now checked and every pid passed between threads or processes is asserted before it is signaled (#25,#29,#75,#76,#82,#85,#87,#89to#92got test-only changes; futex_restart_clone: have the child publish its pid rr-debugger/rr#4141 fixes the same kind of race in an upstream test). -
This issue will be updated as more branches become ready.
Activity
Don't unmap local memory below a mapping that is partly unmapped
Don't munmap rr's memory below a local mapping when the unmapped rang…