[compiler-rt] 09a8b7c - [TySan] Fix struct access with different bases (#120412)

via llvm-commits llvm-commits at lists.llvm.org
Mon Jan 13 07:28:40 PST 2025


Author: gbMattN
Date: 2025-01-13T15:28:37Z
New Revision: 09a8b7cbc29d8704c343197d4b33b6972366c682

URL: https://github.com/llvm/llvm-project/commit/09a8b7cbc29d8704c343197d4b33b6972366c682
DIFF: https://github.com/llvm/llvm-project/commit/09a8b7cbc29d8704c343197d4b33b6972366c682.diff

LOG: [TySan] Fix struct access with different bases (#120412)

Original pull request
[here](https://github.com/llvm/llvm-project/pull/108385)
Fixes issue https://github.com/llvm/llvm-project/issues/105960

If a member in a struct is also a struct, accessing a member partway
through this inner struct currently causes a false positive. This is
because when checking aliasing, the access offset is seen as greater
than the starting offset of the inner struct, so the loop continues one
iteration, and believes we are accessing the member after the inner
struct.

The next member's offset is greater than the offset we are looking for,
so when we subtract the next member's offset from what we are looking
for, the offset underflows.

To fix this, we check if the member we think we are accessing has a
greater offset than the offset we are looking for. If so, we take a step
back. We cannot do this in the loop, since the loop does not check the
final member. This means the penultimate member would still cause false
positives.

Added: 
    compiler-rt/test/tysan/struct-offset-different-base.cpp

Modified: 
    compiler-rt/lib/tysan/tysan.cpp

Removed: 
    


################################################################################
diff  --git a/compiler-rt/lib/tysan/tysan.cpp b/compiler-rt/lib/tysan/tysan.cpp
index 9c87b4782671a0..f0230df9260e3a 100644
--- a/compiler-rt/lib/tysan/tysan.cpp
+++ b/compiler-rt/lib/tysan/tysan.cpp
@@ -131,6 +131,17 @@ static bool isAliasingLegalUp(tysan_type_descriptor *TDA,
           break;
       }
 
+      // This offset can't be negative. Therefore we must be accessing something
+      // before the current type (not legal) or partially inside the last type.
+      // In the latter case, we adjust Idx.
+      if (TDA->Struct.Members[Idx].Offset > OffsetA) {
+        // Trying to access something before the current type.
+        if (!Idx)
+          return false;
+
+        Idx -= 1;
+      }
+
       OffsetA -= TDA->Struct.Members[Idx].Offset;
       TDA = TDA->Struct.Members[Idx].Type;
     } else {

diff  --git a/compiler-rt/test/tysan/struct-offset-
diff erent-base.cpp b/compiler-rt/test/tysan/struct-offset-
diff erent-base.cpp
new file mode 100644
index 00000000000000..862595de8dc81f
--- /dev/null
+++ b/compiler-rt/test/tysan/struct-offset-
diff erent-base.cpp
@@ -0,0 +1,49 @@
+// RUN: %clangxx_tysan -O0 %s -o %t && %run %t >%t.out 2>&1
+// RUN: FileCheck %s --implicit-check-not ERROR < %t.out
+
+// Modified reproducer from https://github.com/llvm/llvm-project/issues/105960
+
+#include <stdio.h>
+
+struct inner1 {
+  char buffer;
+  int i;
+};
+
+struct inner2 {
+  char buffer;
+  int i;
+  float endBuffer;
+};
+
+void init_inner1(inner1 *iPtr) { iPtr->i = 200; }
+void init_inner2(inner2 *iPtr) {
+  iPtr->i = 400;
+  iPtr->endBuffer = 413.0f;
+}
+
+struct outer {
+  inner1 foo;
+  inner2 bar;
+  char buffer;
+};
+
+int main(void) {
+  outer *l = new outer();
+
+  init_inner1(&l->foo);
+  init_inner2(&l->bar);
+
+  int access = l->foo.i;
+  printf("Accessed value 1 is %d\n", access);
+  access = l->bar.i;
+  printf("Accessed value 2 is %d\n", access);
+  float fAccess = l->bar.endBuffer;
+  printf("Accessed value 3 is %f\n", fAccess);
+
+  return 0;
+}
+
+// CHECK: Accessed value 1 is 200
+// CHECK: Accessed value 2 is 400
+// CHECK: Accessed value 3 is 413.0


        


More information about the llvm-commits mailing list