xenoprof: Handle 32-bit guest stacks properly in a 64-bit hypervisor
PATCH 2/3 xenoprof: Handle 32-bit guest stacks properly in a 64-bit hypervisor
xenoprof: Handle 32-bit guest stacks properly in a 64-bit hypervisor
The dump_guest_backtrace() function attempted to walk the stack based on the assumption that the guest and hypervisor pointer sizes were the same; thus any 32-bit guest running under 64-bit hypervisor would have unreliable results.
In 64-bit mode, read the 32-bit stack frame properly.
xen/arch/x86/oprofile/backtrace.c Wed Jan 18 17:35:06 2012 +0000 renamed from xen/arch/x86/oprofile/backtrace.c Wed Jan 18 17:23:02 2012 +0000 +41 -7
@@ -20,6 +20,13 @@ struct frame_head { unsigned long ret; } __attribute__((packed)); +#ifdef CONFIG_X86_64 +struct frame_head_32bit { + uint32_t ebp; + unsigned long ret; +} __attribute__((packed)); +#endif + static struct frame_head * dump_hypervisor_backtrace(struct domain *d, struct vcpu *vcpu, struct frame_head * head, int mode)
@@ -35,19 +42,46 @@ dump_hypervisor_backtrace(struct domain return head->ebp; } +#ifdef CONFIG_X86_64 +static inline int is_32bit_vcpu(struct vcpu *vcpu) +{ + if (is_hvm_vcpu(vcpu)) + return !hvm_long_mode_enabled(vcpu); + else + return is_pv_32bit_vcpu(vcpu); +} +#endif + static struct frame_head * dump_guest_backtrace(struct domain *d, struct vcpu *vcpu, struct frame_head * head, int mode) { struct frame_head bufhead[2]; XEN_GUEST_HANDLE(char) guest_head = guest_handle_from_ptr(head, char); - - /* Also check accessibility of one struct frame_head beyond */ - if (!guest_handle_okay(guest_head, sizeof(bufhead))) - return 0; - if (__copy_from_guest_offset((char *)bufhead, guest_head, 0, - sizeof(bufhead))) - return 0; + +#ifdef CONFIG_X86_64 + if ( is_32bit_vcpu(vcpu) ) + { + struct frame_head_32bit bufhead32[2]; + /* Also check accessibility of one struct frame_head beyond */ + if (!guest_handle_okay(guest_head, sizeof(bufhead32))) + return 0; + if (__copy_from_guest_offset((char *)bufhead32, guest_head, 0, + sizeof(bufhead32))) + return 0; + bufhead[0].ebp=(struct frame_head *)(unsigned long)bufhead32[0].ebp; + bufhead[0].ret=bufhead32[0].ret; + } + else +#endif + { + /* Also check accessibility of one struct frame_head beyond */ + if (!guest_handle_okay(guest_head, sizeof(bufhead))) + return 0; + if (__copy_from_guest_offset((char *)bufhead, guest_head, 0, + sizeof(bufhead))) + return 0; + } if (!xenoprof_add_trace(d, vcpu, bufhead[0].ret, mode)) return 0;
xenoprof: Handle 32-bit guest stacks properly in a 64-bit hypervisor
The dump_guest_backtrace() function attempted to walk the stack based on the assumption that the guest and hypervisor pointer sizes were the same; thus any 32-bit guest running under 64-bit hypervisor would have unreliable results.
In 64-bit mode, read the 32-bit stack frame properly.
xen/arch/x86/oprofile/backtrace.c +41 -7
66 lines not on this page · raw message
_______________________________________________
Xen-devel mailing list
[email protected]
http://lists.xensource.com/xen-devel
Quoted 18 lines
Quoted 1 line
Quoted 1 line · Marcus Granado
xenoprof: Handle 32-bit guest stacks properly in a 64-bit hypervisor
The
dump_guest_backtrace()function attempted to walk the stack based on the assumption that the guest and hypervisor pointer sizes were the same; thus any 32-bit guest running under 64-bit hypervisor would have unreliable results.In 64-bit mode, read the 32-bit stack frame properly.
Signed-off-by Marcus Granado <[email protected]>Signed-off-by George Dunlap <[email protected]>diff -r
f6953e89913f-r21e7744a52b1xen/arch/x86/oprofile/backtrace.c
xen/arch/x86/oprofile/backtrace.c Wed Jan 18 17:35:06 2012 +0000 struct frame_head { · line 26
Quoted 7 lines
xen/arch/x86/oprofile/backtrace.c Wed Jan 18 17:35:06 2012 +0000 renamed from xen/arch/x86/oprofile/backtrace.c Wed Jan 18 17:23:02 2012 +0000 +4
-0@@ -20,6 +20,13 @@ struct frame_head { unsigned long ret; } __attribute__((packed)); +#ifdef CONFIG_X86_64 +struct frame_head_32bit { + uint32_t ebp; + unsigned long ret;
unsigned long (i.e. 64 bits)?
Quoted 32 lines
xen/arch/x86/oprofile/backtrace.c Wed Jan 18 17:35:06 2012 +0000 renamed from xen/arch/x86/oprofile/backtrace.c Wed Jan 18 17:23:02 2012 +0000 +13
-6+} __attribute__((packed)); +#endif + static struct frame_head * dump_hypervisor_backtrace(struct domain *d, struct vcpu *vcpu, struct frame_head * head, int mode)@@ -35,19 +42,46 @@ dump_hypervisor_backtrace(struct domain return head->ebp; } +#ifdef CONFIG_X86_64 +static inline int is_32bit_vcpu(struct vcpu *vcpu) +{ + if (is_hvm_vcpu(vcpu)) + return !hvm_long_mode_enabled(vcpu); + else + return is_pv_32bit_vcpu(vcpu); +} +#endif + static struct frame_head * dump_guest_backtrace(struct domain *d, struct vcpu *vcpu, struct frame_head * head, int mode) { struct frame_head bufhead[2]; XEN_GUEST_HANDLE(char) guest_head = guest_handle_from_ptr(head, char); - - /* Also check accessibility of one struct frame_head beyond */ - if (!guest_handle_okay(guest_head, sizeof(bufhead))) - return 0; - if (__copy_from_guest_offset((char *)bufhead, guest_head, 0, - sizeof(bufhead)))
xen/arch/x86/oprofile/backtrace.c Wed Jan 18 17:35:06 2012 +0000 dump_hypervisor_backtrace(struct domain · line 69
Quoted 10 lines
xen/arch/x86/oprofile/backtrace.c Wed Jan 18 17:35:06 2012 +0000 renamed from xen/arch/x86/oprofile/backtrace.c Wed Jan 18 17:23:02 2012 +0000 +9
-1- return 0; + +#ifdef CONFIG_X86_64 + if ( is_32bit_vcpu(vcpu) ) + { + struct frame_head_32bit bufhead32[2]; + /* Also check accessibility of one struct frame_head beyond */ + if (!guest_handle_okay(guest_head, sizeof(bufhead32))) + return 0; + if (__copy_from_guest_offset((char *)bufhead32, guest_head, 0,
If you're adding a compat mode guest case here, then you should also use compat mode accessors (compat_handle_okay(), __copy_from_compat_offset()), implying that you also have a local handle variable of the appropriate type (and perhaps moving the native one down into the 'else' body).
Also, as you're touching this code anyway, the __copy_from_..._offset() here and below, as they're passing literal zero for the offset, can be abbreviated by using __copy_from_...() (i.e. without the offset).
Jan
Quoted 18 lines
xen/arch/x86/oprofile/backtrace.c Wed Jan 18 17:35:06 2012 +0000 renamed from xen/arch/x86/oprofile/backtrace.c Wed Jan 18 17:23:02 2012 +0000 +15
-0+ sizeof(bufhead32))) + return 0; + bufhead[0].ebp=(struct frame_head *)(unsigned long)bufhead32[0].ebp; + bufhead[0].ret=bufhead32[0].ret; + } + else +#endif + { + /* Also check accessibility of one struct frame_head beyond */ + if (!guest_handle_okay(guest_head, sizeof(bufhead))) + return 0; + if (__copy_from_guest_offset((char *)bufhead, guest_head, 0, + sizeof(bufhead))) + return 0; + } if (!xenoprof_add_trace(d, vcpu, bufhead[0].ret, mode)) return 0;
Quoted 5 lines · Jan Beulich
If you're adding a compat mode guest case here, then you should also use compat mode accessors (
compat_handle_okay(),__copy_from_compat_offset()), implying that you also have a local handle variable of the appropriate type (and perhaps moving the native one down into the 'else' body).
I'm trying to understand the compat handle. It is not clear to me how to map one from head (a 64-bit pointer), since COMPAT_HANDLE seems to store a 32-bit compat_ptr_t value in its structure. Ideally, what I would like to do is
COMPAT_HANDLE(char) guest_head = map_guest_handle_to_compat_handle (guest_handle_from_ptr(head, char)); orCOMPAT_HANDLE(char) guest_head = compat_handle_from_ptr(head, char));
but I can't find any equivalent functions in any header.
The following line compiles,COMPAT_HANDLE(char) guest_head = { (full_ptr_t)head };
but it looks like, in this case, the compat handle structure in compat.h will truncate the most significant bits from the head pointer, so compat_handle_okay(guest_head,...) and __copy_from_compat(...,guest_head,...) below will be using a truncated pointer:
56 static struct frame_head *
57 dump_guest_backtrace(struct domain *d, struct vcpu *vcpu,
58 struct frame_head * head, int mode)
59 {
60 struct frame_head bufhead[2];
61
62 #ifdef CONFIG_X86_64
63 if ( is_32bit_vcpu(vcpu) )
64 {
65 COMPAT_HANDLE(char) guest_head = { (full_ptr_t)head };
66 struct frame_head_32bit bufhead32[2];
67 /* Also check accessibility of one struct frame_head beyond */
68 if (!compat_handle_okay(guest_head, sizeof(bufhead32)))
69 return 0;
70 if (__copy_from_compat((char *)bufhead32, guest_head,
71 sizeof(bufhead32)))
72 return 0;
73 bufhead[0].ebp=(struct frame_head
*)(full_ptr_t)bufhead32[0].ebp;
74 bufhead[0].ret=bufhead32[0].ret;
75 }
76 else
77 #endifAny advice? Maybe the best option in this case is to avoid the compat* functions and to use the original guest* functions instead.
Thanks,
Marcus
Quoted 3 lines
Quoted 1 line
Quoted 1 line · Marcus Granado
I'm trying to understand the compat handle. It is not clear to me how to map one from head (a 64-bit pointer), since
COMPAT_HANDLEseems to store
That cannot generally be done, as a 64-bit pointer can never be represented as a compat handle.
Proper conversion has to start at where 'head' is first generated (i.e. the line
head = (struct frame_head *)regs->ebp;
in xenoprof_backtrace() (the more that here you really *want* to drop the upper 32 bits in the compat case. Working with a union is a possible approach, but it may also be acceptable to actually do the truncation in dump_guest_backtrace(), properly explaining why the dropping of the upper half is valid and intended there.
(I've already put fixing up of this already committed patch on my todo list, so feel free to drop further attempts; once I'm done I'd appreciate review/testing of the code though.)
Jan
Quoted 47 lines
a 32-bit
compat_ptr_tvalue in its structure. Ideally, what I would like to do is
COMPAT_HANDLE(char)guest_head=map_guest_handle_to_compat_handle(guest_handle_from_ptr(head, char));
orCOMPAT_HANDLE(char)guest_head=compat_handle_from_ptr(head, char)); but I can't find any equivalent functions in any header.The following line compiles,
COMPAT_HANDLE(char)guest_head= { (full_ptr_t)head };
but it looks like, in this case, the compat handle structure incompat.hwill truncate the most significant bits from the head pointer, socompat_handle_okay(guest_head,...)and__copy_from_compat(...,guest_head,...)below will be using a truncated pointer:56 static struct frame_head * 57 dump_guest_backtrace(struct domain *d, struct vcpu *vcpu, 58 struct frame_head * head, int mode) 59 { 60 struct frame_head bufhead[2]; 61 62 #ifdef CONFIG_X86_64 63 if ( is_32bit_vcpu(vcpu) ) 64 { 65 COMPAT_HANDLE(char) guest_head = { (full_ptr_t)head }; 66 struct frame_head_32bit bufhead32[2]; 67 /* Also check accessibility of one struct frame_headbeyond */
68 if (!compat_handle_okay(guest_head, sizeof(bufhead32)))
69 return 0;
70 if (__copy_from_compat((char *)bufhead32,guest_head,
71 sizeof(bufhead32)))
72 return 0;
73 bufhead[0].ebp=(structframe_head*)(full_ptr_t)bufhead32[0].ebp; 74 bufhead[0].ret=bufhead32[0].ret; 75 } 76 else 77 #endifAny advice? Maybe the best option in this case is to avoid the compat* functions and to use the original guest* functions instead.
Thanks,
Marcus