Bug 437712

Summary: ptrace: PTRACE_SETREGS does not set RIP
Product: Red Hat Enterprise Linux 4 Reporter: Jan Kratochvil <jan.kratochvil>
Component: kernelAssignee: Red Hat Kernel Manager <kernel-mgr>
Status: CLOSED NOTABUG QA Contact: Martin Jenner <mjenner>
Severity: medium Docs Contact:
Priority: medium    
Version: 4.6CC: kernel-mgr, roland
Target Milestone: rc   
Target Release: ---   
Hardware: x86_64   
OS: Linux   
Whiteboard:
Fixed In Version: Doc Type: Bug Fix
Doc Text:
Story Points: ---
Clone Of: Environment:
Last Closed: 2008-03-17 14:09:04 UTC Type: ---
Regression: --- Mount Type: ---
Documentation: --- CRM:
Verified Versions: Category: ---
oVirt Team: --- RHEL 7.3 requirements from Atomic Host:
Cloudforms Team: --- Target Upstream Version:
Embargoed:

Description Jan Kratochvil 2008-03-16 22:13:42 UTC
Description of problem:
PTRACE_SETREGS(rip=XYZ) followed by PTRACE_GETREGS returns the original RIP value.

Version-Release number of selected component (if applicable):
kernel-smp-2.6.9-68.10.EL.x86_64
(upstream 2.6.22-rc4-git7.x86_64 is OK)
(RHEL-5 kernel-2.6.18-53.1.13.el5.x86_64 is OK)

How reproducible:
Always.

Steps to Reproduce:
1. wget -O user-regs-peekpoke.c
'http://sources.redhat.com/cgi-bin/cvsweb.cgi/~checkout~/tests/ptrace-tests/tests/user-regs-peekpoke.c?cvsroot=systemtap'
2. gcc -o user-regs-peekpoke user-regs-peekpoke.c -Wall -ggdb2 -D_GNU_SOURCE
3. ./user-regs-peekpoke; echo $?

Actual results:
1

Expected results:
0

Additional info:
Register RIP remains unmodified.
wdiff:
$x = {r15 = 506097522914230528, r14 = 1084818905618843912, r13 =
1663540288323457296, r12 = 2242261671028070680,
  rbp = 2820983053732684064, rbx = 3399704436437297448, r11 =
3978425819141910832, r10 = 4557147201846524216,
  r9 = 5135868584551137600, r8 = 5714589967255750984, rax = 6293311349960364368,
rcx = 6872032732664977752,
  rdx = 7450754115369591136, rsi = 8029475498074204520, rdi =
8608196880778817904, orig_rax = 9186918263483431288,
  rip = [-9765639646188044672,-] {+248369046109,+} cs = 51, eflags = 514, rsp =
11501803794301884824, ss = 43, fs_base = 182894074624, gs_base = 0,
  ds = 0, es = 0, fs = 0, gs = 0}

It aborts at the source line 146 (of rev 1.2).

It breaks many other ptrace-testsuite testcases (step-jump-cont, erestart*).

Comment 3 Roland McGrath 2008-03-16 22:52:44 UTC
If I'm reading that wdiff fragment correctly, the old rip value was 0x39d3f2e25d
and you tried to set rip to 0x8786858483828180.  Is that correct?

User addresses on x86_64 cannot be above 0x7fffffffefff on recent kernels, or
above 0x7fbfffffff of RHEL4 kernels.

On most kernels, you can set any rip value you like via ptrace.  For any invalid
value (even the high ones that are permanently invalid), the thread will just
get the normal signal when it resumes at the bad rip.

On RHEL4, the fix for CAN-2005-1762 was kept conservative by making ptrace
disallow setting an rip value that is too high to ever be a valid user address
on that kernel.  On upstream kernels, the issue was fixed a different way that
was more invasive to deep kernel code, but did not affect the ptrace semantics
in this way.

If you try to set rip to the same value with PTRACE_POKEUSR, it will fail with
-EIO.  However, PTRACE_SETREGS just ignores all individual failures from setting
bad values (e.g. also nonzero segment register values with either of the low two
bits clear) and just sets what it can and returns success.

So AFAICT, the issue is trying to set bogus values for rip.  If the tests are
constrained to permissible values, they should work OK on all kernels.  Is that
a sufficient workaround?

Comment 4 Jan Kratochvil 2008-03-17 14:09:04 UTC
(In reply to comment #3)
> If I'm reading that wdiff fragment correctly, the old rip value was
> 0x39d3f2e25d and you tried to set rip to 0x8786858483828180.  Is that correct?

Yes.

> If you try to set rip to the same value with PTRACE_POKEUSR, it will fail with
> -EIO.  However, PTRACE_SETREGS just ignores all individual failures from
> setting bad values

Later I thought PTRACE_SETREGS should -EIO in such case but OK.

> So AFAICT, the issue is trying to set bogus values for rip.  If the tests are
> constrained to permissible values, they should work OK on all kernels.
> Is that a sufficient workaround?

Yes, implemented for `user-regs-peekpoke'.

Thanks for the explanation.