diff options
Diffstat (limited to 'arch/arm/kernel')
| -rw-r--r-- | arch/arm/kernel/signal.c | 96 | ||||
| -rw-r--r-- | arch/arm/kernel/traps.c | 14 |
2 files changed, 27 insertions, 83 deletions
diff --git a/arch/arm/kernel/signal.c b/arch/arm/kernel/signal.c index a94d75fef598..a917e3dd3666 100644 --- a/arch/arm/kernel/signal.c +++ b/arch/arm/kernel/signal.c | |||
| @@ -139,93 +139,33 @@ struct iwmmxt_sigframe { | |||
| 139 | unsigned long storage[0x98/4]; | 139 | unsigned long storage[0x98/4]; |
| 140 | }; | 140 | }; |
| 141 | 141 | ||
| 142 | static int page_present(struct mm_struct *mm, void __user *uptr, int wr) | ||
| 143 | { | ||
| 144 | unsigned long addr = (unsigned long)uptr; | ||
| 145 | pgd_t *pgd = pgd_offset(mm, addr); | ||
| 146 | if (pgd_present(*pgd)) { | ||
| 147 | pmd_t *pmd = pmd_offset(pgd, addr); | ||
| 148 | if (pmd_present(*pmd)) { | ||
| 149 | pte_t *pte = pte_offset_map(pmd, addr); | ||
| 150 | return (pte_present(*pte) && (!wr || pte_write(*pte))); | ||
| 151 | } | ||
| 152 | } | ||
| 153 | return 0; | ||
| 154 | } | ||
| 155 | |||
| 156 | static int copy_locked(void __user *uptr, void *kptr, size_t size, int write, | ||
| 157 | void (*copyfn)(void *, void __user *)) | ||
| 158 | { | ||
| 159 | unsigned char v, __user *userptr = uptr; | ||
| 160 | int err = 0; | ||
| 161 | |||
| 162 | do { | ||
| 163 | struct mm_struct *mm; | ||
| 164 | |||
| 165 | if (write) { | ||
| 166 | __put_user_error(0, userptr, err); | ||
| 167 | __put_user_error(0, userptr + size - 1, err); | ||
| 168 | } else { | ||
| 169 | __get_user_error(v, userptr, err); | ||
| 170 | __get_user_error(v, userptr + size - 1, err); | ||
| 171 | } | ||
| 172 | |||
| 173 | if (err) | ||
| 174 | break; | ||
| 175 | |||
| 176 | mm = current->mm; | ||
| 177 | spin_lock(&mm->page_table_lock); | ||
| 178 | if (page_present(mm, userptr, write) && | ||
| 179 | page_present(mm, userptr + size - 1, write)) { | ||
| 180 | copyfn(kptr, uptr); | ||
| 181 | } else | ||
| 182 | err = 1; | ||
| 183 | spin_unlock(&mm->page_table_lock); | ||
| 184 | } while (err); | ||
| 185 | |||
| 186 | return err; | ||
| 187 | } | ||
| 188 | |||
| 189 | static int preserve_iwmmxt_context(struct iwmmxt_sigframe *frame) | 142 | static int preserve_iwmmxt_context(struct iwmmxt_sigframe *frame) |
| 190 | { | 143 | { |
| 191 | int err = 0; | 144 | char kbuf[sizeof(*frame) + 8]; |
| 145 | struct iwmmxt_sigframe *kframe; | ||
| 192 | 146 | ||
| 193 | /* the iWMMXt context must be 64 bit aligned */ | 147 | /* the iWMMXt context must be 64 bit aligned */ |
| 194 | WARN_ON((unsigned long)frame & 7); | 148 | kframe = (struct iwmmxt_sigframe *)((unsigned long)(kbuf + 8) & ~7); |
| 195 | 149 | kframe->magic0 = IWMMXT_MAGIC0; | |
| 196 | __put_user_error(IWMMXT_MAGIC0, &frame->magic0, err); | 150 | kframe->magic1 = IWMMXT_MAGIC1; |
| 197 | __put_user_error(IWMMXT_MAGIC1, &frame->magic1, err); | 151 | iwmmxt_task_copy(current_thread_info(), &kframe->storage); |
| 198 | 152 | return __copy_to_user(frame, kframe, sizeof(*frame)); | |
| 199 | /* | ||
| 200 | * iwmmxt_task_copy() doesn't check user permissions. | ||
| 201 | * Let's do a dummy write on the upper boundary to ensure | ||
| 202 | * access to user mem is OK all way up. | ||
| 203 | */ | ||
| 204 | err |= copy_locked(&frame->storage, current_thread_info(), | ||
| 205 | sizeof(frame->storage), 1, iwmmxt_task_copy); | ||
| 206 | return err; | ||
| 207 | } | 153 | } |
| 208 | 154 | ||
| 209 | static int restore_iwmmxt_context(struct iwmmxt_sigframe *frame) | 155 | static int restore_iwmmxt_context(struct iwmmxt_sigframe *frame) |
| 210 | { | 156 | { |
| 211 | unsigned long magic0, magic1; | 157 | char kbuf[sizeof(*frame) + 8]; |
| 212 | int err = 0; | 158 | struct iwmmxt_sigframe *kframe; |
| 213 | 159 | ||
| 214 | /* the iWMMXt context is 64 bit aligned */ | 160 | /* the iWMMXt context must be 64 bit aligned */ |
| 215 | WARN_ON((unsigned long)frame & 7); | 161 | kframe = (struct iwmmxt_sigframe *)((unsigned long)(kbuf + 8) & ~7); |
| 216 | 162 | if (__copy_from_user(kframe, frame, sizeof(*frame))) | |
| 217 | /* | 163 | return -1; |
| 218 | * Validate iWMMXt context signature. | 164 | if (kframe->magic0 != IWMMXT_MAGIC0 || |
| 219 | * Also, iwmmxt_task_restore() doesn't check user permissions. | 165 | kframe->magic1 != IWMMXT_MAGIC1) |
| 220 | * Let's do a dummy write on the upper boundary to ensure | 166 | return -1; |
| 221 | * access to user mem is OK all way up. | 167 | iwmmxt_task_restore(current_thread_info(), &kframe->storage); |
| 222 | */ | 168 | return 0; |
| 223 | __get_user_error(magic0, &frame->magic0, err); | ||
| 224 | __get_user_error(magic1, &frame->magic1, err); | ||
| 225 | if (!err && magic0 == IWMMXT_MAGIC0 && magic1 == IWMMXT_MAGIC1) | ||
| 226 | err = copy_locked(&frame->storage, current_thread_info(), | ||
| 227 | sizeof(frame->storage), 0, iwmmxt_task_restore); | ||
| 228 | return err; | ||
| 229 | } | 169 | } |
| 230 | 170 | ||
| 231 | #endif | 171 | #endif |
diff --git a/arch/arm/kernel/traps.c b/arch/arm/kernel/traps.c index baa09601a64e..66e5a0516f23 100644 --- a/arch/arm/kernel/traps.c +++ b/arch/arm/kernel/traps.c | |||
| @@ -483,29 +483,33 @@ asmlinkage int arm_syscall(int no, struct pt_regs *regs) | |||
| 483 | unsigned long addr = regs->ARM_r2; | 483 | unsigned long addr = regs->ARM_r2; |
| 484 | struct mm_struct *mm = current->mm; | 484 | struct mm_struct *mm = current->mm; |
| 485 | pgd_t *pgd; pmd_t *pmd; pte_t *pte; | 485 | pgd_t *pgd; pmd_t *pmd; pte_t *pte; |
| 486 | spinlock_t *ptl; | ||
| 486 | 487 | ||
| 487 | regs->ARM_cpsr &= ~PSR_C_BIT; | 488 | regs->ARM_cpsr &= ~PSR_C_BIT; |
| 488 | spin_lock(&mm->page_table_lock); | 489 | down_read(&mm->mmap_sem); |
| 489 | pgd = pgd_offset(mm, addr); | 490 | pgd = pgd_offset(mm, addr); |
| 490 | if (!pgd_present(*pgd)) | 491 | if (!pgd_present(*pgd)) |
| 491 | goto bad_access; | 492 | goto bad_access; |
| 492 | pmd = pmd_offset(pgd, addr); | 493 | pmd = pmd_offset(pgd, addr); |
| 493 | if (!pmd_present(*pmd)) | 494 | if (!pmd_present(*pmd)) |
| 494 | goto bad_access; | 495 | goto bad_access; |
| 495 | pte = pte_offset_map(pmd, addr); | 496 | pte = pte_offset_map_lock(mm, pmd, addr, &ptl); |
| 496 | if (!pte_present(*pte) || !pte_write(*pte)) | 497 | if (!pte_present(*pte) || !pte_write(*pte)) { |
| 498 | pte_unmap_unlock(pte, ptl); | ||
| 497 | goto bad_access; | 499 | goto bad_access; |
| 500 | } | ||
| 498 | val = *(unsigned long *)addr; | 501 | val = *(unsigned long *)addr; |
| 499 | val -= regs->ARM_r0; | 502 | val -= regs->ARM_r0; |
| 500 | if (val == 0) { | 503 | if (val == 0) { |
| 501 | *(unsigned long *)addr = regs->ARM_r1; | 504 | *(unsigned long *)addr = regs->ARM_r1; |
| 502 | regs->ARM_cpsr |= PSR_C_BIT; | 505 | regs->ARM_cpsr |= PSR_C_BIT; |
| 503 | } | 506 | } |
| 504 | spin_unlock(&mm->page_table_lock); | 507 | pte_unmap_unlock(pte, ptl); |
| 508 | up_read(&mm->mmap_sem); | ||
| 505 | return val; | 509 | return val; |
| 506 | 510 | ||
| 507 | bad_access: | 511 | bad_access: |
| 508 | spin_unlock(&mm->page_table_lock); | 512 | up_read(&mm->mmap_sem); |
| 509 | /* simulate a write access fault */ | 513 | /* simulate a write access fault */ |
| 510 | do_DataAbort(addr, 15 + (1 << 11), regs); | 514 | do_DataAbort(addr, 15 + (1 << 11), regs); |
| 511 | return -1; | 515 | return -1; |
