Skip to content

[analyzer] Fix StreamChecker crash in fread modeling #108393

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

Merged
merged 2 commits into from
Sep 12, 2024
Merged

[analyzer] Fix StreamChecker crash in fread modeling #108393

merged 2 commits into from
Sep 12, 2024

Conversation

steakhal
Copy link
Contributor

In #93408 69bc159
I refined how invalidation is done for fread. It can crash, if the "size" or "count" parameters of "fread" is a perfectly constrained negative value. In such cases, when it will try to allocate a SmallVector with a negative size, which will cause a crash.

To mitigate this issue, let's just guard against negative values.

CPP-3247

In #93408
69bc159

I refined how invalidation is done for `fread`. It can crash, if the
"size" or "count" parameters of "fread" is a perfectly contrained
negative value. In such cases, when it will try to allocate a
SmallVEctor with a negative size, which will cause a crash.

To mitigate this issue, let's just guard against negative values.

CPP-3247
@steakhal steakhal requested a review from NagyDonat September 12, 2024 13:44
@llvmbot llvmbot added the clang Clang issues not falling into any other category label Sep 12, 2024
@llvmbot
Copy link
Member

llvmbot commented Sep 12, 2024

@llvm/pr-subscribers-clang

@llvm/pr-subscribers-clang-static-analyzer-1

Author: Balazs Benics (steakhal)

Changes

In #93408 69bc159
I refined how invalidation is done for fread. It can crash, if the "size" or "count" parameters of "fread" is a perfectly constrained negative value. In such cases, when it will try to allocate a SmallVector with a negative size, which will cause a crash.

To mitigate this issue, let's just guard against negative values.

CPP-3247


Full diff: https://github.com/llvm/llvm-project/pull/108393.diff

2 Files Affected:

  • (modified) clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp (+1-1)
  • (modified) clang/test/Analysis/fread.c (+30)
diff --git a/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp
index 22061373c4b393..8bb7880a3cc283 100644
--- a/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp
@@ -1129,7 +1129,7 @@ tryToInvalidateFReadBufferByElements(ProgramStateRef State, CheckerContext &C,
   if (!ElemTy.isNull() && CountVal && Size && StartIndexVal) {
     int64_t NumBytesRead = Size.value() * CountVal.value();
     int64_t ElemSizeInChars = Ctx.getTypeSizeInChars(ElemTy).getQuantity();
-    if (ElemSizeInChars == 0)
+    if (ElemSizeInChars == 0 || NumBytesRead < 0)
       return nullptr;
 
     bool IncompleteLastElement = (NumBytesRead % ElemSizeInChars) != 0;
diff --git a/clang/test/Analysis/fread.c b/clang/test/Analysis/fread.c
index 3f286421fd7a13..d470f0abebe621 100644
--- a/clang/test/Analysis/fread.c
+++ b/clang/test/Analysis/fread.c
@@ -443,3 +443,33 @@ void test_unaligned_start_read(void) {
     fclose(fp);
   }
 }
+
+void no_crash_if_count_is_negative(long s, unsigned char *buffer) {
+  FILE *fp = fopen("path", "r");
+  if (fp) {
+    if (s * s == -1) {
+      fread(buffer, 1, s * s, fp); // no-crash
+    }
+    fclose(fp);
+  }
+}
+
+void no_crash_if_size_is_negative(long s, unsigned char *buffer) {
+  FILE *fp = fopen("path", "r");
+  if (fp) {
+    if (s * s == -1) {
+      fread(buffer, s * s, 1, fp); // no-crash
+    }
+    fclose(fp);
+  }
+}
+
+void no_crash_if_size_and_count_are_negative(long s, unsigned char *buffer) {
+  FILE *fp = fopen("path", "r");
+  if (fp) {
+    if (s * s == -1) {
+      fread(buffer, s * s, s * s, fp); // no-crash
+    }
+    fclose(fp);
+  }
+}

@steakhal
Copy link
Contributor Author

I'm debating if we should backport this to clang-19, but I'd say this crash does not happen too often in real life to advocate for it. So I don't intend to backport this, unless someone asks for it.

Copy link
Contributor

@NagyDonat NagyDonat left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, thanks for catching this!

void no_crash_if_count_is_negative(long s, unsigned char *buffer) {
FILE *fp = fopen("path", "r");
if (fp) {
if (s * s == -1) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nitpick: $s^2 = -1$ is mathematically impossible (even if we allow overflows: 0xffff...f $= (4^{k})^2 - 1$ where $k$ is the number of f digits and two consecutive integers that are >1 cannot be both perfect squares). I know that the static analyzer doesn't know this and will happily assume that s * s == -1 -- but it would be still nicer to use a different example.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yea, I noticed that too. I figured it's a funny example, but refining it is also good.

@steakhal steakhal merged commit 2e30f8d into llvm:main Sep 12, 2024
8 checks passed
@steakhal steakhal deleted the bb/fix-stream-checker-crash branch September 12, 2024 15:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
clang:static analyzer clang Clang issues not falling into any other category
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants