Skip to content

[clang][analyzer] Add more notes to PointerSubChecker #102432

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

Closed
wants to merge 1 commit into from

Conversation

balazske
Copy link
Collaborator

@balazske balazske commented Aug 8, 2024

Notes appear at out-of-range array index for index value and array size.

Notes appear at out-of-range array index for index value and array size.
@balazske balazske requested review from gamesh411 and NagyDonat August 8, 2024 07:52
@llvmbot llvmbot added clang Clang issues not falling into any other category clang:static analyzer labels Aug 8, 2024
@llvmbot
Copy link
Member

llvmbot commented Aug 8, 2024

@llvm/pr-subscribers-clang

Author: Balázs Kéri (balazske)

Changes

Notes appear at out-of-range array index for index value and array size.


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

2 Files Affected:

  • (modified) clang/lib/StaticAnalyzer/Checkers/PointerSubChecker.cpp (+23-7)
  • (modified) clang/test/Analysis/pointer-sub.c (+25-7)
diff --git a/clang/lib/StaticAnalyzer/Checkers/PointerSubChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/PointerSubChecker.cpp
index b856b0edc61514..1b215cc59c95ae 100644
--- a/clang/lib/StaticAnalyzer/Checkers/PointerSubChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/PointerSubChecker.cpp
@@ -73,31 +73,47 @@ bool PointerSubChecker::checkArrayBounds(CheckerContext &C, const Expr *E,
   if (!ElemReg)
     return true;
 
+  ProgramStateRef State = C.getState();
+  SValBuilder &SVB = C.getSValBuilder();
+
   const MemRegion *SuperReg = ElemReg->getSuperRegion();
   if (!isArrayVar(SuperReg))
     return true;
+  DefinedOrUnknownSVal ElemCount =
+      getDynamicElementCount(State, SuperReg, SVB, ElemReg->getElementType());
+
+  const ValueDecl *SuperDecl = nullptr;
+  if (auto *DR = dyn_cast<DeclRegion>(SuperReg))
+    SuperDecl = DR->getDecl();
+  const llvm::APSInt *ElemCountKnown = SVB.getKnownValue(State, ElemCount);
+  const llvm::APSInt *IndexKnown =
+      SVB.getKnownValue(State, ElemReg->getIndex());
 
   auto ReportBug = [&](const llvm::StringLiteral &Msg) {
     if (ExplodedNode *N = C.generateNonFatalErrorNode()) {
       auto R = std::make_unique<PathSensitiveBugReport>(BT, Msg, N);
       R->addRange(E->getSourceRange());
+      if (SuperDecl && ElemCountKnown && SuperDecl->getType()->isArrayType()) {
+        std::string Msg =
+            llvm::formatv("Array of size {0} declared here", *ElemCountKnown);
+        R->addNote(Msg, {SuperDecl, C.getSourceManager()});
+      }
+      if (IndexKnown) {
+        std::string Msg =
+            llvm::formatv("Memory object indexed with {0}", *IndexKnown);
+        R->addNote(Msg, {E, C.getSourceManager(), C.getLocationContext()});
+      }
       C.emitReport(std::move(R));
     }
   };
 
-  ProgramStateRef State = C.getState();
-  SValBuilder &SVB = C.getSValBuilder();
-
   if (SuperReg == Reg) {
     // Case like `(&x + 1) - &x`. Only 1 or 0 is allowed as index.
-    if (const llvm::APSInt *I = SVB.getKnownValue(State, ElemReg->getIndex());
-        I && (!I->isOne() && !I->isZero()))
+    if (IndexKnown && (!IndexKnown->isOne() && !IndexKnown->isZero()))
       ReportBug(Msg_BadVarIndex);
     return false;
   }
 
-  DefinedOrUnknownSVal ElemCount =
-      getDynamicElementCount(State, SuperReg, SVB, ElemReg->getElementType());
   auto IndexTooLarge = SVB.evalBinOp(C.getState(), BO_GT, ElemReg->getIndex(),
                                      ElemCount, SVB.getConditionType())
                            .getAs<DefinedOrUnknownSVal>();
diff --git a/clang/test/Analysis/pointer-sub.c b/clang/test/Analysis/pointer-sub.c
index 194c8918899525..0ce8fe850ca0fe 100644
--- a/clang/test/Analysis/pointer-sub.c
+++ b/clang/test/Analysis/pointer-sub.c
@@ -1,7 +1,7 @@
 // RUN: %clang_analyze_cc1 -analyzer-checker=core,alpha.core.PointerSub -analyzer-output=text-minimal -verify %s
 
 void f1(void) {
-  int x, y, z[10];
+  int x, y, z[10]; // expected-note2{{Array of size 10 declared here}}
   int d = &y - &x; // expected-warning{{Subtraction of two pointers that do not point into the same array is undefined behavior}}
   d = z - &y; // expected-warning{{Subtraction of two pointers that do not point into the same array is undefined behavior}}
   d = &x - &x; // no-warning (subtraction of any two identical pointers is allowed)
@@ -9,18 +9,23 @@ void f1(void) {
   d = (&x + 1) - &x; // no-warning ('&x' is like a single-element array)
   d = &x - (&x + 1); // no-warning
   d = (&x + 0) - &x; // no-warning
-  d = (&x - 1) - &x; // expected-warning{{Indexing the address of a variable with other than 1 at this place is undefined behavior}}
-  d = (&x + 2) - &x; // expected-warning{{Indexing the address of a variable with other than 1 at this place is undefined behavior}}
+  d = (&x - 1) - &x; // expected-warning{{Indexing the address of a variable with other than 1 at this place is undefined behavior}} \
+                     // expected-note{{Memory object indexed with -1}}
+  d = (&x + 2) - &x; // expected-warning{{Indexing the address of a variable with other than 1 at this place is undefined behavior}} \
+                     // expected-note{{Memory object indexed with 2}}
 
   d = (z + 9) - z; // no-warning (pointers to same array)
   d = (z + 10) - z; // no-warning (pointer to "one after the end")
-  d = (z + 11) - z; // expected-warning{{Using an array index greater than the array size at pointer subtraction is undefined behavior}}
-  d = (z - 1) - z; // expected-warning{{Using a negative array index at pointer subtraction is undefined behavior}}
+  d = (z + 11) - z; // expected-warning{{Using an array index greater than the array size at pointer subtraction is undefined behavior}} \
+                    // expected-note{{Memory object indexed with 11}}
+  d = (z - 1) - z; // expected-warning{{Using a negative array index at pointer subtraction is undefined behavior}} \
+                   // expected-note{{Memory object indexed with -1}}
 }
 
 void f2(void) {
   int a[10], b[10], c; // expected-note{{Array at the left-hand side of subtraction}} \
-                       // expected-note2{{Array at the right-hand side of subtraction}}
+                       // expected-note2{{Array at the right-hand side of subtraction}} \
+                       // expected-note{{Array of size 10 declared here}}
   int *p = &a[2];
   int *q = &a[8];
   int d = q - p; // no-warning (pointers into the same array)
@@ -31,7 +36,8 @@ void f2(void) {
   q = a + 10;
   d = q - p; // no warning (use of pointer to one after the end is allowed)
   q = a + 11;
-  d = q - a; // expected-warning{{Using an array index greater than the array size at pointer subtraction is undefined behavior}}
+  d = q - a; // expected-warning{{Using an array index greater than the array size at pointer subtraction is undefined behavior}} \
+             // expected-note{{Memory object indexed with 11}}
 
   d = &a[4] - a; // no-warning
   d = &a[2] - p; // no-warning
@@ -154,3 +160,15 @@ int f12() {
   init_S2(&s);
   return s.p1 - s.p2; // no-warning (pointers are unknown)
 }
+
+void f13() {
+  int a[0]; // expected-note2{{Array of size 0 declared here}}
+  int *p1 = a, *p2 = a, *p3 = a;
+  --p1;
+  ++p2;
+  int d1 = a - p1; // expected-warning{{negative array index}} \
+                   // expected-note{{Memory object indexed with -1}}
+  int d2 = a - p2; // expected-warning{{array index greater}} \
+                   // expected-note{{Memory object indexed with 1}}
+  int d3 = a - p3;
+}

@llvmbot
Copy link
Member

llvmbot commented Aug 8, 2024

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

Author: Balázs Kéri (balazske)

Changes

Notes appear at out-of-range array index for index value and array size.


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

2 Files Affected:

  • (modified) clang/lib/StaticAnalyzer/Checkers/PointerSubChecker.cpp (+23-7)
  • (modified) clang/test/Analysis/pointer-sub.c (+25-7)
diff --git a/clang/lib/StaticAnalyzer/Checkers/PointerSubChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/PointerSubChecker.cpp
index b856b0edc61514..1b215cc59c95ae 100644
--- a/clang/lib/StaticAnalyzer/Checkers/PointerSubChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/PointerSubChecker.cpp
@@ -73,31 +73,47 @@ bool PointerSubChecker::checkArrayBounds(CheckerContext &C, const Expr *E,
   if (!ElemReg)
     return true;
 
+  ProgramStateRef State = C.getState();
+  SValBuilder &SVB = C.getSValBuilder();
+
   const MemRegion *SuperReg = ElemReg->getSuperRegion();
   if (!isArrayVar(SuperReg))
     return true;
+  DefinedOrUnknownSVal ElemCount =
+      getDynamicElementCount(State, SuperReg, SVB, ElemReg->getElementType());
+
+  const ValueDecl *SuperDecl = nullptr;
+  if (auto *DR = dyn_cast<DeclRegion>(SuperReg))
+    SuperDecl = DR->getDecl();
+  const llvm::APSInt *ElemCountKnown = SVB.getKnownValue(State, ElemCount);
+  const llvm::APSInt *IndexKnown =
+      SVB.getKnownValue(State, ElemReg->getIndex());
 
   auto ReportBug = [&](const llvm::StringLiteral &Msg) {
     if (ExplodedNode *N = C.generateNonFatalErrorNode()) {
       auto R = std::make_unique<PathSensitiveBugReport>(BT, Msg, N);
       R->addRange(E->getSourceRange());
+      if (SuperDecl && ElemCountKnown && SuperDecl->getType()->isArrayType()) {
+        std::string Msg =
+            llvm::formatv("Array of size {0} declared here", *ElemCountKnown);
+        R->addNote(Msg, {SuperDecl, C.getSourceManager()});
+      }
+      if (IndexKnown) {
+        std::string Msg =
+            llvm::formatv("Memory object indexed with {0}", *IndexKnown);
+        R->addNote(Msg, {E, C.getSourceManager(), C.getLocationContext()});
+      }
       C.emitReport(std::move(R));
     }
   };
 
-  ProgramStateRef State = C.getState();
-  SValBuilder &SVB = C.getSValBuilder();
-
   if (SuperReg == Reg) {
     // Case like `(&x + 1) - &x`. Only 1 or 0 is allowed as index.
-    if (const llvm::APSInt *I = SVB.getKnownValue(State, ElemReg->getIndex());
-        I && (!I->isOne() && !I->isZero()))
+    if (IndexKnown && (!IndexKnown->isOne() && !IndexKnown->isZero()))
       ReportBug(Msg_BadVarIndex);
     return false;
   }
 
-  DefinedOrUnknownSVal ElemCount =
-      getDynamicElementCount(State, SuperReg, SVB, ElemReg->getElementType());
   auto IndexTooLarge = SVB.evalBinOp(C.getState(), BO_GT, ElemReg->getIndex(),
                                      ElemCount, SVB.getConditionType())
                            .getAs<DefinedOrUnknownSVal>();
diff --git a/clang/test/Analysis/pointer-sub.c b/clang/test/Analysis/pointer-sub.c
index 194c8918899525..0ce8fe850ca0fe 100644
--- a/clang/test/Analysis/pointer-sub.c
+++ b/clang/test/Analysis/pointer-sub.c
@@ -1,7 +1,7 @@
 // RUN: %clang_analyze_cc1 -analyzer-checker=core,alpha.core.PointerSub -analyzer-output=text-minimal -verify %s
 
 void f1(void) {
-  int x, y, z[10];
+  int x, y, z[10]; // expected-note2{{Array of size 10 declared here}}
   int d = &y - &x; // expected-warning{{Subtraction of two pointers that do not point into the same array is undefined behavior}}
   d = z - &y; // expected-warning{{Subtraction of two pointers that do not point into the same array is undefined behavior}}
   d = &x - &x; // no-warning (subtraction of any two identical pointers is allowed)
@@ -9,18 +9,23 @@ void f1(void) {
   d = (&x + 1) - &x; // no-warning ('&x' is like a single-element array)
   d = &x - (&x + 1); // no-warning
   d = (&x + 0) - &x; // no-warning
-  d = (&x - 1) - &x; // expected-warning{{Indexing the address of a variable with other than 1 at this place is undefined behavior}}
-  d = (&x + 2) - &x; // expected-warning{{Indexing the address of a variable with other than 1 at this place is undefined behavior}}
+  d = (&x - 1) - &x; // expected-warning{{Indexing the address of a variable with other than 1 at this place is undefined behavior}} \
+                     // expected-note{{Memory object indexed with -1}}
+  d = (&x + 2) - &x; // expected-warning{{Indexing the address of a variable with other than 1 at this place is undefined behavior}} \
+                     // expected-note{{Memory object indexed with 2}}
 
   d = (z + 9) - z; // no-warning (pointers to same array)
   d = (z + 10) - z; // no-warning (pointer to "one after the end")
-  d = (z + 11) - z; // expected-warning{{Using an array index greater than the array size at pointer subtraction is undefined behavior}}
-  d = (z - 1) - z; // expected-warning{{Using a negative array index at pointer subtraction is undefined behavior}}
+  d = (z + 11) - z; // expected-warning{{Using an array index greater than the array size at pointer subtraction is undefined behavior}} \
+                    // expected-note{{Memory object indexed with 11}}
+  d = (z - 1) - z; // expected-warning{{Using a negative array index at pointer subtraction is undefined behavior}} \
+                   // expected-note{{Memory object indexed with -1}}
 }
 
 void f2(void) {
   int a[10], b[10], c; // expected-note{{Array at the left-hand side of subtraction}} \
-                       // expected-note2{{Array at the right-hand side of subtraction}}
+                       // expected-note2{{Array at the right-hand side of subtraction}} \
+                       // expected-note{{Array of size 10 declared here}}
   int *p = &a[2];
   int *q = &a[8];
   int d = q - p; // no-warning (pointers into the same array)
@@ -31,7 +36,8 @@ void f2(void) {
   q = a + 10;
   d = q - p; // no warning (use of pointer to one after the end is allowed)
   q = a + 11;
-  d = q - a; // expected-warning{{Using an array index greater than the array size at pointer subtraction is undefined behavior}}
+  d = q - a; // expected-warning{{Using an array index greater than the array size at pointer subtraction is undefined behavior}} \
+             // expected-note{{Memory object indexed with 11}}
 
   d = &a[4] - a; // no-warning
   d = &a[2] - p; // no-warning
@@ -154,3 +160,15 @@ int f12() {
   init_S2(&s);
   return s.p1 - s.p2; // no-warning (pointers are unknown)
 }
+
+void f13() {
+  int a[0]; // expected-note2{{Array of size 0 declared here}}
+  int *p1 = a, *p2 = a, *p3 = a;
+  --p1;
+  ++p2;
+  int d1 = a - p1; // expected-warning{{negative array index}} \
+                   // expected-note{{Memory object indexed with -1}}
+  int d2 = a - p2; // expected-warning{{array index greater}} \
+                   // expected-note{{Memory object indexed with 1}}
+  int d3 = a - p3;
+}

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.

The change LGTM, it makes these reports somewhat easier to understand.

However, note that I'll delete all array bounds checking logic from this checker when I'll bring alpha.security.ArrayBoundV2 out of alpha, because there is no reason to have several checkers that are looking for the same error type. (Also note that the logic in ArrayBoundV2 is more sophisticated and general than the logic of this checker.)

This means that you shouldn't work on improving this part of this checker, because your efforts will be wasted when this is removed.

(EDIT: the checker is named ArrayBoundV2, while I originally accidentally wrote ArrayBoundsV2.)

@balazske
Copy link
Collaborator Author

balazske commented Aug 8, 2024

If the ArrayBoundsV2 checker is finished it should find all of the cases in the test of PointerSubChecker that have out-of-bound indexing, and including the cases where a single variable is handled like an 1-element array? If yes the bounds check is not needed in this checker, only the check for different memory object. I like it better if the bounds check is then removed before the PointerSub checker goes out of alpha (this means now). If the bounds check is removed this checker will not report the cases where invalid indexing occurs because that is a different error.

@NagyDonat
Copy link
Contributor

If the ArrayBoundsV2 checker is finished it should find all of the cases in the test of PointerSubChecker that have out-of-bound indexing, and including the cases where a single variable is handled like an 1-element array?

Yes, ArrayBoundV2 will handle the cases where a single variable is handled like an 1-element array.

If yes the bounds check is not needed in this checker, only the check for different memory object. I like it better if the bounds check is then removed before the PointerSub checker goes out of alpha (this means now). If the bounds check is removed this checker will not report the cases where invalid indexing occurs because that is a different error.

Yes, I can agree with that, checking out of bounds indexing does not belong to this checker. Feel free to create a different commit which removes this feature instead of tweaking its messages.

@balazske
Copy link
Collaborator Author

balazske commented Aug 9, 2024

I uploaded now #102580 that removes the entire array bounds checking.

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.

Thanks!

We should close this when that other PR is accepted and merged. Until then I'm putting a "Request changes" mark on this to prevent an accidental merge.

@balazske balazske closed this Sep 2, 2024
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