diff options
author | Roland McGrath <roland@redhat.com> | 2008-02-06 16:39:44 -0500 |
---|---|---|
committer | Ingo Molnar <mingo@elte.hu> | 2008-02-06 16:39:44 -0500 |
commit | c63855d04034c96db791a7217954c93aa66d24cb (patch) | |
tree | 97f22e5bf68de576cad776cd14921903f7d51449 /arch/x86/kernel/ptrace.c | |
parent | c1f766b5519f9b5a51b0e6884ed9e02bce775ea8 (diff) |
x86 ptrace: disallow null cs/ss
In my revamp of the x86 ptrace code for setting register values,
I accidentally omitted a check that was there in the old code.
Allowing %cs to be 0 causes a bad crash in recovery from iret failure.
This patch fixes that regression against 2.6.24, and adds a comment
that should help prevent this subtlety from being overlooked again.
Signed-off-by: Roland McGrath <roland@redhat.com>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
Diffstat (limited to 'arch/x86/kernel/ptrace.c')
-rw-r--r-- | arch/x86/kernel/ptrace.c | 25 |
1 files changed, 23 insertions, 2 deletions
diff --git a/arch/x86/kernel/ptrace.c b/arch/x86/kernel/ptrace.c index 96286df1bb81..702c33efea84 100644 --- a/arch/x86/kernel/ptrace.c +++ b/arch/x86/kernel/ptrace.c | |||
@@ -103,9 +103,26 @@ static int set_segment_reg(struct task_struct *task, | |||
103 | if (invalid_selector(value)) | 103 | if (invalid_selector(value)) |
104 | return -EIO; | 104 | return -EIO; |
105 | 105 | ||
106 | if (offset != offsetof(struct user_regs_struct, gs)) | 106 | /* |
107 | * For %cs and %ss we cannot permit a null selector. | ||
108 | * We can permit a bogus selector as long as it has USER_RPL. | ||
109 | * Null selectors are fine for other segment registers, but | ||
110 | * we will never get back to user mode with invalid %cs or %ss | ||
111 | * and will take the trap in iret instead. Much code relies | ||
112 | * on user_mode() to distinguish a user trap frame (which can | ||
113 | * safely use invalid selectors) from a kernel trap frame. | ||
114 | */ | ||
115 | switch (offset) { | ||
116 | case offsetof(struct user_regs_struct, cs): | ||
117 | case offsetof(struct user_regs_struct, ss): | ||
118 | if (unlikely(value == 0)) | ||
119 | return -EIO; | ||
120 | |||
121 | default: | ||
107 | *pt_regs_access(task_pt_regs(task), offset) = value; | 122 | *pt_regs_access(task_pt_regs(task), offset) = value; |
108 | else { | 123 | break; |
124 | |||
125 | case offsetof(struct user_regs_struct, gs): | ||
109 | task->thread.gs = value; | 126 | task->thread.gs = value; |
110 | if (task == current) | 127 | if (task == current) |
111 | /* | 128 | /* |
@@ -227,12 +244,16 @@ static int set_segment_reg(struct task_struct *task, | |||
227 | * Can't actually change these in 64-bit mode. | 244 | * Can't actually change these in 64-bit mode. |
228 | */ | 245 | */ |
229 | case offsetof(struct user_regs_struct,cs): | 246 | case offsetof(struct user_regs_struct,cs): |
247 | if (unlikely(value == 0)) | ||
248 | return -EIO; | ||
230 | #ifdef CONFIG_IA32_EMULATION | 249 | #ifdef CONFIG_IA32_EMULATION |
231 | if (test_tsk_thread_flag(task, TIF_IA32)) | 250 | if (test_tsk_thread_flag(task, TIF_IA32)) |
232 | task_pt_regs(task)->cs = value; | 251 | task_pt_regs(task)->cs = value; |
233 | #endif | 252 | #endif |
234 | break; | 253 | break; |
235 | case offsetof(struct user_regs_struct,ss): | 254 | case offsetof(struct user_regs_struct,ss): |
255 | if (unlikely(value == 0)) | ||
256 | return -EIO; | ||
236 | #ifdef CONFIG_IA32_EMULATION | 257 | #ifdef CONFIG_IA32_EMULATION |
237 | if (test_tsk_thread_flag(task, TIF_IA32)) | 258 | if (test_tsk_thread_flag(task, TIF_IA32)) |
238 | task_pt_regs(task)->ss = value; | 259 | task_pt_regs(task)->ss = value; |