Commit 7b3552024380f306a6c50d5105d18d9d4258fa4e

Authored by Zachary Amsden
Committed by Andi Kleen
1 parent bbab4f3bb7

[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>

Showing 3 changed files with 21 additions and 12 deletions Side-by-side Diff

arch/i386/kernel/time.c
... ... @@ -131,15 +131,13 @@
131 131 unsigned long pc = instruction_pointer(regs);
132 132  
133 133 #ifdef CONFIG_SMP
134   - if (!user_mode_vm(regs) && in_lock_functions(pc)) {
  134 + if (!v8086_mode(regs) && SEGMENT_IS_KERNEL_CODE(regs->xcs) &&
  135 + in_lock_functions(pc)) {
135 136 #ifdef CONFIG_FRAME_POINTER
136 137 return *(unsigned long *)(regs->ebp + 4);
137 138 #else
138   - unsigned long *sp;
139   - if ((regs->xcs & 3) == 0)
140   - sp = (unsigned long *)&regs->esp;
141   - else
142   - sp = (unsigned long *)regs->esp;
  139 + unsigned long *sp = (unsigned long *)&regs->esp;
  140 +
143 141 /* Return address is either directly at stack pointer
144 142 or above a saved eflags. Eflags has bits 22-31 zero,
145 143 kernel addresses don't. */
include/asm-i386/ptrace.h
... ... @@ -49,6 +49,10 @@
49 49 {
50 50 return ((regs->xcs & SEGMENT_RPL_MASK) | (regs->eflags & VM_MASK)) >= USER_RPL;
51 51 }
  52 +static inline int v8086_mode(struct pt_regs *regs)
  53 +{
  54 + return (regs->eflags & VM_MASK);
  55 +}
52 56  
53 57 #define instruction_pointer(regs) ((regs)->eip)
54 58 #define regs_return_value(regs) ((regs)->eax)
include/asm-i386/segment.h
... ... @@ -83,14 +83,8 @@
83 83 * The GDT has 32 entries
84 84 */
85 85 #define GDT_ENTRIES 32
86   -
87 86 #define GDT_SIZE (GDT_ENTRIES * 8)
88 87  
89   -/* Matches __KERNEL_CS and __USER_CS (they must be 2 entries apart) */
90   -#define SEGMENT_IS_FLAT_CODE(x) (((x) & 0xec) == GDT_ENTRY_KERNEL_CS * 8)
91   -/* Matches PNP_CS32 and PNP_CS16 (they must be consecutive) */
92   -#define SEGMENT_IS_PNP_CODE(x) (((x) & 0xf4) == GDT_ENTRY_PNPBIOS_BASE * 8)
93   -
94 88 /* Simple and small GDT entries for booting only */
95 89  
96 90 #define GDT_ENTRY_BOOT_CS 2
... ... @@ -134,5 +128,18 @@
134 128 #ifndef CONFIG_PARAVIRT
135 129 #define get_kernel_rpl() 0
136 130 #endif
  131 +/*
  132 + * Matching rules for certain types of segments.
  133 + */
  134 +
  135 +/* Matches only __KERNEL_CS, ignoring PnP / USER / APM segments */
  136 +#define SEGMENT_IS_KERNEL_CODE(x) (((x) & 0xfc) == GDT_ENTRY_KERNEL_CS * 8)
  137 +
  138 +/* Matches __KERNEL_CS and __USER_CS (they must be 2 entries apart) */
  139 +#define SEGMENT_IS_FLAT_CODE(x) (((x) & 0xec) == GDT_ENTRY_KERNEL_CS * 8)
  140 +
  141 +/* Matches PNP_CS32 and PNP_CS16 (they must be consecutive) */
  142 +#define SEGMENT_IS_PNP_CODE(x) (((x) & 0xf4) == GDT_ENTRY_PNPBIOS_BASE * 8)
  143 +
137 144 #endif