Skip to content

Commit 86026ee

Browse files
malavikasamakMalavikaSamak
andauthored
[clang-tidy] Warn about misuse of sizeof operator in loops. (#143205)
The sizeof operator misuses in loop conditionals can be a source of bugs. The common misuse is attempting to retrieve the number of elements in the array by using the sizeof expression and forgetting to divide the value by the sizeof the array elements. This results in an incorrect computation of the array length and requires a warning from the sizeof checker. Example: ``` int array[20]; void test_for_loop() { // Needs warning. for(int i = 0; i < sizeof(array); i++) { array[i] = i; } } void test_while_loop() { int count = 0; // Needs warning. while(count < sizeof(array)) { array[count] = 0; count = count + 2; } } ``` rdar://151403083 --------- Co-authored-by: MalavikaSamak <malavika2@apple.com>
1 parent 532c15a commit 86026ee

File tree

5 files changed

+115
-1
lines changed

5 files changed

+115
-1
lines changed

clang-tools-extra/clang-tidy/bugprone/SizeofExpressionCheck.cpp

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,9 @@ SizeofExpressionCheck::SizeofExpressionCheck(StringRef Name,
7272
Options.get("WarnOnSizeOfPointerToAggregate", true)),
7373
WarnOnSizeOfPointer(Options.get("WarnOnSizeOfPointer", false)),
7474
WarnOnOffsetDividedBySizeOf(
75-
Options.get("WarnOnOffsetDividedBySizeOf", true)) {}
75+
Options.get("WarnOnOffsetDividedBySizeOf", true)),
76+
WarnOnSizeOfInLoopTermination(
77+
Options.get("WarnOnSizeOfInLoopTermination", true)) {}
7678

7779
void SizeofExpressionCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
7880
Options.store(Opts, "WarnOnSizeOfConstant", WarnOnSizeOfConstant);
@@ -86,13 +88,22 @@ void SizeofExpressionCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
8688
Options.store(Opts, "WarnOnSizeOfPointer", WarnOnSizeOfPointer);
8789
Options.store(Opts, "WarnOnOffsetDividedBySizeOf",
8890
WarnOnOffsetDividedBySizeOf);
91+
Options.store(Opts, "WarnOnSizeOfInLoopTermination",
92+
WarnOnSizeOfInLoopTermination);
8993
}
9094

