Message ID | 20220803172508.1215-4-mhal@rbox.co (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Series | [kvm-unit-tests,1/4] x86: emulator.c cleanup: Save and restore exception handlers | expand |
On Wed, Aug 03, 2022, Michal Luczaj wrote: > TRY_ASM() mishandles #UD thrown by the forced-emulation-triggered emulator. > While the faulting address stored in the exception table points at forced > emulation prefix, when #UD comes, RIP is 5 bytes (size of KVM_FEP) ahead > and the exception ends up unhandled. Ah, but that's only the behavior if FEP is allowed. If FEP is disabled, then the #UD will be on the prefix. > Suggested-by: Sean Christopherson <seanjc@google.com> Heh, I didn't really suggest this because I didn't even realize it was a problem :-) > Signed-off-by: Michal Luczaj <mhal@rbox.co> > --- > While here, I've also took the opportunity to merge both 32 and 64-bit > versions of ASM_TRY() (.dc.a for .long and .quad), but perhaps there > were some reasons for not using .dc.a? This should be a separate patch, and probably as the very last patch in case dc.a isn't viable for whatever reason. I've never seen/used dc.a so I really have no idea whether or not it's ok to use. As a "safe" alternative that can be done before adding ASM_TRY_FEP(), we can steal the kernel's __ASM_SEL() approach so that you don't have to implement two versions of ASM_TRY_FEP(). That's worth doing because __ASM_SEL() can probably be used in other places too. Patch at the bottom. > lib/x86/desc.h | 11 +++++------ > x86/emulator.c | 4 ++-- > 2 files changed, 7 insertions(+), 8 deletions(-) > > diff --git a/lib/x86/desc.h b/lib/x86/desc.h > index 2a285eb..99cc224 100644 > --- a/lib/x86/desc.h > +++ b/lib/x86/desc.h > @@ -80,21 +80,20 @@ typedef struct __attribute__((packed)) { > u16 iomap_base; > } tss64_t; > > -#ifdef __x86_64 > #define ASM_TRY(catch) \ > "movl $0, %%gs:4 \n\t" \ > ".pushsection .data.ex \n\t" \ > - ".quad 1111f, " catch "\n\t" \ > + ".dc.a 1111f, " catch "\n\t" \ > ".popsection \n\t" \ > "1111:" > -#else > -#define ASM_TRY(catch) \ > + > +#define ASM_TRY_PREFIXED(prefix, catch) \ Rather than a generic ASM_TRY_PREFIXED, I think it makes sense to add an explicit ASM_TRY_FEP() that takes in only the label and hardcodes the FEP prefix. The "#UD skips the prefix" behavior is unique to "successful" forced emulation, so this is literally the only scenario where skipping a prefix is expected/correct. *sigh* That'll require moving the KVM_FEP definition, but that's a good change on its own. Probably throw it in lib/x86/processor.h? > "movl $0, %%gs:4 \n\t" \ > ".pushsection .data.ex \n\t" \ > - ".long 1111f, " catch "\n\t" \ > + ".dc.a 1111f, " catch "\n\t" \ > ".popsection \n\t" \ > + prefix "\n\t" \ > "1111:" > -#endif > > /* > * selector 32-bit 64-bit > diff --git a/x86/emulator.c b/x86/emulator.c > index df0bc49..d2a5302 100644 > --- a/x86/emulator.c > +++ b/x86/emulator.c > @@ -900,8 +900,8 @@ static void test_illegal_lea(void) > { > unsigned int vector; > > - asm volatile (ASM_TRY("1f") > - KVM_FEP ".byte 0x8d; .byte 0xc0\n\t" > + asm volatile (ASM_TRY_PREFIXED(KVM_FEP, "1f") > + ".byte 0x8d; .byte 0xc0\n\t" Put this patch earlier in the series, before test_illegal_lea() is introduced. Both so that there's no unnecessary churn, and also because running the illegal LEA testcase without this patch will fail. > "1:" > : : : "memory", "eax"); --- From: Sean Christopherson <seanjc@google.com> Date: Wed, 3 Aug 2022 11:09:41 -0700 Subject: [PATCH] x86: Dedup 32-bit vs. 64-bit ASM_TRY() by stealing kernel's __ASM_SEL() Steal the kernel's __ASM_SEL() implementation and use it to consolidate ASM_TRY(). The only difference between the 32-bit and 64-bit versions is the size of the address stored in the table. No functional change intended. Signed-off-by: Sean Christopherson <seanjc@google.com> --- lib/x86/desc.h | 19 +++++-------------- lib/x86/processor.h | 12 ++++++++++++ 2 files changed, 17 insertions(+), 14 deletions(-) diff --git a/lib/x86/desc.h b/lib/x86/desc.h index 2a285eb6..e670ebf2 100644 --- a/lib/x86/desc.h +++ b/lib/x86/desc.h @@ -80,21 +80,12 @@ typedef struct __attribute__((packed)) { u16 iomap_base; } tss64_t; -#ifdef __x86_64 -#define ASM_TRY(catch) \ - "movl $0, %%gs:4 \n\t" \ - ".pushsection .data.ex \n\t" \ - ".quad 1111f, " catch "\n\t" \ - ".popsection \n\t" \ +#define ASM_TRY(catch) \ + "movl $0, %%gs:4 \n\t" \ + ".pushsection .data.ex \n\t" \ + __ASM_SEL(.long, .quad) " 1111f, " catch "\n\t" \ + ".popsection \n\t" \ "1111:" -#else -#define ASM_TRY(catch) \ - "movl $0, %%gs:4 \n\t" \ - ".pushsection .data.ex \n\t" \ - ".long 1111f, " catch "\n\t" \ - ".popsection \n\t" \ - "1111:" -#endif /* * selector 32-bit 64-bit diff --git a/lib/x86/processor.h b/lib/x86/processor.h index 03242206..30e2de87 100644 --- a/lib/x86/processor.h +++ b/lib/x86/processor.h @@ -19,6 +19,18 @@ # define S "4" #endif +#ifdef __ASSEMBLY__ +#define __ASM_FORM(x, ...) x,## __VA_ARGS__ +#else +#define __ASM_FORM(x, ...) " " xstr(x,##__VA_ARGS__) " " +#endif + +#ifndef __x86_64__ +#define __ASM_SEL(a,b) __ASM_FORM(a) +#else +#define __ASM_SEL(a,b) __ASM_FORM(b) +#endif + #define DB_VECTOR 1 #define BP_VECTOR 3 #define UD_VECTOR 6 base-commit: a106b30d39425b7afbaa3bbd4aab16fd26d333e7 --
On 8/3/22 20:16, Sean Christopherson wrote: >> While here, I've also took the opportunity to merge both 32 and 64-bit >> versions of ASM_TRY() (.dc.a for .long and .quad), but perhaps there >> were some reasons for not using .dc.a? > This should be a separate patch, and probably as the very last patch in case dc.a > isn't viable for whatever reason. I've never seen/used dc.a so I really have no > idea whether or not it's ok to use. Yes, for now I'll squash this, which is similar to Michal's idea but using the trusty double underscore prefix: diff --git a/lib/x86/desc.h b/lib/x86/desc.h index 2a285eb..5b21820 100644 --- a/lib/x86/desc.h +++ b/lib/x86/desc.h @@ -81,11 +81,12 @@ typedef struct __attribute__((packed)) { } tss64_t; #ifdef __x86_64 -#define ASM_TRY(catch) \ +#define __ASM_TRY(prefix, catch) \ "movl $0, %%gs:4 \n\t" \ ".pushsection .data.ex \n\t" \ ".quad 1111f, " catch "\n\t" \ ".popsection \n\t" \ + prefix "\n\t" \ "1111:" #else #define ASM_TRY(catch) \ @@ -96,6 +97,8 @@ typedef struct __attribute__((packed)) { "1111:" #endif +#define ASM_TRY(catch) __ASM_TRY("", catch) + /* * selector 32-bit 64-bit * 0x00 NULL descriptor NULL descriptor diff --git a/x86/emulator.c b/x86/emulator.c index df0bc49..6d2f166 100644 --- a/x86/emulator.c +++ b/x86/emulator.c @@ -19,6 +19,7 @@ static int exceptions; /* Forced emulation prefix, used to invoke the emulator unconditionally. */ #define KVM_FEP "ud2; .byte 'k', 'v', 'm';" +#define ASM_TRY_FEP(catch) __ASM_TRY(KVM_FEP, catch) struct regs { u64 rax, rbx, rcx, rdx; @@ -900,8 +901,8 @@ static void test_illegal_lea(void) { unsigned int vector; - asm volatile (ASM_TRY("1f") - KVM_FEP ".byte 0x8d; .byte 0xc0\n\t" + asm volatile (ASM_TRY_FEP("1f") + ".byte 0x8d; .byte 0xc0\n\t" "1:" : : : "memory", "eax"); and the __ASM_SEL() idea can be sent as a separate patch.
diff --git a/lib/x86/desc.h b/lib/x86/desc.h index 2a285eb..99cc224 100644 --- a/lib/x86/desc.h +++ b/lib/x86/desc.h @@ -80,21 +80,20 @@ typedef struct __attribute__((packed)) { u16 iomap_base; } tss64_t; -#ifdef __x86_64 #define ASM_TRY(catch) \ "movl $0, %%gs:4 \n\t" \ ".pushsection .data.ex \n\t" \ - ".quad 1111f, " catch "\n\t" \ + ".dc.a 1111f, " catch "\n\t" \ ".popsection \n\t" \ "1111:" -#else -#define ASM_TRY(catch) \ + +#define ASM_TRY_PREFIXED(prefix, catch) \ "movl $0, %%gs:4 \n\t" \ ".pushsection .data.ex \n\t" \ - ".long 1111f, " catch "\n\t" \ + ".dc.a 1111f, " catch "\n\t" \ ".popsection \n\t" \ + prefix "\n\t" \ "1111:" -#endif /* * selector 32-bit 64-bit diff --git a/x86/emulator.c b/x86/emulator.c index df0bc49..d2a5302 100644 --- a/x86/emulator.c +++ b/x86/emulator.c @@ -900,8 +900,8 @@ static void test_illegal_lea(void) { unsigned int vector; - asm volatile (ASM_TRY("1f") - KVM_FEP ".byte 0x8d; .byte 0xc0\n\t" + asm volatile (ASM_TRY_PREFIXED(KVM_FEP, "1f") + ".byte 0x8d; .byte 0xc0\n\t" "1:" : : : "memory", "eax");
TRY_ASM() mishandles #UD thrown by the forced-emulation-triggered emulator. While the faulting address stored in the exception table points at forced emulation prefix, when #UD comes, RIP is 5 bytes (size of KVM_FEP) ahead and the exception ends up unhandled. Suggested-by: Sean Christopherson <seanjc@google.com> Signed-off-by: Michal Luczaj <mhal@rbox.co> --- While here, I've also took the opportunity to merge both 32 and 64-bit versions of ASM_TRY() (.dc.a for .long and .quad), but perhaps there were some reasons for not using .dc.a? lib/x86/desc.h | 11 +++++------ x86/emulator.c | 4 ++-- 2 files changed, 7 insertions(+), 8 deletions(-)