-
Notifications
You must be signed in to change notification settings - Fork 14.3k
[clang][analyzer] Add notes to PointerSubChecker #95899
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
Conversation
Notes are added to indicate the array declarations of the arrays in a found invalid pointer subtraction.
@llvm/pr-subscribers-clang Author: Balázs Kéri (balazske) ChangesNotes are added to indicate the array declarations of the arrays in a found invalid pointer subtraction. Full diff: https://github.com/llvm/llvm-project/pull/95899.diff 2 Files Affected:
diff --git a/clang/lib/StaticAnalyzer/Checkers/PointerSubChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/PointerSubChecker.cpp
index b73534136fdf0..a983e66df0818 100644
--- a/clang/lib/StaticAnalyzer/Checkers/PointerSubChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/PointerSubChecker.cpp
@@ -43,8 +43,6 @@ class PointerSubChecker
bool checkArrayBounds(CheckerContext &C, const Expr *E,
const ElementRegion *ElemReg,
const MemRegion *Reg) const;
- void reportBug(CheckerContext &C, const Expr *E,
- const llvm::StringLiteral &Msg) const;
public:
void checkPreStmt(const BinaryOperator *B, CheckerContext &C) const;
@@ -57,6 +55,14 @@ bool PointerSubChecker::checkArrayBounds(CheckerContext &C, const Expr *E,
if (!ElemReg)
return true;
+ auto ReportBug = [&](const llvm::StringLiteral &Msg) {
+ if (ExplodedNode *N = C.generateNonFatalErrorNode()) {
+ auto R = std::make_unique<PathSensitiveBugReport>(BT, Msg, N);
+ R->addRange(E->getSourceRange());
+ C.emitReport(std::move(R));
+ }
+ };
+
ProgramStateRef State = C.getState();
const MemRegion *SuperReg = ElemReg->getSuperRegion();
SValBuilder &SVB = C.getSValBuilder();
@@ -64,7 +70,7 @@ bool PointerSubChecker::checkArrayBounds(CheckerContext &C, const Expr *E,
if (SuperReg == Reg) {
if (const llvm::APSInt *I = SVB.getKnownValue(State, ElemReg->getIndex());
I && (!I->isOne() && !I->isZero()))
- reportBug(C, E, Msg_BadVarIndex);
+ ReportBug(Msg_BadVarIndex);
return false;
}
@@ -77,7 +83,7 @@ bool PointerSubChecker::checkArrayBounds(CheckerContext &C, const Expr *E,
ProgramStateRef S1, S2;
std::tie(S1, S2) = C.getState()->assume(*IndexTooLarge);
if (S1 && !S2) {
- reportBug(C, E, Msg_LargeArrayIndex);
+ ReportBug(Msg_LargeArrayIndex);
return false;
}
}
@@ -89,22 +95,13 @@ bool PointerSubChecker::checkArrayBounds(CheckerContext &C, const Expr *E,
ProgramStateRef S1, S2;
std::tie(S1, S2) = State->assume(*IndexTooSmall);
if (S1 && !S2) {
- reportBug(C, E, Msg_NegativeArrayIndex);
+ ReportBug(Msg_NegativeArrayIndex);
return false;
}
}
return true;
}
-void PointerSubChecker::reportBug(CheckerContext &C, const Expr *E,
- const llvm::StringLiteral &Msg) const {
- if (ExplodedNode *N = C.generateNonFatalErrorNode()) {
- auto R = std::make_unique<PathSensitiveBugReport>(BT, Msg, N);
- R->addRange(E->getSourceRange());
- C.emitReport(std::move(R));
- }
-}
-
void PointerSubChecker::checkPreStmt(const BinaryOperator *B,
CheckerContext &C) const {
// When doing pointer subtraction, if the two pointers do not point to the
@@ -136,6 +133,9 @@ void PointerSubChecker::checkPreStmt(const BinaryOperator *B,
if (!checkArrayBounds(C, B->getRHS(), ElemRR, LR))
return;
+ const ValueDecl *DiffDeclL = nullptr;
+ const ValueDecl *DiffDeclR = nullptr;
+
if (ElemLR && ElemRR) {
const MemRegion *SuperLR = ElemLR->getSuperRegion();
const MemRegion *SuperRR = ElemRR->getSuperRegion();
@@ -144,9 +144,24 @@ void PointerSubChecker::checkPreStmt(const BinaryOperator *B,
// Allow arithmetic on different symbolic regions.
if (isa<SymbolicRegion>(SuperLR) || isa<SymbolicRegion>(SuperRR))
return;
+ if (const auto *SuperDLR = dyn_cast<DeclRegion>(SuperLR))
+ DiffDeclL = SuperDLR->getDecl();
+ if (const auto *SuperDRR = dyn_cast<DeclRegion>(SuperRR))
+ DiffDeclR = SuperDRR->getDecl();
}
- reportBug(C, B, Msg_MemRegionDifferent);
+ if (ExplodedNode *N = C.generateNonFatalErrorNode()) {
+ auto R =
+ std::make_unique<PathSensitiveBugReport>(BT, Msg_MemRegionDifferent, N);
+ R->addRange(B->getSourceRange());
+ if (DiffDeclL)
+ R->addNote("Array at the left-hand side of subtraction",
+ {DiffDeclL, C.getSourceManager()});
+ if (DiffDeclR)
+ R->addNote("Array at the right-hand side of subtraction",
+ {DiffDeclR, C.getSourceManager()});
+ C.emitReport(std::move(R));
+ }
}
void ento::registerPointerSubChecker(CheckerManager &mgr) {
diff --git a/clang/test/Analysis/pointer-sub-notes.c b/clang/test/Analysis/pointer-sub-notes.c
new file mode 100644
index 0000000000000..dfdace3a58deb
--- /dev/null
+++ b/clang/test/Analysis/pointer-sub-notes.c
@@ -0,0 +1,34 @@
+// RUN: %clang_analyze_cc1 -analyzer-checker=alpha.core.PointerSub -analyzer-output=text -verify %s
+
+void negative_1() {
+ int a[3];
+ int x = -1;
+ // FIXME: should indicate that 'x' is -1
+ int d = &a[x] - &a[0]; // expected-warning{{Using a negative array index at pointer subtraction is undefined behavior}} \
+ // expected-note{{Using a negative array index at pointer subtraction is undefined behavior}}
+}
+
+void negative_2() {
+ int a[3];
+ int *p1 = a, *p2 = a;
+ --p2;
+ // FIXME: should indicate that 'p2' is negative
+ int d = p1 - p2; // expected-warning{{Using a negative array index at pointer subtraction is undefined behavior}} \
+ // expected-note{{Using a negative array index at pointer subtraction is undefined behavior}}
+}
+
+void different_1() {
+ int a[3]; // expected-note{{Array at the left-hand side of subtraction}}
+ int b[3]; // expected-note{{Array at the right-hand side of subtraction}}
+ int d = &a[2] - &b[0]; // expected-warning{{Subtraction of two pointers that do not point into the same array is undefined behavior}} \
+ // expected-note{{Subtraction of two pointers that do not point into the same array is undefined behavior}}
+}
+
+void different_2() {
+ int a[3]; // expected-note{{Array at the right-hand side of subtraction}}
+ int b[3]; // expected-note{{Array at the left-hand side of subtraction}}
+ int *p1 = a + 1;
+ int *p2 = b;
+ int d = p2 - p1; // expected-warning{{Subtraction of two pointers that do not point into the same array is undefined behavior}} \
+ // expected-note{{Subtraction of two pointers that do not point into the same array is undefined behavior}}
+}
|
@llvm/pr-subscribers-clang-static-analyzer-1 Author: Balázs Kéri (balazske) ChangesNotes are added to indicate the array declarations of the arrays in a found invalid pointer subtraction. Full diff: https://github.com/llvm/llvm-project/pull/95899.diff 2 Files Affected:
diff --git a/clang/lib/StaticAnalyzer/Checkers/PointerSubChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/PointerSubChecker.cpp
index b73534136fdf0..a983e66df0818 100644
--- a/clang/lib/StaticAnalyzer/Checkers/PointerSubChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/PointerSubChecker.cpp
@@ -43,8 +43,6 @@ class PointerSubChecker
bool checkArrayBounds(CheckerContext &C, const Expr *E,
const ElementRegion *ElemReg,
const MemRegion *Reg) const;
- void reportBug(CheckerContext &C, const Expr *E,
- const llvm::StringLiteral &Msg) const;
public:
void checkPreStmt(const BinaryOperator *B, CheckerContext &C) const;
@@ -57,6 +55,14 @@ bool PointerSubChecker::checkArrayBounds(CheckerContext &C, const Expr *E,
if (!ElemReg)
return true;
+ auto ReportBug = [&](const llvm::StringLiteral &Msg) {
+ if (ExplodedNode *N = C.generateNonFatalErrorNode()) {
+ auto R = std::make_unique<PathSensitiveBugReport>(BT, Msg, N);
+ R->addRange(E->getSourceRange());
+ C.emitReport(std::move(R));
+ }
+ };
+
ProgramStateRef State = C.getState();
const MemRegion *SuperReg = ElemReg->getSuperRegion();
SValBuilder &SVB = C.getSValBuilder();
@@ -64,7 +70,7 @@ bool PointerSubChecker::checkArrayBounds(CheckerContext &C, const Expr *E,
if (SuperReg == Reg) {
if (const llvm::APSInt *I = SVB.getKnownValue(State, ElemReg->getIndex());
I && (!I->isOne() && !I->isZero()))
- reportBug(C, E, Msg_BadVarIndex);
+ ReportBug(Msg_BadVarIndex);
return false;
}
@@ -77,7 +83,7 @@ bool PointerSubChecker::checkArrayBounds(CheckerContext &C, const Expr *E,
ProgramStateRef S1, S2;
std::tie(S1, S2) = C.getState()->assume(*IndexTooLarge);
if (S1 && !S2) {
- reportBug(C, E, Msg_LargeArrayIndex);
+ ReportBug(Msg_LargeArrayIndex);
return false;
}
}
@@ -89,22 +95,13 @@ bool PointerSubChecker::checkArrayBounds(CheckerContext &C, const Expr *E,
ProgramStateRef S1, S2;
std::tie(S1, S2) = State->assume(*IndexTooSmall);
if (S1 && !S2) {
- reportBug(C, E, Msg_NegativeArrayIndex);
+ ReportBug(Msg_NegativeArrayIndex);
return false;
}
}
return true;
}
-void PointerSubChecker::reportBug(CheckerContext &C, const Expr *E,
- const llvm::StringLiteral &Msg) const {
- if (ExplodedNode *N = C.generateNonFatalErrorNode()) {
- auto R = std::make_unique<PathSensitiveBugReport>(BT, Msg, N);
- R->addRange(E->getSourceRange());
- C.emitReport(std::move(R));
- }
-}
-
void PointerSubChecker::checkPreStmt(const BinaryOperator *B,
CheckerContext &C) const {
// When doing pointer subtraction, if the two pointers do not point to the
@@ -136,6 +133,9 @@ void PointerSubChecker::checkPreStmt(const BinaryOperator *B,
if (!checkArrayBounds(C, B->getRHS(), ElemRR, LR))
return;
+ const ValueDecl *DiffDeclL = nullptr;
+ const ValueDecl *DiffDeclR = nullptr;
+
if (ElemLR && ElemRR) {
const MemRegion *SuperLR = ElemLR->getSuperRegion();
const MemRegion *SuperRR = ElemRR->getSuperRegion();
@@ -144,9 +144,24 @@ void PointerSubChecker::checkPreStmt(const BinaryOperator *B,
// Allow arithmetic on different symbolic regions.
if (isa<SymbolicRegion>(SuperLR) || isa<SymbolicRegion>(SuperRR))
return;
+ if (const auto *SuperDLR = dyn_cast<DeclRegion>(SuperLR))
+ DiffDeclL = SuperDLR->getDecl();
+ if (const auto *SuperDRR = dyn_cast<DeclRegion>(SuperRR))
+ DiffDeclR = SuperDRR->getDecl();
}
- reportBug(C, B, Msg_MemRegionDifferent);
+ if (ExplodedNode *N = C.generateNonFatalErrorNode()) {
+ auto R =
+ std::make_unique<PathSensitiveBugReport>(BT, Msg_MemRegionDifferent, N);
+ R->addRange(B->getSourceRange());
+ if (DiffDeclL)
+ R->addNote("Array at the left-hand side of subtraction",
+ {DiffDeclL, C.getSourceManager()});
+ if (DiffDeclR)
+ R->addNote("Array at the right-hand side of subtraction",
+ {DiffDeclR, C.getSourceManager()});
+ C.emitReport(std::move(R));
+ }
}
void ento::registerPointerSubChecker(CheckerManager &mgr) {
diff --git a/clang/test/Analysis/pointer-sub-notes.c b/clang/test/Analysis/pointer-sub-notes.c
new file mode 100644
index 0000000000000..dfdace3a58deb
--- /dev/null
+++ b/clang/test/Analysis/pointer-sub-notes.c
@@ -0,0 +1,34 @@
+// RUN: %clang_analyze_cc1 -analyzer-checker=alpha.core.PointerSub -analyzer-output=text -verify %s
+
+void negative_1() {
+ int a[3];
+ int x = -1;
+ // FIXME: should indicate that 'x' is -1
+ int d = &a[x] - &a[0]; // expected-warning{{Using a negative array index at pointer subtraction is undefined behavior}} \
+ // expected-note{{Using a negative array index at pointer subtraction is undefined behavior}}
+}
+
+void negative_2() {
+ int a[3];
+ int *p1 = a, *p2 = a;
+ --p2;
+ // FIXME: should indicate that 'p2' is negative
+ int d = p1 - p2; // expected-warning{{Using a negative array index at pointer subtraction is undefined behavior}} \
+ // expected-note{{Using a negative array index at pointer subtraction is undefined behavior}}
+}
+
+void different_1() {
+ int a[3]; // expected-note{{Array at the left-hand side of subtraction}}
+ int b[3]; // expected-note{{Array at the right-hand side of subtraction}}
+ int d = &a[2] - &b[0]; // expected-warning{{Subtraction of two pointers that do not point into the same array is undefined behavior}} \
+ // expected-note{{Subtraction of two pointers that do not point into the same array is undefined behavior}}
+}
+
+void different_2() {
+ int a[3]; // expected-note{{Array at the right-hand side of subtraction}}
+ int b[3]; // expected-note{{Array at the left-hand side of subtraction}}
+ int *p1 = a + 1;
+ int *p2 = b;
+ int d = p2 - p1; // expected-warning{{Subtraction of two pointers that do not point into the same array is undefined behavior}} \
+ // expected-note{{Subtraction of two pointers that do not point into the same array is undefined behavior}}
+}
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good overall, I have one remark about a rare case that would require some specialized code.
if (const auto *SuperDLR = dyn_cast<DeclRegion>(SuperLR)) | ||
DiffDeclL = SuperDLR->getDecl(); | ||
if (const auto *SuperDRR = dyn_cast<DeclRegion>(SuperRR)) | ||
DiffDeclR = SuperDRR->getDecl(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note that FieldRegion
s are DeclRegion
s, so these declarations may be data member declarations within a struct
or class
. This is usually not a problem, but there is a corner case where SuperLR != SuperRR
, but the corresponding declarations are identical:
struct {
int array[5];
} a, b;
int func(void) {
return &a.array[3] - &b.array[2];
}
In this case the current code would place both notes onto the declaration of field
, which would be confusing for the user.
Consider adding some code that handles this situation explicitly. (Either simply skip note creation when DiffDeclL == DiffDeclR
, or create a specialized note for this case.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Warning is now omitted if both would be at the same declaration.
I found difficult results from the checker where it is not obvious what the problem is. Other problem is when different arrays are found: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM, thanks for the update. I added two minor edit suggestions, but after that the commit can be merged.
// expected-note{{Subtraction of two pointers that do not point into the same array is undefined behavior}} | ||
} | ||
|
||
int different_5() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
int different_5() { | |
int different_4() { |
Very minor nitpick: different_3
was followed by different_5
.
if (DiffDeclR) | ||
R->addNote("Array at the right-hand side of subtraction", | ||
{DiffDeclR, C.getSourceManager()}); | ||
if (DiffDeclL != DiffDeclR) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if (DiffDeclL != DiffDeclR) { | |
if (DiffDeclL != DiffDeclR) { | |
// The declarations may be identical even if the regions are different, | |
// if they are field regions within different objects: | |
// struct { int array[10]; } a, b; | |
// do_something_with(&a.array[5] - &b.array[5]); | |
// In this case the notes would be confusing, so don't emit them. |
Add a comment that explains this if (DiffDeclL != DiffDeclR)
check because otherwise it would be very surprising.
Notes are added to indicate the array declarations of the arrays in a found invalid pointer subtraction.
Notes are added to indicate the array declarations of the arrays in a found invalid pointer subtraction.