Skip to content

[clang-tidy] Warn about misuse of sizeof operator in loops. #143205

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

Merged
merged 11 commits into from
Jun 25, 2025

Conversation

malavikasamak
Copy link
Contributor

@malavikasamak malavikasamak commented Jun 6, 2025

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

@llvmbot
Copy link
Member

llvmbot commented Jun 6, 2025

@llvm/pr-subscribers-clang
@llvm/pr-subscribers-clang-tidy

@llvm/pr-subscribers-clang-tools-extra

Author: Malavika Samak (malavikasamak)

Changes

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

3 Files Affected:

  • (modified) clang-tools-extra/clang-tidy/bugprone/SizeofExpressionCheck.cpp (+16-1)
  • (modified) clang-tools-extra/clang-tidy/bugprone/SizeofExpressionCheck.h (+1)
  • (modified) clang-tools-extra/test/clang-tidy/checkers/bugprone/sizeof-expression.cpp (+31)
diff --git a/clang-tools-extra/clang-tidy/bugprone/SizeofExpressionCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/SizeofExpressionCheck.cpp
index f3d4c2255d86e..ee66a880792b8 100644
--- a/clang-tools-extra/clang-tidy/bugprone/SizeofExpressionCheck.cpp
+++ b/clang-tools-extra/clang-tidy/bugprone/SizeofExpressionCheck.cpp
@@ -72,7 +72,8 @@ SizeofExpressionCheck::SizeofExpressionCheck(StringRef Name,
           Options.get("WarnOnSizeOfPointerToAggregate", true)),
       WarnOnSizeOfPointer(Options.get("WarnOnSizeOfPointer", false)),
       WarnOnOffsetDividedBySizeOf(
-          Options.get("WarnOnOffsetDividedBySizeOf", true)) {}
+          Options.get("WarnOnOffsetDividedBySizeOf", true)),
+      WarnOnLoopExprSizeOf(Options.get("WarnOnLoopExprSizeOf", true)) {}
 
 void SizeofExpressionCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
   Options.store(Opts, "WarnOnSizeOfConstant", WarnOnSizeOfConstant);
@@ -86,6 +87,7 @@ void SizeofExpressionCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
   Options.store(Opts, "WarnOnSizeOfPointer", WarnOnSizeOfPointer);
   Options.store(Opts, "WarnOnOffsetDividedBySizeOf",
                 WarnOnOffsetDividedBySizeOf);
+  Options.store(Opts, "WarnOnLoopExprSizeOf", WarnOnLoopExprSizeOf);
 }
 
 void SizeofExpressionCheck::registerMatchers(MatchFinder *Finder) {
@@ -93,6 +95,11 @@ void SizeofExpressionCheck::registerMatchers(MatchFinder *Finder) {
   // Some of the checks should not match in template code to avoid false
   // positives if sizeof is applied on template argument.
 
+  auto LoopExpr =
+      [](const ast_matchers::internal::Matcher<Stmt> &InnerMatcher) {
+        return stmt(anyOf(forStmt(InnerMatcher), whileStmt(InnerMatcher)));
+      };
+
   const auto IntegerExpr = ignoringParenImpCasts(integerLiteral());
   const auto ConstantExpr = ignoringParenImpCasts(
       anyOf(integerLiteral(), unaryOperator(hasUnaryOperand(IntegerExpr)),
@@ -130,6 +137,11 @@ void SizeofExpressionCheck::registerMatchers(MatchFinder *Finder) {
                        this);
   }
 
+  if (WarnOnLoopExprSizeOf) {
+    Finder->addMatcher(
+        LoopExpr(has(binaryOperator(has(SizeOfExpr)))).bind("loop-expr"), this);
+  }
+
   // Detect sizeof(kPtr) where kPtr is 'const char* kPtr = "abc"';
   const auto CharPtrType = pointerType(pointee(isAnyCharacter()));
   const auto ConstStrLiteralDecl =
@@ -353,6 +365,9 @@ void SizeofExpressionCheck::check(const MatchFinder::MatchResult &Result) {
     diag(E->getBeginLoc(),
          "suspicious usage of 'sizeof(char*)'; do you mean 'strlen'?")
         << E->getSourceRange();
+  } else if (const auto *E = Result.Nodes.getNodeAs<Stmt>("loop-expr")) {
+    diag(E->getBeginLoc(), "suspicious usage of 'sizeof' in the loop")
+        << E->getSourceRange();
   } else if (const auto *E = Result.Nodes.getNodeAs<Expr>("sizeof-pointer")) {
     if (Result.Nodes.getNodeAs<Type>("struct-type")) {
       diag(E->getBeginLoc(),
diff --git a/clang-tools-extra/clang-tidy/bugprone/SizeofExpressionCheck.h b/clang-tools-extra/clang-tidy/bugprone/SizeofExpressionCheck.h
index fbd62cb80fb2d..f7dccf39687a5 100644
--- a/clang-tools-extra/clang-tidy/bugprone/SizeofExpressionCheck.h
+++ b/clang-tools-extra/clang-tidy/bugprone/SizeofExpressionCheck.h
@@ -32,6 +32,7 @@ class SizeofExpressionCheck : public ClangTidyCheck {
   const bool WarnOnSizeOfPointerToAggregate;
   const bool WarnOnSizeOfPointer;
   const bool WarnOnOffsetDividedBySizeOf;
+  const bool WarnOnLoopExprSizeOf;
 };
 
 } // namespace clang::tidy::bugprone
diff --git a/clang-tools-extra/test/clang-tidy/checkers/bugprone/sizeof-expression.cpp b/clang-tools-extra/test/clang-tidy/checkers/bugprone/sizeof-expression.cpp
index 5e6f394152e9d..52b71277466b1 100644
--- a/clang-tools-extra/test/clang-tidy/checkers/bugprone/sizeof-expression.cpp
+++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone/sizeof-expression.cpp
@@ -164,6 +164,37 @@ int Test2(MyConstChar* A) {
   return sum;
 }
 
+struct A {
+   int array[10];
+};
+
+struct B {
+  struct A a;
+};
+
+int square(int num, struct B b) {
+    struct A arr[10];
+    // CHECK-MESSAGES: :[[@LINE+1]]:5: warning: suspicious usage of 'sizeof' in the loop [bugprone-sizeof-expression]
+    for(int i = 0; i < sizeof(arr); i++) {
+       struct A a = arr[i];
+    }
+    // CHECK-MESSAGES: :[[@LINE+2]]:24: warning: suspicious usage of 'sizeof(K)'; did you mean 'K'? [bugprone-sizeof-expression]
+    // CHECK-MESSAGES: :[[@LINE+1]]:34: warning: suspicious usage of 'sizeof(...)/sizeof(...)'; numerator is not a multiple of denominator [bugprone-sizeof-expression] 
+    for(int i = 0; i < sizeof(10)/sizeof(A); i++) {
+       struct A a = arr[i];
+    }
+    
+    for(int i = 0; i < sizeof(arr)/sizeof(A); i++) {
+       struct A a = arr[i];
+    }
+
+    // CHECK-MESSAGES: :[[@LINE+1]]:5: warning: suspicious usage of 'sizeof' in the loop [bugprone-sizeof-expression]
+    for(int j = 0; j < sizeof(b.a); j++) {
+
+    }
+    return 2;
+}
+
 template <int T>
 int Foo() { int A[T]; return sizeof(T); }
 // CHECK-MESSAGES: :[[@LINE-1]]:30: warning: suspicious usage of 'sizeof(K)'

Copy link

github-actions bot commented Jun 6, 2025

✅ With the latest revision this PR passed the C/C++ code formatter.

Copy link
Contributor

@EugeneZelenko EugeneZelenko left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please mention changes in Release Notes.

@llvmbot llvmbot added the clang Clang issues not falling into any other category label Jun 10, 2025
@malavikasamak malavikasamak changed the title [WIP] Warn about misuse of sizeof operator in loops. Warn about misuse of sizeof operator in loops. Jun 12, 2025
@malavikasamak malavikasamak changed the title Warn about misuse of sizeof operator in loops. [clang-tidy] Warn about misuse of sizeof operator in loops. Jun 12, 2025
Copy link
Contributor

@HerrCai0907 HerrCai0907 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM exception release note and doc.

CharUnits sSize = Ctx.getTypeSizeInChars(type->getElementType());
if (!sSize.isOne()) {
diag(E->getBeginLoc(), "suspicious usage of 'sizeof' in the loop")
<< E->getSourceRange();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need this line? I think this argument is never printed because we don't have %0 is message

Copy link
Contributor Author

@malavikasamak malavikasamak Jun 18, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The check here is ensuring the size of the array is not equal to the number of elements in the array. If they are equal, using the sizeof operator in the condition of the loop would be acceptable and unlikely to cause an out of bound access. I will add a comment explaining this.

@malavikasamak malavikasamak force-pushed the msamak-clang-tidy-sizeof-loops branch from 3e9e933 to 2e0e01e Compare June 18, 2025 18:33
@malavikasamak malavikasamak force-pushed the msamak-clang-tidy-sizeof-loops branch from 390cbca to 2db451f Compare June 24, 2025 18:58
@malavikasamak
Copy link
Contributor Author

malavikasamak commented Jun 24, 2025

Hi Folks, If there is no more feedback, I am thinking of merging this PR today.

@malavikasamak malavikasamak merged commit 86026ee into llvm:main Jun 25, 2025
8 checks passed
anthonyhatran pushed a commit to anthonyhatran/llvm-project that referenced this pull request Jun 26, 2025
)

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 <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
clang Clang issues not falling into any other category clang-tidy clang-tools-extra
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants