Skip to content

Commit 50d4ae4

Browse files
authored
[analyzer] Fix format attribute handling in GenericTaintChecker (#132765)
Previously `optin.taint.GenericTaint` misinterpreted the parameter indices and produced false positives in situations when a [format attribute](https://clang.llvm.org/docs/AttributeReference.html#format) is applied on a non-static method. This commit fixes this bug
1 parent 71f43a7 commit 50d4ae4

File tree

2 files changed

+58
-0
lines changed

2 files changed

+58
-0
lines changed

clang/lib/StaticAnalyzer/Checkers/GenericTaintChecker.cpp

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1080,7 +1080,23 @@ static bool getPrintfFormatArgumentNum(const CallEvent &Call,
10801080
const ArgIdxTy CallNumArgs = fromArgumentCount(Call.getNumArgs());
10811081

10821082
for (const auto *Format : FDecl->specific_attrs<FormatAttr>()) {
1083+
// The format attribute uses 1-based parameter indexing, for example
1084+
// plain `printf(const char *fmt, ...)` would be annotated with
1085+
// `__format__(__printf__, 1, 2)`, so we need to subtract 1 to get a
1086+
// 0-based index. (This checker uses 0-based parameter indices.)
10831087
ArgNum = Format->getFormatIdx() - 1;
1088+
// The format attribute also counts the implicit `this` parameter of
1089+
// methods, so e.g. in `SomeClass::method(const char *fmt, ...)` could be
1090+
// annotated with `__format__(__printf__, 2, 3)`. This checker doesn't
1091+
// count the implicit `this` parameter, so in this case we need to subtract
1092+
// one again.
1093+
// FIXME: Apparently the implementation of the format attribute doesn't
1094+
// support methods with an explicit object parameter, so we cannot
1095+
// implement proper support for that rare case either.
1096+
const CXXMethodDecl *MDecl = dyn_cast<CXXMethodDecl>(FDecl);
1097+
if (MDecl && !MDecl->isStatic())
1098+
ArgNum--;
1099+
10841100
if ((Format->getType()->getName() == "printf") && CallNumArgs > ArgNum)
10851101
return true;
10861102
}

clang/test/Analysis/taint-generic.cpp

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,3 +161,45 @@ void top() {
161161
clang_analyzer_isTainted(A.data); // expected-warning {{YES}}
162162
}
163163
} // namespace gh114270
164+
165+
166+
namespace format_attribute {
167+
__attribute__((__format__ (__printf__, 1, 2)))
168+
void log_freefunc(const char *fmt, ...);
169+
170+
void test_format_attribute_freefunc() {
171+
int n;
172+
fscanf(stdin, "%d", &n); // Get a tainted value.
173+
174+
log_freefunc("This number is suspicious: %d\n", n); // no-warning
175+
}
176+
177+
struct Foo {
178+
// When the format attribute is applied to a method, argumet '1' is the
179+
// implicit `this`, so e.g. in this case argument '2' specifies `fmt`.
180+
// Specifying '1' instead of '2' would produce a compilation error:
181+
// "format attribute cannot specify the implicit this argument as the format string"
182+
__attribute__((__format__ (__printf__, 2, 3)))
183+
void log_method(const char *fmt, ...);
184+
185+
void test_format_attribute_method() {
186+
int n;
187+
fscanf(stdin, "%d", &n); // Get a tainted value.
188+
189+
// The analyzer used to misinterpret the parameter indices in the format
190+
// attribute when the format attribute is applied to a method.
191+
log_method("This number is suspicious: %d\n", n); // no-warning
192+
}
193+
194+
__attribute__((__format__ (__printf__, 1, 2)))
195+
static void log_static_method(const char *fmt, ...);
196+
197+
void test_format_attribute_static_method() {
198+
int n;
199+
fscanf(stdin, "%d", &n); // Get a tainted value.
200+
201+
log_static_method("This number is suspicious: %d\n", n); // no-warning
202+
}
203+
};
204+
205+
} // namespace format_attribute

0 commit comments

Comments
 (0)