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
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions clang/lib/StaticAnalyzer/Checkers/GenericTaintChecker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down
42 changes: 42 additions & 0 deletions clang/test/Analysis/taint-generic.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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_freefunc(const char *fmt, ...);

void test_format_attribute_freefunc() {
int n;
fscanf(stdin, "%d", &n); // Get a tainted value.

log_freefunc("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