Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

UBSan false positive: runtime error: load of address with insufficient space for an object of type 'int' #111709

Open
hvdijk opened this issue Oct 9, 2024 · 2 comments · May be fixed by #111827
Assignees
Labels
compiler-rt:ubsan Undefined behavior sanitizer false-positive Warning fires when it should not miscompilation

Comments

@hvdijk
Copy link
Contributor

hvdijk commented Oct 9, 2024

Please consider this small reduced testcase:

int x;
int main(void) {
  int array[4] = {0};
  int *ptr;
  if (x) {
    ptr = 0;
  } else {
    ptr = array + 4;
  }
  return ptr[-1];
}

This, as far as I know, is a strictly conforming C program. But Clang's UBSan rejects it as of version 15:

$ clang-14 test.c -O3 -o test -fsanitize=undefined && ./test
$ clang-15 test.c -O3 -o test -fsanitize=undefined && ./test
test.c:10:10: runtime error: load of address 0xffffd0f236dc with insufficient space for an object of type 'int'
0xffffd0f236dc: note: pointer points here
  00 00 00 00 00 00 00 00  00 38 f2 d0 ff ff 00 00  c4 84 f1 eb 72 e1 00 00  78 38 f2 d0 ff ff 00 00
              ^
SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior test.c:10:10 in

This continues to be rejected in all later versions too. From what I can tell debugging, it's going wrong in llvm::lowerObjectSizeCall: this sets EvalOptions.EvalMode = ObjectSizeOpts::Mode::ExactSizeFromOffset. ExactSizeFromOffset allows combining SizeOffsetValues if the sizes are the same, even if the offsets are different. So, we combine "0 bytes into a 0-byte-sized object" and "16 bytes into a 16-byte-sized object" into just "0 bytes into a 0-byte-sized object". But that is wrong when we then use that to conclude that we cannot access any bytes before that object.

If I change LLVM as

diff --git a/llvm/lib/Analysis/MemoryBuiltins.cpp b/llvm/lib/Analysis/MemoryBuiltins.cpp
index e1abf5e4d885..8abbb074647c 100644
--- a/llvm/lib/Analysis/MemoryBuiltins.cpp
+++ b/llvm/lib/Analysis/MemoryBuiltins.cpp
@@ -610,7 +610,7 @@ Value *llvm::lowerObjectSizeCall(
     EvalOptions.EvalMode =
         MaxVal ? ObjectSizeOpts::Mode::Max : ObjectSizeOpts::Mode::Min;
   else
-    EvalOptions.EvalMode = ObjectSizeOpts::Mode::ExactSizeFromOffset;
+    EvalOptions.EvalMode = ObjectSizeOpts::Mode::ExactUnderlyingSizeAndOffset;

   EvalOptions.NullIsUnknownSize =
       cast<ConstantInt>(ObjectSize->getArgOperand(2))->isOne();

then this program gets accepted. But I expect this change to not be right, I expect this to result in poorer codegen where lowerObjectSizeCall is already doing the right thing.

@hvdijk hvdijk added clang Clang issues not falling into any other category compiler-rt:ubsan Undefined behavior sanitizer miscompilation labels Oct 9, 2024
@EugeneZelenko EugeneZelenko added false-positive Warning fires when it should not and removed clang Clang issues not falling into any other category labels Oct 9, 2024
@hvdijk
Copy link
Contributor Author

hvdijk commented Oct 9, 2024

This happened ever since PHI node handling was added in D121897 d8e0a6d, cc @serge-sans-paille & @nikic

@serge-sans-paille serge-sans-paille self-assigned this Oct 9, 2024
@serge-sans-paille
Copy link
Collaborator

I managed to reproduce and analyze the issue. I have a working patch but I need to test it some more, keep in touch.

serge-sans-paille added a commit to serge-sans-paille/llvm-project that referenced this issue Oct 10, 2024
…and Select/Phi

When picking a SizeOffsetAPInt through combineSizeOffset, the behavior
differs if we're going to apply a constant offset that's positive or negative:
If it's positive, then we need to compare the remaining bytes (i.e. Size
- Offset), but if it's negative, we need to compare the preceding bytes
  (i.e. Offset).

Fix llvm#111709
serge-sans-paille added a commit to serge-sans-paille/llvm-project that referenced this issue Oct 10, 2024
…and Select/Phi

When picking a SizeOffsetAPInt through combineSizeOffset, the behavior
differs if we're going to apply a constant offset that's positive or negative:
If it's positive, then we need to compare the remaining bytes (i.e. Size
- Offset), but if it's negative, we need to compare the preceding bytes
  (i.e. Offset).

Fix llvm#111709
serge-sans-paille added a commit to serge-sans-paille/llvm-project that referenced this issue Oct 10, 2024
…and Select/Phi

When picking a SizeOffsetAPInt through combineSizeOffset, the current
constant offset that has been extracted should be taken into account to
perform the right decision, otherwise in the presence of nullptr, we may
perform the wrong decision.

Fix llvm#111709
serge-sans-paille added a commit to serge-sans-paille/llvm-project that referenced this issue Oct 13, 2024
…and Select/Phi

When picking a SizeOffsetAPInt through combineSizeOffset, the current
constant offset that has been extracted should be taken into account to
perform the right decision, otherwise in the presence of nullptr, we may
perform the wrong decision.

Fix llvm#111709
serge-sans-paille added a commit to serge-sans-paille/llvm-project that referenced this issue Oct 14, 2024
…and Select/Phi

When picking a SizeOffsetAPInt through combineSizeOffset, the current
constant offset that has been extracted should be taken into account to
perform the right decision, otherwise in the presence of nullptr, we may
perform the wrong decision.

Fix llvm#111709
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
compiler-rt:ubsan Undefined behavior sanitizer false-positive Warning fires when it should not miscompilation
Projects
None yet
3 participants