[PATCH] i386: Profile pc badness
Profile_pc was broken when using paravirtualization because the assumption the kernel was running at CPL 0 was violated, causing bad logic to read a random value off the stack. The only way to be in kernel lock functions is to be in kernel code, so validate that assumption explicitly by checking the CS value. We don't want to be fooled by BIOS / APM segments and try to read those stacks, so only match KERNEL_CS. I moved some stuff in segment.h to make it prettier. Signed-off-by: Zachary Amsden <zach@vmware.com> Signed-off-by: Andi Kleen <ak@suse.de>
This commit is contained in:
Родитель
bbab4f3bb7
Коммит
7b35520243
|
@ -131,15 +131,13 @@ unsigned long profile_pc(struct pt_regs *regs)
|
||||||
unsigned long pc = instruction_pointer(regs);
|
unsigned long pc = instruction_pointer(regs);
|
||||||
|
|
||||||
#ifdef CONFIG_SMP
|
#ifdef CONFIG_SMP
|
||||||
if (!user_mode_vm(regs) && in_lock_functions(pc)) {
|
if (!v8086_mode(regs) && SEGMENT_IS_KERNEL_CODE(regs->xcs) &&
|
||||||
|
in_lock_functions(pc)) {
|
||||||
#ifdef CONFIG_FRAME_POINTER
|
#ifdef CONFIG_FRAME_POINTER
|
||||||
return *(unsigned long *)(regs->ebp + 4);
|
return *(unsigned long *)(regs->ebp + 4);
|
||||||
#else
|
#else
|
||||||
unsigned long *sp;
|
unsigned long *sp = (unsigned long *)®s->esp;
|
||||||
if ((regs->xcs & 3) == 0)
|
|
||||||
sp = (unsigned long *)®s->esp;
|
|
||||||
else
|
|
||||||
sp = (unsigned long *)regs->esp;
|
|
||||||
/* Return address is either directly at stack pointer
|
/* Return address is either directly at stack pointer
|
||||||
or above a saved eflags. Eflags has bits 22-31 zero,
|
or above a saved eflags. Eflags has bits 22-31 zero,
|
||||||
kernel addresses don't. */
|
kernel addresses don't. */
|
||||||
|
|
|
@ -49,6 +49,10 @@ static inline int user_mode_vm(struct pt_regs *regs)
|
||||||
{
|
{
|
||||||
return ((regs->xcs & SEGMENT_RPL_MASK) | (regs->eflags & VM_MASK)) >= USER_RPL;
|
return ((regs->xcs & SEGMENT_RPL_MASK) | (regs->eflags & VM_MASK)) >= USER_RPL;
|
||||||
}
|
}
|
||||||
|
static inline int v8086_mode(struct pt_regs *regs)
|
||||||
|
{
|
||||||
|
return (regs->eflags & VM_MASK);
|
||||||
|
}
|
||||||
|
|
||||||
#define instruction_pointer(regs) ((regs)->eip)
|
#define instruction_pointer(regs) ((regs)->eip)
|
||||||
#define regs_return_value(regs) ((regs)->eax)
|
#define regs_return_value(regs) ((regs)->eax)
|
||||||
|
|
|
@ -83,14 +83,8 @@
|
||||||
* The GDT has 32 entries
|
* The GDT has 32 entries
|
||||||
*/
|
*/
|
||||||
#define GDT_ENTRIES 32
|
#define GDT_ENTRIES 32
|
||||||
|
|
||||||
#define GDT_SIZE (GDT_ENTRIES * 8)
|
#define GDT_SIZE (GDT_ENTRIES * 8)
|
||||||
|
|
||||||
/* Matches __KERNEL_CS and __USER_CS (they must be 2 entries apart) */
|
|
||||||
#define SEGMENT_IS_FLAT_CODE(x) (((x) & 0xec) == GDT_ENTRY_KERNEL_CS * 8)
|
|
||||||
/* Matches PNP_CS32 and PNP_CS16 (they must be consecutive) */
|
|
||||||
#define SEGMENT_IS_PNP_CODE(x) (((x) & 0xf4) == GDT_ENTRY_PNPBIOS_BASE * 8)
|
|
||||||
|
|
||||||
/* Simple and small GDT entries for booting only */
|
/* Simple and small GDT entries for booting only */
|
||||||
|
|
||||||
#define GDT_ENTRY_BOOT_CS 2
|
#define GDT_ENTRY_BOOT_CS 2
|
||||||
|
@ -134,4 +128,17 @@
|
||||||
#ifndef CONFIG_PARAVIRT
|
#ifndef CONFIG_PARAVIRT
|
||||||
#define get_kernel_rpl() 0
|
#define get_kernel_rpl() 0
|
||||||
#endif
|
#endif
|
||||||
|
/*
|
||||||
|
* Matching rules for certain types of segments.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Matches only __KERNEL_CS, ignoring PnP / USER / APM segments */
|
||||||
|
#define SEGMENT_IS_KERNEL_CODE(x) (((x) & 0xfc) == GDT_ENTRY_KERNEL_CS * 8)
|
||||||
|
|
||||||
|
/* Matches __KERNEL_CS and __USER_CS (they must be 2 entries apart) */
|
||||||
|
#define SEGMENT_IS_FLAT_CODE(x) (((x) & 0xec) == GDT_ENTRY_KERNEL_CS * 8)
|
||||||
|
|
||||||
|
/* Matches PNP_CS32 and PNP_CS16 (they must be consecutive) */
|
||||||
|
#define SEGMENT_IS_PNP_CODE(x) (((x) & 0xf4) == GDT_ENTRY_PNPBIOS_BASE * 8)
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
Загрузка…
Ссылка в новой задаче