9195
void SizeofExpressionCheck::registerMatchers(MatchFinder *Finder) {
9296
// FIXME:
9397
// Some of the checks should not match in template code to avoid false
9498
// positives if sizeof is applied on template argument.
9599

100+
auto LoopCondExpr =
101+
[](const ast_matchers::internal::Matcher<Stmt> &InnerMatcher) {
102+
return stmt(anyOf(forStmt(hasCondition(InnerMatcher)),
103+
whileStmt(hasCondition(InnerMatcher)),
104+
doStmt(hasCondition(InnerMatcher))));
105+
};
106+
96107
const auto IntegerExpr = ignoringParenImpCasts(integerLiteral());
97108
const auto ConstantExpr = ignoringParenImpCasts(
98109
anyOf(integerLiteral(), unaryOperator(hasUnaryOperand(IntegerExpr)),
@@ -130,6 +141,14 @@ void SizeofExpressionCheck::registerMatchers(MatchFinder *Finder) {
130141
this);
131142
}
132143

144+
if (WarnOnSizeOfInLoopTermination) {
145+
auto CondExpr = binaryOperator(
146+
allOf(has(SizeOfExpr.bind("sizeof-expr")), isComparisonOperator()));
147+
Finder->addMatcher(LoopCondExpr(anyOf(CondExpr, hasDescendant(CondExpr)))
148+
.bind("loop-expr"),
149+
this);
150+
}
151+
133152
// Detect sizeof(kPtr) where kPtr is 'const char* kPtr = "abc"';
134153
const auto CharPtrType = pointerType(pointee(isAnyCharacter()));
135154
const auto ConstStrLiteralDecl =
@@ -349,6 +368,23 @@ void SizeofExpressionCheck::check(const MatchFinder::MatchResult &Result) {
349368
diag(E->getBeginLoc(),
350369
"suspicious usage of 'sizeof(char*)'; do you mean 'strlen'?")
351370
<< E->getSourceRange();
371+
} else if (const auto *E = Result.Nodes.getNodeAs<Stmt>("loop-expr")) {
372+
auto *SizeofArgTy = Result.Nodes.getNodeAs<Type>("sizeof-arg-type");
373+
if (const auto member = dyn_cast<MemberPointerType>(SizeofArgTy))
374+
SizeofArgTy = member->getPointeeType().getTypePtr();
375+
376+
const auto *SzOfExpr = Result.Nodes.getNodeAs<Expr>("sizeof-expr");
377+
378+
if (const auto type = dyn_cast<ArrayType>(SizeofArgTy)) {
379+
// check if the array element size is larger than one. If true,
380+
// the size of the array is higher than the number of elements
381+
CharUnits sSize = Ctx.getTypeSizeInChars(type->getElementType());
382+
if (!sSize.isOne()) {
383+
diag(SzOfExpr->getBeginLoc(),
384+
"suspicious usage of 'sizeof' in the loop")
385+
<< SzOfExpr->getSourceRange();
386+
}
387+
}
352388
} else if (const auto *E = Result.Nodes.getNodeAs<Expr>("sizeof-pointer")) {
353389
diag(E->getBeginLoc(), "suspicious usage of 'sizeof()' on an expression "
354390
"of pointer type")

clang-tools-extra/clang-tidy/bugprone/SizeofExpressionCheck.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ class SizeofExpressionCheck : public ClangTidyCheck {
3232
const bool WarnOnSizeOfPointerToAggregate;
3333
const bool WarnOnSizeOfPointer;
3434
const bool WarnOnOffsetDividedBySizeOf;
35+
const bool WarnOnSizeOfInLoopTermination;
3536
};
3637

3738
} // namespace clang::tidy::bugprone

clang-tools-extra/docs/ReleaseNotes.rst

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,11 @@ Changes in existing checks
173173
<clang-tidy/checks/bugprone/optional-value-conversion>` check to detect
174174
conversion in argument of ``std::make_optional``.
175175

176+
- Improved :doc: `bugprone-sizeof-expression
177+
<clang-tidy/checks/bugprone/bugprone-sizeof-expression>` check by adding
178+
`WarnOnSizeOfInLoopTermination` option to detect misuses of ``sizeof``
179+
expression in loop conditions.
180+
176181
- Improved :doc:`bugprone-string-constructor
177182
<clang-tidy/checks/bugprone/string-constructor>` check to find suspicious
178183
calls of ``std::string`` constructor with char pointer, start position and

clang-tools-extra/docs/clang-tidy/checks/bugprone/sizeof-expression.rst

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -316,3 +316,12 @@ Options
316316
When `true`, the check will warn on pointer arithmetic where the
317317
element count is obtained from a division with ``sizeof(...)``,
318318
e.g., ``Ptr + Bytes / sizeof(*T)``. Default is `true`.
319+
320+
.. option:: WarnOnSizeOfInLoopTermination
321+
322+
When `true`, the check will warn about incorrect use of sizeof expression
323+
in loop termination condition. The warning triggers if the ``sizeof``
324+
expression appears to be incorrectly used to determine the number of
325+
array/buffer elements.
326+
e.g, ``long arr[10]; for(int i = 0; i < sizeof(arr); i++) { ... }``. Default
327+
is `true`.

clang-tools-extra/test/clang-tidy/checkers/bugprone/sizeof-expression.cpp

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,69 @@ int Test2(MyConstChar* A) {
164164
return sum;
165165
}
166166

167+
struct A {
168+
int array[10];
169+
};
170+
171+
struct B {
172+
struct A a;
173+
};
174+
175+
void loop_access_elements(int num, struct B b) {
176+
struct A arr[10];
177+
char buf[20];
178+
179+
// CHECK-MESSAGES: :[[@LINE+1]]:22: warning: suspicious usage of 'sizeof' in the loop [bugprone-sizeof-expression]
180+
for(int i = 0; i < sizeof(arr); i++) {
181+
struct A a = arr[i];
182+
}
183+
184+
// Loop warning should not trigger here, even though this code is incorrect
185+
// CHECK-MESSAGES: :[[@LINE+2]]:22: warning: suspicious usage of 'sizeof(K)'; did you mean 'K'? [bugprone-sizeof-expression]
186+
// CHECK-MESSAGES: :[[@LINE+1]]:32: warning: suspicious usage of 'sizeof(...)/sizeof(...)'; numerator is not a multiple of denominator [bugprone-sizeof-expression]
187+
for(int i = 0; i < sizeof(10)/sizeof(A); i++) {
188+
struct A a = arr[i];
189+
}
190+
191+
// Should not warn here
192+
for(int i = 0; i < sizeof(arr)/sizeof(A); i++) {}
193+
194+
// Should not warn here
195+
for (int i = 0; i < 10; i++) {
196+
if (sizeof(arr) != 0) {
197+
198+
}
199+
}
200+
201+
for (int i = 0; i < 10; i++) {
202+
// CHECK-MESSAGES: :[[@LINE+1]]:25: warning: suspicious usage of 'sizeof' in the loop [bugprone-sizeof-expression]
203+
for (int j = 0; j < sizeof(arr); j++) {
204+
}
205+
}
206+
207+
// CHECK-MESSAGES: :[[@LINE+1]]:22: warning: suspicious usage of 'sizeof' in the loop [bugprone-sizeof-expression]
208+
for(int j = 0; j < sizeof(b.a.array); j++) {}
209+
210+
// Should not warn here
211+
for(int i = 0; i < sizeof(buf); i++) {}
212+
213+
// Should not warn here
214+
for(int i = 0; i < (sizeof(arr) << 3); i++) {}
215+
216+
int i = 0;
217+
// CHECK-MESSAGES: :[[@LINE+1]]:14: warning: suspicious usage of 'sizeof' in the loop [bugprone-sizeof-expression]
218+
while(i <= sizeof(arr)) {i++;}
219+
220+
i = 0;
221+
do {
222+
i++;
223+
// CHECK-MESSAGES: :[[@LINE+1]]:16: warning: suspicious usage of 'sizeof' in the loop [bugprone-sizeof-expression]
224+
} while(i <= sizeof(arr));
225+
226+
// CHECK-MESSAGES: :[[@LINE+1]]:29: warning: suspicious usage of 'sizeof' in the loop [bugprone-sizeof-expression]
227+
for(int i = 0, j = 0; i < sizeof(arr) && j < sizeof(buf); i++, j++) {}
228+
}
229+
167230
template <int T>
168231
int Foo() { int A[T]; return sizeof(T); }
169232
// CHECK-MESSAGES: :[[@LINE-1]]:30: warning: suspicious usage of 'sizeof(K)'

0 commit comments

Comments
 (0)