https://github.com/steakhal updated 
https://github.com/llvm/llvm-project/pull/97199

>From 6eeab014b09cfa0909c3ccb1b2d3e6fadadb983f Mon Sep 17 00:00:00 2001
From: Balazs Benics <benicsbal...@gmail.com>
Date: Mon, 1 Jul 2024 17:29:25 +0200
Subject: [PATCH 1/5] [analyzer] Fix crash in Stream checker when using void
 pointers

We can get zero type size (thus div by zero crash) if the region is for
a 'void*' pointer.
In this patch, let's just override the void type with a char type to
avoid the crash.

Fixes
https://github.com/llvm/llvm-project/pull/93408#issuecomment-2189766510
---
 .../StaticAnalyzer/Checkers/StreamChecker.cpp | 39 ++++++++++++-------
 clang/test/Analysis/stream.c                  | 13 +++++++
 2 files changed, 38 insertions(+), 14 deletions(-)

diff --git a/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp 
b/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp
index 9aee7f952ad2d..0d1d50d19f4c4 100644
--- a/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp
@@ -1034,16 +1034,19 @@ void StreamChecker::preWrite(const FnDescription *Desc, 
const CallEvent &Call,
   C.addTransition(State);
 }
 
-static std::optional<QualType> getPointeeType(const MemRegion *R) {
+static QualType getPointeeType(const MemRegion *R) {
   if (!R)
-    return std::nullopt;
-  if (const auto *ER = dyn_cast<ElementRegion>(R))
-    return ER->getElementType();
-  if (const auto *TR = dyn_cast<TypedValueRegion>(R))
-    return TR->getValueType();
-  if (const auto *SR = dyn_cast<SymbolicRegion>(R))
-    return SR->getPointeeStaticType();
-  return std::nullopt;
+    return {};
+  QualType Ty = [R] {
+    if (const auto *ER = dyn_cast<ElementRegion>(R))
+      return ER->getElementType();
+    if (const auto *TR = dyn_cast<TypedValueRegion>(R))
+      return TR->getValueType();
+    if (const auto *SR = dyn_cast<SymbolicRegion>(R))
+      return SR->getPointeeStaticType();
+    return QualType{};
+  }();
+  return !Ty.isNull() ? Ty->getCanonicalTypeUnqualified() : QualType{};
 }
 
 static std::optional<NonLoc> getStartIndex(SValBuilder &SVB,
@@ -1073,7 +1076,16 @@ tryToInvalidateFReadBufferByElements(ProgramStateRef 
State, CheckerContext &C,
   const auto *Buffer =
       dyn_cast_or_null<SubRegion>(Call.getArgSVal(0).getAsRegion());
 
-  std::optional<QualType> ElemTy = getPointeeType(Buffer);
+  const ASTContext &Ctx = C.getASTContext();
+
+  QualType ElemTy = getPointeeType(Buffer);
+
+  // Consider pointer to void as a pointer to char buffer such that it has a
+  // non-zero type size.
+  if (!ElemTy.isNull() && ElemTy == Ctx.VoidTy) {
+    ElemTy = Ctx.CharTy;
+  }
+
   std::optional<SVal> StartElementIndex =
       getStartIndex(C.getSValBuilder(), Buffer);
 
@@ -1086,10 +1098,9 @@ tryToInvalidateFReadBufferByElements(ProgramStateRef 
State, CheckerContext &C,
   std::optional<int64_t> StartIndexVal =
       getKnownValue(State, StartElementIndex.value_or(UnknownVal()));
 
-  if (ElemTy && CountVal && Size && StartIndexVal) {
+  if (!ElemTy.isNull() && CountVal && Size && StartIndexVal) {
     int64_t NumBytesRead = Size.value() * CountVal.value();
-    int64_t ElemSizeInChars =
-        C.getASTContext().getTypeSizeInChars(*ElemTy).getQuantity();
+    int64_t ElemSizeInChars = Ctx.getTypeSizeInChars(ElemTy).getQuantity();
     bool IncompleteLastElement = (NumBytesRead % ElemSizeInChars) != 0;
     int64_t NumCompleteOrIncompleteElementsRead =
         NumBytesRead / ElemSizeInChars + IncompleteLastElement;
@@ -1097,7 +1108,7 @@ tryToInvalidateFReadBufferByElements(ProgramStateRef 
State, CheckerContext &C,
     constexpr int MaxInvalidatedElementsLimit = 64;
     if (NumCompleteOrIncompleteElementsRead <= MaxInvalidatedElementsLimit) {
       return escapeByStartIndexAndCount(State, Call, C.blockCount(), Buffer,
-                                        *ElemTy, *StartIndexVal,
+                                        ElemTy, *StartIndexVal,
                                         NumCompleteOrIncompleteElementsRead);
     }
   }
diff --git a/clang/test/Analysis/stream.c b/clang/test/Analysis/stream.c
index db03d90cd8af4..0869b1b325172 100644
--- a/clang/test/Analysis/stream.c
+++ b/clang/test/Analysis/stream.c
@@ -453,3 +453,16 @@ void getline_buffer_size_negative() {
   free(buffer);
   fclose(file);
 }
+
+void gh_93408_regression(void *buffer) {
+  FILE *f = fopen("/tmp/foo.txt", "r");
+  fread(buffer, 1, 1, f); // expected-warning {{Stream pointer might be NULL}} 
no-crash: void is treated as char.
+  fclose(f);
+}
+
+typedef void VOID;
+void gh_93408_regression_typedef(VOID *buffer) {
+  FILE *f = fopen("/tmp/foo.txt", "r");
+  fread(buffer, 1, 1, f); // expected-warning {{Stream pointer might be NULL}} 
no-crash: VOID is treated as char.
+  fclose(f);
+}

>From 4b26e6fb59e4794f12faa7afd6353e4db6d7fba5 Mon Sep 17 00:00:00 2001
From: Balazs Benics <benicsbal...@gmail.com>
Date: Mon, 1 Jul 2024 17:29:25 +0200
Subject: [PATCH 2/5] Prefer directly checking against 0

---
 .../StaticAnalyzer/Checkers/StreamChecker.cpp | 11 ++----
 clang/test/Analysis/stream.c                  | 36 +++++++++++++++++--
 2 files changed, 37 insertions(+), 10 deletions(-)

diff --git a/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp 
b/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp
index 0d1d50d19f4c4..530d971fc5305 100644
--- a/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp
@@ -1077,15 +1077,7 @@ tryToInvalidateFReadBufferByElements(ProgramStateRef 
State, CheckerContext &C,
       dyn_cast_or_null<SubRegion>(Call.getArgSVal(0).getAsRegion());
 
   const ASTContext &Ctx = C.getASTContext();
-
   QualType ElemTy = getPointeeType(Buffer);
-
-  // Consider pointer to void as a pointer to char buffer such that it has a
-  // non-zero type size.
-  if (!ElemTy.isNull() && ElemTy == Ctx.VoidTy) {
-    ElemTy = Ctx.CharTy;
-  }
-
   std::optional<SVal> StartElementIndex =
       getStartIndex(C.getSValBuilder(), Buffer);
 
@@ -1101,6 +1093,9 @@ 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)
+      return nullptr;
+
     bool IncompleteLastElement = (NumBytesRead % ElemSizeInChars) != 0;
     int64_t NumCompleteOrIncompleteElementsRead =
         NumBytesRead / ElemSizeInChars + IncompleteLastElement;
diff --git a/clang/test/Analysis/stream.c b/clang/test/Analysis/stream.c
index 0869b1b325172..c924cbd36f759 100644
--- a/clang/test/Analysis/stream.c
+++ b/clang/test/Analysis/stream.c
@@ -456,13 +456,45 @@ void getline_buffer_size_negative() {
 
 void gh_93408_regression(void *buffer) {
   FILE *f = fopen("/tmp/foo.txt", "r");
-  fread(buffer, 1, 1, f); // expected-warning {{Stream pointer might be NULL}} 
no-crash: void is treated as char.
+  fread(buffer, 1, 1, f); // expected-warning {{Stream pointer might be NULL}} 
no-crash
   fclose(f);
 }
 
 typedef void VOID;
 void gh_93408_regression_typedef(VOID *buffer) {
   FILE *f = fopen("/tmp/foo.txt", "r");
-  fread(buffer, 1, 1, f); // expected-warning {{Stream pointer might be NULL}} 
no-crash: VOID is treated as char.
+  fread(buffer, 1, 1, f); // expected-warning {{Stream pointer might be NULL}} 
no-crash
+  fclose(f);
+}
+
+struct FAM {
+  int data;
+  int tail[];
+};
+
+struct FAM0 {
+  int data;
+  int tail[0];
+};
+
+void gh_93408_regression_FAM(struct FAM *p) {
+  FILE *f = fopen("/tmp/foo.txt", "r");
+  fread(p->tail, 1, 1, f); // expected-warning {{Stream pointer might be NULL}}
+  fclose(f);
+}
+
+void gh_93408_regression_FAM0(struct FAM0 *p) {
+  FILE *f = fopen("/tmp/foo.txt", "r");
+  fread(p->tail, 1, 1, f); // expected-warning {{Stream pointer might be NULL}}
+  fclose(f);
+}
+
+struct ZeroSized {
+    int data[0];
+};
+
+void gh_93408_regression_ZeroSized(struct ZeroSized *buffer) {
+  FILE *f = fopen("/tmp/foo.txt", "r");
+  fread(buffer, 1, 1, f); // expected-warning {{Stream pointer might be NULL}} 
no-crash
   fclose(f);
 }

>From 0f1ad42a0d5b16d7f44178e29490af3fcdfd7bad Mon Sep 17 00:00:00 2001
From: Balazs Benics <benicsbal...@gmail.com>
Date: Mon, 1 Jul 2024 17:29:25 +0200
Subject: [PATCH 3/5] Replace immediately invoked lambda by duplicating code

---
 .../StaticAnalyzer/Checkers/StreamChecker.cpp   | 17 +++++++----------
 1 file changed, 7 insertions(+), 10 deletions(-)

diff --git a/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp 
b/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp
index 530d971fc5305..b1769081764d5 100644
--- a/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp
@@ -1037,16 +1037,13 @@ void StreamChecker::preWrite(const FnDescription *Desc, 
const CallEvent &Call,
 static QualType getPointeeType(const MemRegion *R) {
   if (!R)
     return {};
-  QualType Ty = [R] {
-    if (const auto *ER = dyn_cast<ElementRegion>(R))
-      return ER->getElementType();
-    if (const auto *TR = dyn_cast<TypedValueRegion>(R))
-      return TR->getValueType();
-    if (const auto *SR = dyn_cast<SymbolicRegion>(R))
-      return SR->getPointeeStaticType();
-    return QualType{};
-  }();
-  return !Ty.isNull() ? Ty->getCanonicalTypeUnqualified() : QualType{};
+  if (const auto *ER = dyn_cast<ElementRegion>(R))
+    return ER->getElementType()->getCanonicalTypeUnqualified();
+  if (const auto *TR = dyn_cast<TypedValueRegion>(R))
+    return TR->getValueType()->getCanonicalTypeUnqualified();
+  if (const auto *SR = dyn_cast<SymbolicRegion>(R))
+    return SR->getPointeeStaticType()->getCanonicalTypeUnqualified();
+  return QualType{};
 }
 
 static std::optional<NonLoc> getStartIndex(SValBuilder &SVB,

>From 10ec54a666cf5d92f63594ab443986968f7d7905 Mon Sep 17 00:00:00 2001
From: Balazs Benics <benicsbal...@gmail.com>
Date: Mon, 1 Jul 2024 17:29:25 +0200
Subject: [PATCH 4/5] Harmonize return paths

---
 clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp 
b/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp
index b1769081764d5..96e74478c4797 100644
--- a/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp
@@ -1043,7 +1043,7 @@ static QualType getPointeeType(const MemRegion *R) {
     return TR->getValueType()->getCanonicalTypeUnqualified();
   if (const auto *SR = dyn_cast<SymbolicRegion>(R))
     return SR->getPointeeStaticType()->getCanonicalTypeUnqualified();
-  return QualType{};
+  return {};
 }
 
 static std::optional<NonLoc> getStartIndex(SValBuilder &SVB,

>From fdf2dfdc1483477da665776421daaf896c5d18d3 Mon Sep 17 00:00:00 2001
From: Balazs Benics <benicsbal...@gmail.com>
Date: Mon, 1 Jul 2024 17:29:25 +0200
Subject: [PATCH 5/5] Drop the getCanonicalTypeUnqualified calls

---
 clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp 
b/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp
index 96e74478c4797..e8d538388e56c 100644
--- a/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp
@@ -1038,11 +1038,11 @@ static QualType getPointeeType(const MemRegion *R) {
   if (!R)
     return {};
   if (const auto *ER = dyn_cast<ElementRegion>(R))
-    return ER->getElementType()->getCanonicalTypeUnqualified();
+    return ER->getElementType();
   if (const auto *TR = dyn_cast<TypedValueRegion>(R))
-    return TR->getValueType()->getCanonicalTypeUnqualified();
+    return TR->getValueType();
   if (const auto *SR = dyn_cast<SymbolicRegion>(R))
-    return SR->getPointeeStaticType()->getCanonicalTypeUnqualified();
+    return SR->getPointeeStaticType();
   return {};
 }
 

_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to