diff options
author | Chris Metcalf <cmetcalf@tilera.com> | 2010-10-28 15:47:06 -0400 |
---|---|---|
committer | Chris Metcalf <cmetcalf@tilera.com> | 2010-11-01 15:31:17 -0400 |
commit | 1deb9c5dfb179819ecdbf80a1d121e26c63caab3 (patch) | |
tree | c6f6ec6030eb22f094a65c07e63312758d079794 /arch/tile | |
parent | 34a89d26bdc4ba46a406fa3842239e921c493d44 (diff) |
arch/tile: don't allow user code to set the PL via ptrace or signal return
The kernel was allowing any component of the pt_regs to be updated either
by signal handlers writing to the stack, or by processes writing via
PTRACE_POKEUSR or PTRACE_SETREGS, which meant they could set their PL
up from 0 to 1 and get access to kernel code and data (or, in practice,
cause a kernel panic). We now always reset the ex1 field, allowing the
user to set their ICS bit only.
Signed-off-by: Chris Metcalf <cmetcalf@tilera.com>
Diffstat (limited to 'arch/tile')
-rw-r--r-- | arch/tile/kernel/ptrace.c | 39 | ||||
-rw-r--r-- | arch/tile/kernel/signal.c | 3 |
2 files changed, 24 insertions, 18 deletions
diff --git a/arch/tile/kernel/ptrace.c b/arch/tile/kernel/ptrace.c index 9cd29884c09f..e92e40527d6d 100644 --- a/arch/tile/kernel/ptrace.c +++ b/arch/tile/kernel/ptrace.c | |||
@@ -50,10 +50,10 @@ long arch_ptrace(struct task_struct *child, long request, | |||
50 | { | 50 | { |
51 | unsigned long __user *datap = (long __user __force *)data; | 51 | unsigned long __user *datap = (long __user __force *)data; |
52 | unsigned long tmp; | 52 | unsigned long tmp; |
53 | int i; | ||
54 | long ret = -EIO; | 53 | long ret = -EIO; |
55 | unsigned long *childregs; | ||
56 | char *childreg; | 54 | char *childreg; |
55 | struct pt_regs copyregs; | ||
56 | int ex1_offset; | ||
57 | 57 | ||
58 | switch (request) { | 58 | switch (request) { |
59 | 59 | ||
@@ -80,6 +80,16 @@ long arch_ptrace(struct task_struct *child, long request, | |||
80 | if (addr >= PTREGS_SIZE) | 80 | if (addr >= PTREGS_SIZE) |
81 | break; | 81 | break; |
82 | childreg = (char *)task_pt_regs(child) + addr; | 82 | childreg = (char *)task_pt_regs(child) + addr; |
83 | |||
84 | /* Guard against overwrites of the privilege level. */ | ||
85 | ex1_offset = PTREGS_OFFSET_EX1; | ||
86 | #if defined(CONFIG_COMPAT) && defined(__BIG_ENDIAN) | ||
87 | if (is_compat_task()) /* point at low word */ | ||
88 | ex1_offset += sizeof(compat_long_t); | ||
89 | #endif | ||
90 | if (addr == ex1_offset) | ||
91 | data = PL_ICS_EX1(USER_PL, EX1_ICS(data)); | ||
92 | |||
83 | #ifdef CONFIG_COMPAT | 93 | #ifdef CONFIG_COMPAT |
84 | if (is_compat_task()) { | 94 | if (is_compat_task()) { |
85 | if (addr & (sizeof(compat_long_t)-1)) | 95 | if (addr & (sizeof(compat_long_t)-1)) |
@@ -96,26 +106,19 @@ long arch_ptrace(struct task_struct *child, long request, | |||
96 | break; | 106 | break; |
97 | 107 | ||
98 | case PTRACE_GETREGS: /* Get all registers from the child. */ | 108 | case PTRACE_GETREGS: /* Get all registers from the child. */ |
99 | if (!access_ok(VERIFY_WRITE, datap, PTREGS_SIZE)) | 109 | if (copy_to_user(datap, task_pt_regs(child), |
100 | break; | 110 | sizeof(struct pt_regs)) == 0) { |
101 | childregs = (long *)task_pt_regs(child); | 111 | ret = 0; |
102 | for (i = 0; i < sizeof(struct pt_regs)/sizeof(unsigned long); | ||
103 | ++i) { | ||
104 | ret = __put_user(childregs[i], &datap[i]); | ||
105 | if (ret != 0) | ||
106 | break; | ||
107 | } | 112 | } |
108 | break; | 113 | break; |
109 | 114 | ||
110 | case PTRACE_SETREGS: /* Set all registers in the child. */ | 115 | case PTRACE_SETREGS: /* Set all registers in the child. */ |
111 | if (!access_ok(VERIFY_READ, datap, PTREGS_SIZE)) | 116 | if (copy_from_user(©regs, datap, |
112 | break; | 117 | sizeof(struct pt_regs)) == 0) { |
113 | childregs = (long *)task_pt_regs(child); | 118 | copyregs.ex1 = |
114 | for (i = 0; i < sizeof(struct pt_regs)/sizeof(unsigned long); | 119 | PL_ICS_EX1(USER_PL, EX1_ICS(copyregs.ex1)); |
115 | ++i) { | 120 | *task_pt_regs(child) = copyregs; |
116 | ret = __get_user(childregs[i], &datap[i]); | 121 | ret = 0; |
117 | if (ret != 0) | ||
118 | break; | ||
119 | } | 122 | } |
120 | break; | 123 | break; |
121 | 124 | ||
diff --git a/arch/tile/kernel/signal.c b/arch/tile/kernel/signal.c index 704ce0bce833..687719d4abd1 100644 --- a/arch/tile/kernel/signal.c +++ b/arch/tile/kernel/signal.c | |||
@@ -71,6 +71,9 @@ int restore_sigcontext(struct pt_regs *regs, | |||
71 | for (i = 0; i < sizeof(struct pt_regs)/sizeof(long); ++i) | 71 | for (i = 0; i < sizeof(struct pt_regs)/sizeof(long); ++i) |
72 | err |= __get_user(regs->regs[i], &sc->gregs[i]); | 72 | err |= __get_user(regs->regs[i], &sc->gregs[i]); |
73 | 73 | ||
74 | /* Ensure that the PL is always set to USER_PL. */ | ||
75 | regs->ex1 = PL_ICS_EX1(USER_PL, EX1_ICS(regs->ex1)); | ||
76 | |||
74 | regs->faultnum = INT_SWINT_1_SIGRETURN; | 77 | regs->faultnum = INT_SWINT_1_SIGRETURN; |
75 | 78 | ||
76 | err |= __get_user(*pr0, &sc->gregs[0]); | 79 | err |= __get_user(*pr0, &sc->gregs[0]); |