Skip to content

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