[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[xen master] x86/nospec: Fix evaluate_nospec() code generation under Clang



commit bc3c133841435829ba5c0a48427e2a77633502ab
Author:     Andrew Cooper <andrew.cooper3@xxxxxxxxxx>
AuthorDate: Mon Apr 25 17:25:53 2022 +0100
Commit:     Andrew Cooper <andrew.cooper3@xxxxxxxxxx>
CommitDate: Fri Mar 24 12:16:31 2023 +0000

    x86/nospec: Fix evaluate_nospec() code generation under Clang
    
    It turns out that evaluate_nospec() code generation is not safe under Clang.
    Given:
    
      void eval_nospec_test(int x)
      {
          if ( evaluate_nospec(x) )
              asm volatile ("nop #true" ::: "memory");
          else
              asm volatile ("nop #false" ::: "memory");
      }
    
    Clang emits:
    
      <eval_nospec_test>:
             0f ae e8                lfence
             85 ff                   test   %edi,%edi
             74 02                   je     <eval_nospec_test+0x9>
             90                      nop
             c3                      ret
             90                      nop
             c3                      ret
    
    which is not safe because the lfence has been hoisted above the conditional
    jump.  Clang concludes that both barrier_nospec_true()'s have identical side
    effects and can safely be merged.
    
    Clang can be persuaded that the side effects are different if there are
    different comments in the asm blocks.  This is fragile, but no more fragile
    that other aspects of this construct.
    
    Introduce barrier_nospec_false() with a separate internal comment to prevent
    Clang merging it with barrier_nospec_true() despite the otherwise-identical
    content.  The generated code now becomes:
    
      <eval_nospec_test>:
             85 ff                   test   %edi,%edi
             74 05                   je     <eval_nospec_test+0x9>
             0f ae e8                lfence
             90                      nop
             c3                      ret
             0f ae e8                lfence
             90                      nop
             c3                      ret
    
    which has the correct number of lfence's, and in the correct place.
    
    Link: https://github.com/llvm/llvm-project/issues/55084
    Signed-off-by: Andrew Cooper <andrew.cooper3@xxxxxxxxxx>
    Reviewed-by: Roger Pau Monné <roger.pau@xxxxxxxxxx>
    Reviewed-by: Jan Beulich <jbeulich@xxxxxxxx>
---
 xen/arch/x86/include/asm/nospec.h | 15 +++++++++++++--
 1 file changed, 13 insertions(+), 2 deletions(-)

diff --git a/xen/arch/x86/include/asm/nospec.h 
b/xen/arch/x86/include/asm/nospec.h
index 5312ae4c6f..7150e76b87 100644
--- a/xen/arch/x86/include/asm/nospec.h
+++ b/xen/arch/x86/include/asm/nospec.h
@@ -10,15 +10,26 @@
 static always_inline bool barrier_nospec_true(void)
 {
 #ifdef CONFIG_SPECULATIVE_HARDEN_BRANCH
-    alternative("lfence", "", X86_FEATURE_SC_NO_BRANCH_HARDEN);
+    alternative("lfence #nospec-true", "", X86_FEATURE_SC_NO_BRANCH_HARDEN);
 #endif
     return true;
 }
 
+static always_inline bool barrier_nospec_false(void)
+{
+#ifdef CONFIG_SPECULATIVE_HARDEN_BRANCH
+    alternative("lfence #nospec-false", "", X86_FEATURE_SC_NO_BRANCH_HARDEN);
+#endif
+    return false;
+}
+
 /* Allow to protect evaluation of conditionals with respect to speculation */
 static always_inline bool evaluate_nospec(bool condition)
 {
-    return condition ? barrier_nospec_true() : !barrier_nospec_true();
+    if ( condition )
+        return barrier_nospec_true();
+    else
+        return barrier_nospec_false();
 }
 
 /* Allow to block speculative execution in generic code */
--
generated by git-patchbot for /home/xen/git/xen.git#master



 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.