Skip to content

[analyzer] Fix format attribute handling in GenericTaintChecker #132765

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 3 commits into from
Mar 28, 2025

Conversation

NagyDonat
Copy link
Contributor

@NagyDonat NagyDonat commented Mar 24, 2025

Previously optin.taint.GenericTaint misinterpreted the parameter indices and produced false positives in situations when a format attribute is applied on a non-static method. This commit fixes this bug

Currently `optin.taint.GenericTaint` can produce false positives if a
[format attribute](https://clang.llvm.org/docs/AttributeReference.html#format)
is applied on a non-static method.

This commit adds a testcase that highlights this buggy behavior.
Previously the `optin.taint.GenericTaint` checker misinterpreted the
parameter indices in situations when the format attribute was applied to
a (non-static) method. This commit fixes this bug.
@NagyDonat NagyDonat changed the title [NFC][analyzer] Add testcase to highlight GenericTaint bug [analyzer] Fix format attribute handling in GenericTaintChecker Mar 24, 2025
@NagyDonat NagyDonat marked this pull request as ready for review March 24, 2025 16:50
@llvmbot llvmbot added clang Clang issues not falling into any other category clang:static analyzer labels Mar 24, 2025
@llvmbot
Copy link
Member

llvmbot commented Mar 24, 2025

@llvm/pr-subscribers-clang

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

Author: Donát Nagy (NagyDonat)

Changes

Previously optin.taint.GenericTaint misinterpreted the parameter indices and produced false positives in situations when a format attribute is applied on a non-static method. This commit fixes this bug


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

2 Files Affected:

  • (modified) clang/lib/StaticAnalyzer/Checkers/GenericTaintChecker.cpp (+16)
  • (modified) clang/test/Analysis/taint-generic.cpp (+42)
diff --git a/clang/lib/StaticAnalyzer/Checkers/GenericTaintChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/GenericTaintChecker.cpp
index b89a6e2588c98..1b61370a580d9 100644
--- a/clang/lib/StaticAnalyzer/Checkers/GenericTaintChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/GenericTaintChecker.cpp
@@ -1080,7 +1080,23 @@ static bool getPrintfFormatArgumentNum(const CallEvent &Call,
   const ArgIdxTy CallNumArgs = fromArgumentCount(Call.getNumArgs());
 
   for (const auto *Format : FDecl->specific_attrs<FormatAttr>()) {
+    // The format attribute uses 1-based parameter indexing, for example
+    // plain `printf(const char *fmt, ...)` would be annotated with
+    // `__format__(__printf__, 1, 2)`, so we need to subtract 1 to get a
+    // 0-based index. (This checker uses 0-based parameter indices.)
     ArgNum = Format->getFormatIdx() - 1;
+    // The format attribute also counts the implicit `this` parameter of
+    // methods, so e.g. in `SomeClass::method(const char *fmt, ...)` could be
+    // annotated with `__format__(__printf__, 2, 3)`. This checker doesn't
+    // count the implicit `this` parameter, so in this case we need to subtract
+    // one again.
+    // FIXME: Apparently the implementation of the format attribute doesn't
+    // support methods with an explicit object parameter, so we cannot
+    // implement proper support for that rare case either.
+    const CXXMethodDecl *MDecl = dyn_cast<CXXMethodDecl>(FDecl);
+    if (MDecl && !MDecl->isStatic())
+      ArgNum--;
+
     if ((Format->getType()->getName() == "printf") && CallNumArgs > ArgNum)
       return true;
   }
diff --git a/clang/test/Analysis/taint-generic.cpp b/clang/test/Analysis/taint-generic.cpp
index 8836e1d3d2d98..3bf3ca3a75099 100644
--- a/clang/test/Analysis/taint-generic.cpp
+++ b/clang/test/Analysis/taint-generic.cpp
@@ -161,3 +161,45 @@ void top() {
   clang_analyzer_isTainted(A.data); // expected-warning {{YES}}
 }
 } // namespace gh114270
+
+
+namespace format_attribute {
+__attribute__((__format__ (__printf__, 1, 2)))
+void log_nonmethod(const char *fmt, ...);
+
+void test_format_attribute_nonmethod() {
+  int n;
+  fscanf(stdin, "%d", &n); // Get a tainted value.
+                           
+  log_nonmethod("This number is suspicious: %d\n", n); // no-warning
+}
+
+struct Foo {
+  // When the format attribute is applied to a method, argumet '1' is the
+  // implicit `this`, so e.g. in this case argument '2' specifies `fmt`.
+  // Specifying '1' instead of '2' would produce a compilation error:
+  // "format attribute cannot specify the implicit this argument as the format string"
+  __attribute__((__format__ (__printf__, 2, 3)))
+  void log_method(const char *fmt, ...);
+
+  void test_format_attribute_method() {
+    int n;
+    fscanf(stdin, "%d", &n); // Get a tainted value.
+                             
+    // The analyzer used to misinterpret the parameter indices in the format
+    // attribute when the format attribute is applied to a method.
+    log_method("This number is suspicious: %d\n", n); // no-warning
+  }
+
+  __attribute__((__format__ (__printf__, 1, 2)))
+  static void log_static_method(const char *fmt, ...);
+
+  void test_format_attribute_static_method() {
+    int n;
+    fscanf(stdin, "%d", &n); // Get a tainted value.
+                             
+    log_static_method("This number is suspicious: %d\n", n); // no-warning
+  }
+};
+
+} // namespace format_attribute

@NagyDonat NagyDonat merged commit 50d4ae4 into llvm:main Mar 28, 2025
11 checks passed
@llvm-ci
Copy link
Collaborator

llvm-ci commented Mar 28, 2025

LLVM Buildbot has detected a new failure on builder clang-aarch64-sve-vla-2stage running on linaro-g3-01 while building clang at step 12 "ninja check 2".

Full details are available at: https://lab.llvm.org/buildbot/#/builders/41/builds/5807

Here is the relevant piece of the build log for the reference
Step 12 (ninja check 2) failure: stage 2 checked (failure)
...
PASS: DataFlowSanitizer-aarch64 :: origin_ldst.c (25155 of 97434)
PASS: Flang :: Driver/print-effective-triple.f90 (25156 of 97434)
PASS: Flang :: Driver/print-resource-dir.F90 (25157 of 97434)
PASS: Flang :: Driver/implicit-none.f90 (25158 of 97434)
PASS: Flang :: Driver/bbc-openmp-version-macro.f90 (25159 of 97434)
PASS: Flang :: Driver/predefined-macros-compiler-version.F90 (25160 of 97434)
PASS: Flang :: Driver/override-triple.ll (25161 of 97434)
PASS: Flang :: Driver/mlir-pass-pipeline.f90 (25162 of 97434)
PASS: Flang :: Driver/macro-def-undef.F90 (25163 of 97434)
UNRESOLVED: Flang :: Driver/slp-vectorize.ll (25164 of 97434)
******************** TEST 'Flang :: Driver/slp-vectorize.ll' FAILED ********************
Test has no 'RUN:' line
********************
PASS: Flang :: Driver/parse-error.ll (25165 of 97434)
PASS: Flang :: Driver/missing-arg.f90 (25166 of 97434)
PASS: Flang :: Driver/print-pipeline-passes.f90 (25167 of 97434)
PASS: Flang :: Driver/mlink-builtin-bc.f90 (25168 of 97434)
PASS: Flang :: Driver/parse-fir-error.ll (25169 of 97434)
PASS: Flang :: Driver/include-header.f90 (25170 of 97434)
PASS: Flang :: Driver/fd-lines-as.f90 (25171 of 97434)
PASS: Flang :: Driver/phases.f90 (25172 of 97434)
PASS: Flang :: Driver/print-target-triple.f90 (25173 of 97434)
PASS: Flang :: Driver/scanning-error.f95 (25174 of 97434)
PASS: Flang :: Driver/pthread.f90 (25175 of 97434)
PASS: Flang :: Driver/parse-ir-error.f95 (25176 of 97434)
PASS: Flang :: Driver/std2018-wrong.f90 (25177 of 97434)
PASS: Flang :: Driver/supported-suffices/f03-suffix.f03 (25178 of 97434)
PASS: Clangd Unit Tests :: ./ClangdTests/71/81 (25179 of 97434)
PASS: Flang :: Driver/supported-suffices/f08-suffix.f08 (25180 of 97434)
PASS: Flang :: Driver/pp-fixed-form.f90 (25181 of 97434)
PASS: Clangd Unit Tests :: ./ClangdTests/80/81 (25182 of 97434)
PASS: Flang :: Driver/target-gpu-features.f90 (25183 of 97434)
PASS: Flang :: Driver/pass-plugin-not-found.f90 (25184 of 97434)
PASS: Clangd Unit Tests :: ./ClangdTests/68/81 (25185 of 97434)
PASS: Flang :: Driver/tco-code-gen-llvm.fir (25186 of 97434)
PASS: Flang :: Driver/config-file.f90 (25187 of 97434)
PASS: Flang :: Driver/target.f90 (25188 of 97434)
PASS: Flang :: Driver/q-unused-arguments.f90 (25189 of 97434)
PASS: Flang :: Driver/multiple-input-files.f90 (25190 of 97434)
PASS: Flang :: Driver/lto-bc.f90 (25191 of 97434)
PASS: Flang :: Driver/unsupported-vscale-max-min.f90 (25192 of 97434)
PASS: Flang :: Driver/linker-flags.f90 (25193 of 97434)
PASS: Flang :: Driver/mllvm.f90 (25194 of 97434)
PASS: Flang :: Driver/no-duplicate-main.f90 (25195 of 97434)
PASS: DataFlowSanitizer-aarch64 :: pair.cpp (25196 of 97434)
PASS: Flang :: Driver/target-machine-error.f90 (25197 of 97434)
PASS: Flang :: Driver/unparse-with-modules.f90 (25198 of 97434)
PASS: Flang :: Driver/lto-flags.f90 (25199 of 97434)
PASS: Flang :: Driver/fixed-line-length.f90 (25200 of 97434)

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.

4 participants