diff options
Diffstat (limited to 'arch/x86_64/kernel/ptrace.c')
-rw-r--r-- | arch/x86_64/kernel/ptrace.c | 29 |
1 files changed, 17 insertions, 12 deletions
diff --git a/arch/x86_64/kernel/ptrace.c b/arch/x86_64/kernel/ptrace.c index 2d50024c9f30..addc14af0c56 100644 --- a/arch/x86_64/kernel/ptrace.c +++ b/arch/x86_64/kernel/ptrace.c | |||
@@ -116,17 +116,17 @@ unsigned long convert_rip_to_linear(struct task_struct *child, struct pt_regs *r | |||
116 | return addr; | 116 | return addr; |
117 | } | 117 | } |
118 | 118 | ||
119 | static int is_at_popf(struct task_struct *child, struct pt_regs *regs) | 119 | static int is_setting_trap_flag(struct task_struct *child, struct pt_regs *regs) |
120 | { | 120 | { |
121 | int i, copied; | 121 | int i, copied; |
122 | unsigned char opcode[16]; | 122 | unsigned char opcode[15]; |
123 | unsigned long addr = convert_rip_to_linear(child, regs); | 123 | unsigned long addr = convert_rip_to_linear(child, regs); |
124 | 124 | ||
125 | copied = access_process_vm(child, addr, opcode, sizeof(opcode), 0); | 125 | copied = access_process_vm(child, addr, opcode, sizeof(opcode), 0); |
126 | for (i = 0; i < copied; i++) { | 126 | for (i = 0; i < copied; i++) { |
127 | switch (opcode[i]) { | 127 | switch (opcode[i]) { |
128 | /* popf */ | 128 | /* popf and iret */ |
129 | case 0x9d: | 129 | case 0x9d: case 0xcf: |
130 | return 1; | 130 | return 1; |
131 | 131 | ||
132 | /* CHECKME: 64 65 */ | 132 | /* CHECKME: 64 65 */ |
@@ -138,14 +138,17 @@ static int is_at_popf(struct task_struct *child, struct pt_regs *regs) | |||
138 | case 0x26: case 0x2e: | 138 | case 0x26: case 0x2e: |
139 | case 0x36: case 0x3e: | 139 | case 0x36: case 0x3e: |
140 | case 0x64: case 0x65: | 140 | case 0x64: case 0x65: |
141 | case 0xf0: case 0xf2: case 0xf3: | 141 | case 0xf2: case 0xf3: |
142 | continue; | 142 | continue; |
143 | 143 | ||
144 | /* REX prefixes */ | ||
145 | case 0x40 ... 0x4f: | 144 | case 0x40 ... 0x4f: |
145 | if (regs->cs != __USER_CS) | ||
146 | /* 32-bit mode: register increment */ | ||
147 | return 0; | ||
148 | /* 64-bit mode: REX prefix */ | ||
146 | continue; | 149 | continue; |
147 | 150 | ||
148 | /* CHECKME: f0, f2, f3 */ | 151 | /* CHECKME: f2, f3 */ |
149 | 152 | ||
150 | /* | 153 | /* |
151 | * pushf: NOTE! We should probably not let | 154 | * pushf: NOTE! We should probably not let |
@@ -186,10 +189,8 @@ static void set_singlestep(struct task_struct *child) | |||
186 | * ..but if TF is changed by the instruction we will trace, | 189 | * ..but if TF is changed by the instruction we will trace, |
187 | * don't mark it as being "us" that set it, so that we | 190 | * don't mark it as being "us" that set it, so that we |
188 | * won't clear it by hand later. | 191 | * won't clear it by hand later. |
189 | * | ||
190 | * AK: this is not enough, LAHF and IRET can change TF in user space too. | ||
191 | */ | 192 | */ |
192 | if (is_at_popf(child, regs)) | 193 | if (is_setting_trap_flag(child, regs)) |
193 | return; | 194 | return; |
194 | 195 | ||
195 | child->ptrace |= PT_DTRACE; | 196 | child->ptrace |= PT_DTRACE; |
@@ -420,9 +421,13 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data) | |||
420 | if ((0x5554 >> ((data >> (16 + 4*i)) & 0xf)) & 1) | 421 | if ((0x5554 >> ((data >> (16 + 4*i)) & 0xf)) & 1) |
421 | break; | 422 | break; |
422 | if (i == 4) { | 423 | if (i == 4) { |
423 | child->thread.debugreg7 = data; | 424 | child->thread.debugreg7 = data; |
425 | if (data) | ||
426 | set_tsk_thread_flag(child, TIF_DEBUG); | ||
427 | else | ||
428 | clear_tsk_thread_flag(child, TIF_DEBUG); | ||
424 | ret = 0; | 429 | ret = 0; |
425 | } | 430 | } |
426 | break; | 431 | break; |
427 | } | 432 | } |
428 | break; | 433 | break; |