c34d1b4d16
check_user_page_readable is a problematic variant of follow_page. It's used only by oprofile's i386 and arm backtrace code, at interrupt time, to establish whether a userspace stackframe is currently readable. This is problematic, because we want to push the page_table_lock down inside follow_page, and later split it; whereas oprofile is doing a spin_trylock on it (in the i386 case, forgotten in the arm case), and needs that to pin perhaps two pages spanned by the stackframe (which might be covered by different locks when we split). I think oprofile is going about this in the wrong way: it doesn't need to know the area is readable (neither i386 nor arm uses read protection of user pages), it doesn't need to pin the memory, it should simply __copy_from_user_inatomic, and see if that succeeds or not. Sorry, but I've not got around to devising the sparse __user annotations for this. Then we can eliminate check_user_page_readable, and return to a single follow_page without the __follow_page variants. Signed-off-by: Hugh Dickins <hugh@veritas.com> Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
116 lines
2.9 KiB
C
116 lines
2.9 KiB
C
/*
|
|
* Arm specific backtracing code for oprofile
|
|
*
|
|
* Copyright 2005 Openedhand Ltd.
|
|
*
|
|
* Author: Richard Purdie <rpurdie@openedhand.com>
|
|
*
|
|
* Based on i386 oprofile backtrace code by John Levon, David Smith
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License version 2 as
|
|
* published by the Free Software Foundation.
|
|
*
|
|
*/
|
|
|
|
#include <linux/oprofile.h>
|
|
#include <linux/sched.h>
|
|
#include <linux/mm.h>
|
|
#include <asm/ptrace.h>
|
|
#include <asm/uaccess.h>
|
|
|
|
|
|
/*
|
|
* The registers we're interested in are at the end of the variable
|
|
* length saved register structure. The fp points at the end of this
|
|
* structure so the address of this struct is:
|
|
* (struct frame_tail *)(xxx->fp)-1
|
|
*/
|
|
struct frame_tail {
|
|
struct frame_tail *fp;
|
|
unsigned long sp;
|
|
unsigned long lr;
|
|
} __attribute__((packed));
|
|
|
|
|
|
#ifdef CONFIG_FRAME_POINTER
|
|
static struct frame_tail* kernel_backtrace(struct frame_tail *tail)
|
|
{
|
|
oprofile_add_trace(tail->lr);
|
|
|
|
/* frame pointers should strictly progress back up the stack
|
|
* (towards higher addresses) */
|
|
if (tail >= tail->fp)
|
|
return NULL;
|
|
|
|
return tail->fp-1;
|
|
}
|
|
#endif
|
|
|
|
static struct frame_tail* user_backtrace(struct frame_tail *tail)
|
|
{
|
|
struct frame_tail buftail[2];
|
|
|
|
/* Also check accessibility of one struct frame_tail beyond */
|
|
if (!access_ok(VERIFY_READ, tail, sizeof(buftail)))
|
|
return NULL;
|
|
if (__copy_from_user_inatomic(buftail, tail, sizeof(buftail)))
|
|
return NULL;
|
|
|
|
oprofile_add_trace(buftail[0].lr);
|
|
|
|
/* frame pointers should strictly progress back up the stack
|
|
* (towards higher addresses) */
|
|
if (tail >= buftail[0].fp)
|
|
return NULL;
|
|
|
|
return buftail[0].fp-1;
|
|
}
|
|
|
|
/*
|
|
* | | /\ Higher addresses
|
|
* | |
|
|
* --------------- stack base (address of current_thread_info)
|
|
* | thread info |
|
|
* . .
|
|
* | stack |
|
|
* --------------- saved regs->ARM_fp value if valid (frame_tail address)
|
|
* . .
|
|
* --------------- struct pt_regs stored on stack (struct pt_regs *)
|
|
* | |
|
|
* . .
|
|
* | |
|
|
* --------------- %esp
|
|
* | |
|
|
* | | \/ Lower addresses
|
|
*
|
|
* Thus, &pt_regs <-> stack base restricts the valid(ish) fp values
|
|
*/
|
|
static int valid_kernel_stack(struct frame_tail *tail, struct pt_regs *regs)
|
|
{
|
|
unsigned long tailaddr = (unsigned long)tail;
|
|
unsigned long stack = (unsigned long)regs;
|
|
unsigned long stack_base = (stack & ~(THREAD_SIZE - 1)) + THREAD_SIZE;
|
|
|
|
return (tailaddr > stack) && (tailaddr < stack_base);
|
|
}
|
|
|
|
void arm_backtrace(struct pt_regs * const regs, unsigned int depth)
|
|
{
|
|
struct frame_tail *tail;
|
|
|
|
tail = ((struct frame_tail *) regs->ARM_fp) - 1;
|
|
|
|
if (!user_mode(regs)) {
|
|
|
|
#ifdef CONFIG_FRAME_POINTER
|
|
while (depth-- && tail && valid_kernel_stack(tail, regs)) {
|
|
tail = kernel_backtrace(tail);
|
|
}
|
|
#endif
|
|
return;
|
|
}
|
|
|
|
while (depth-- && tail && !((unsigned long) tail & 3))
|
|
tail = user_backtrace(tail);
|
|
}
|