x86/arm64: allow partial PTRACE_GETREGSET NT_PRSTATUS reads#12899
x86/arm64: allow partial PTRACE_GETREGSET NT_PRSTATUS reads#12899ivg wants to merge 1 commit intogoogle:masterfrom
Conversation
|
Thanks for your pull request! It looks like this may be your first contribution to a Google open source project. Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA). View this failed invocation of the CLA check for more information. For the most up to date status, view the checks section at the bottom of the pull request. |
EtiennePerot
left a comment
There was a problem hiding this comment.
Can you add a syscall test in test/syscalls to exercise this and ensure Linux behaves the same way?
|
See test/syscalls/linux/ptrace.cc |
c772307 to
663db51
Compare
The test is added, and here's the permalink to the kernel implementation: The kernel clamps iov_len down to the actual register set size, then proceeds with copy_regset_to_user — no size check, no EFAULT. |
663db51 to
7eead62
Compare
|
The newly added test is failing: https://buildkite.com/gvisor/pipeline/builds/41656 |
The Linux kernel's ptrace_regset() (kernel/ptrace.c) writes min(iov_len, actual_size) bytes and returns success for any non-zero buffer; it never rejects a PTRACE_GETREGSET request solely because the buffer is smaller than the full register set.
gVisor's PtraceGetRegSet previously returned EFAULT when maxlen was less than ptraceRegistersSize (216 bytes for x86_64). This broke any tool that passes a smaller buffer, most notably libunwind whose _UPT_access_reg() allocates gregset_t (184 bytes) rather than struct user_regs_struct (216 bytes) for the PTRACE_GETREGSET call. The result was that unw_init_remote() failed with UNW_EBADREG ("bad register number"), making ptrace-based stack unwinding completely non-functional inside gVisor containers. The corresponding fix to libunwind: libunwind/libunwind#977
Fix: when maxlen is in the range (0, ptraceRegistersSize), serialize the full register set to a temporary buffer and write only maxlen bytes to dst, matching the kernel's truncation semantics. The fast path (maxlen
>= ptraceRegistersSize) is unchanged.
FUTURE_COPYBARA_INTEGRATE_REVIEW=#12899 from ivg:fix-ptrace-getregset-partial-read 7eead62
PiperOrigin-RevId: 900253310
The Linux kernel's ptrace_regset() (kernel/ptrace.c) writes min(iov_len, actual_size) bytes and returns success for any non-zero buffer; it never rejects a PTRACE_GETREGSET request solely because the buffer is smaller than the full register set.
gVisor's PtraceGetRegSet previously returned EFAULT when maxlen was less than ptraceRegistersSize (216 bytes for x86_64). This broke any tool that passes a smaller buffer, most notably libunwind whose _UPT_access_reg() allocates gregset_t (184 bytes) rather than struct user_regs_struct (216 bytes) for the PTRACE_GETREGSET call. The result was that unw_init_remote() failed with UNW_EBADREG ("bad register number"), making ptrace-based stack unwinding completely non-functional inside gVisor containers. The corresponding fix to libunwind: libunwind/libunwind#977
Fix: when maxlen is in the range (0, ptraceRegistersSize), serialize the full register set to a temporary buffer and write only maxlen bytes to dst, matching the kernel's truncation semantics. The fast path (maxlen
>= ptraceRegistersSize) is unchanged.
FUTURE_COPYBARA_INTEGRATE_REVIEW=#12899 from ivg:fix-ptrace-getregset-partial-read 7eead62
PiperOrigin-RevId: 900253310
7eead62 to
75f9fd4
Compare
|
Addressed, the implementation is now much cleaner and the tests pass locally on my machine. |
The Linux kernel's ptrace_regset() (kernel/ptrace.c) writes min(iov_len, actual_size) bytes and returns success for any non-zero buffer; it never rejects a PTRACE_GETREGSET request solely because the buffer is smaller than the full register set.
gVisor's PtraceGetRegSet previously returned EFAULT when maxlen was less than ptraceRegistersSize (216 bytes for x86_64). This broke any tool that passes a smaller buffer, most notably libunwind whose _UPT_access_reg() allocates gregset_t (184 bytes) rather than struct user_regs_struct (216 bytes) for the PTRACE_GETREGSET call. The result was that unw_init_remote() failed with UNW_EBADREG ("bad register number"), making ptrace-based stack unwinding completely non-functional inside gVisor containers. The corresponding fix to libunwind: libunwind/libunwind#977
Fix: when maxlen is in the range (0, ptraceRegistersSize), serialize the full register set to a temporary buffer and write only maxlen bytes to dst, matching the kernel's truncation semantics. The fast path (maxlen
>= ptraceRegistersSize) is unchanged.
FUTURE_COPYBARA_INTEGRATE_REVIEW=#12899 from ivg:fix-ptrace-getregset-partial-read 75f9fd4
PiperOrigin-RevId: 900253310
The Linux kernel's ptrace_regset() (kernel/ptrace.c) writes min(iov_len, actual_size) bytes and returns success for any non-zero buffer; it never rejects a PTRACE_GETREGSET request solely because the buffer is smaller than the full register set.
gVisor's PtraceGetRegSet previously returned EFAULT when maxlen was less than ptraceRegistersSize (216 bytes for x86_64). This broke any tool that passes a smaller buffer, most notably libunwind whose _UPT_access_reg() allocates gregset_t (184 bytes) rather than struct user_regs_struct (216 bytes) for the PTRACE_GETREGSET call. The result was that unw_init_remote() failed with UNW_EBADREG ("bad register number"), making ptrace-based stack unwinding completely non-functional inside gVisor containers. The corresponding fix to libunwind: libunwind/libunwind#977
Fix: when maxlen is in the range (0, ptraceRegistersSize), serialize the full register set to a temporary buffer and write only maxlen bytes to dst, matching the kernel's truncation semantics. The fast path (maxlen
>= ptraceRegistersSize) is unchanged.
FUTURE_COPYBARA_INTEGRATE_REVIEW=#12899 from ivg:fix-ptrace-getregset-partial-read 75f9fd4
PiperOrigin-RevId: 900253310
The Linux kernel's ptrace_regset() (kernel/ptrace.c) writes min(iov_len, actual_size) bytes and returns success for any non-zero buffer; it never rejects a PTRACE_GETREGSET request solely because the buffer is smaller than the full register set.
gVisor's PtraceGetRegSet previously returned EFAULT when maxlen was less than ptraceRegistersSize (216 bytes for x86_64). This broke any tool that passes a smaller buffer, most notably libunwind whose _UPT_access_reg() allocates gregset_t (184 bytes) rather than struct user_regs_struct (216 bytes) for the PTRACE_GETREGSET call. The result was that unw_init_remote() failed with UNW_EBADREG ("bad register number"), making ptrace-based stack unwinding completely non-functional inside gVisor containers. The corresponding fix to libunwind: libunwind/libunwind#977
Fix: when maxlen is in the range (0, ptraceRegistersSize), serialize the full register set to a temporary buffer and write only maxlen bytes to dst, matching the kernel's truncation semantics. The fast path (maxlen
>= ptraceRegistersSize) is unchanged.
FUTURE_COPYBARA_INTEGRATE_REVIEW=#12899 from ivg:fix-ptrace-getregset-partial-read 75f9fd4
PiperOrigin-RevId: 900253310
|
Could you describe how you are running the test locally? It is still failing: https://buildkite.com/gvisor/pipeline/builds/41675 |
|
This is what I do: So it is run on native linux, I can't rebuild gvisor locally. It looks like it is failing on ARM64, which I didn't fix (and didn't even fix about it, sorry). Let me address it. |
The Linux kernel's ptrace_regset() (kernel/ptrace.c) writes min(iov_len, actual_size) bytes and returns success for any non-zero buffer; it never rejects a PTRACE_GETREGSET request solely because the buffer is smaller than the full register set. gVisor's PtraceGetRegSet previously returned EFAULT when maxlen was less than ptraceRegistersSize on both x86 and arm64. This broke any tool that passes a smaller buffer, most notably libunwind which passes sizeof(gregset_t) = 184 bytes rather than sizeof(user_regs_struct) = 216 bytes for the PTRACE_GETREGSET call, causing unw_init_remote() to fail with UNW_EBADREG and making ptrace-based stack unwinding non-functional inside gVisor containers. Fix PtraceGetRegSet on x86 and arm64 to write min(maxlen, ptraceRegistersSize) bytes when maxlen is positive but smaller than the full register set, matching Linux kernel semantics. Add a syscall test that exercises the partial read path and verifies the bytes written match the corresponding bytes from a full register read. Signed-off-by: Ivan Gotovchits <ivg@ieee.org>
75f9fd4 to
c071889
Compare
|
updated the arm64 achitecture as well (and updated the title). |
The Linux kernel's ptrace_regset() (kernel/ptrace.c) writes min(iov_len, actual_size) bytes and returns success for any non-zero buffer; it never rejects a PTRACE_GETREGSET request solely because the buffer is smaller than the full register set.
gVisor's PtraceGetRegSet previously returned EFAULT when maxlen was less than ptraceRegistersSize (216 bytes for x86_64). This broke any tool that passes a smaller buffer, most notably libunwind whose _UPT_access_reg() allocates gregset_t (184 bytes) rather than struct user_regs_struct (216 bytes) for the PTRACE_GETREGSET call. The result was that unw_init_remote() failed with UNW_EBADREG ("bad register number"), making ptrace-based stack unwinding completely non-functional inside gVisor containers. The corresponding fix to libunwind: libunwind/libunwind#977
Fix: when maxlen is in the range (0, ptraceRegistersSize), serialize the full register set to a temporary buffer and write only maxlen bytes to dst, matching the kernel's truncation semantics. The fast path (maxlen