From 16250c7c1847184f543d8a837512765cd6eb4bda Mon Sep 17 00:00:00 2001 From: Hans Wennborg Date: Tue, 31 Jul 2012 16:37:47 +0000 Subject: [PATCH] -Wformat: better handling of qualifiers on pointer arguments Warn about using pointers to const-qualified types as arguments to scanf. Ignore the volatile qualifier when checking if types match. llvm-svn: 161052 --- clang/lib/Analysis/FormatString.cpp | 7 +++++++ clang/lib/Analysis/ScanfFormatString.cpp | 12 +++++++++--- clang/test/Sema/format-strings-scanf.c | 18 ++++++++++++++++++ clang/test/Sema/format-strings.c | 16 ++++++++++++++++ 4 files changed, 50 insertions(+), 3 deletions(-) diff --git a/clang/lib/Analysis/FormatString.cpp b/clang/lib/Analysis/FormatString.cpp index a68b9bb3240f..f03a84fb523e 100644 --- a/clang/lib/Analysis/FormatString.cpp +++ b/clang/lib/Analysis/FormatString.cpp @@ -262,6 +262,13 @@ bool ArgTypeResult::matchesType(ASTContext &C, QualType argTy) const { argTy = ETy->getDecl()->getIntegerType(); argTy = C.getCanonicalType(argTy).getUnqualifiedType(); + if (const PointerType *PTy = argTy->getAs()) { + // Strip volatile qualifier from pointee type. + QualType Pointee = PTy->getPointeeType(); + Pointee.removeLocalVolatile(); + argTy = C.getPointerType(Pointee); + } + if (T == argTy) return true; // Check for "compatible types". diff --git a/clang/lib/Analysis/ScanfFormatString.cpp b/clang/lib/Analysis/ScanfFormatString.cpp index 3c848f1f093d..1bbd5f2d296e 100644 --- a/clang/lib/Analysis/ScanfFormatString.cpp +++ b/clang/lib/Analysis/ScanfFormatString.cpp @@ -453,6 +453,15 @@ bool clang::analyze_format_string::ParseScanfString(FormatStringHandler &H, } bool ScanfArgTypeResult::matchesType(ASTContext& C, QualType argTy) const { + // It has to be a pointer type. + const PointerType *PT = argTy->getAs(); + if (!PT) + return false; + + // We cannot write through a const qualified pointer. + if (PT->getPointeeType().isConstQualified()) + return false; + switch (K) { case InvalidTy: llvm_unreachable("ArgTypeResult must be valid"); @@ -463,9 +472,6 @@ bool ScanfArgTypeResult::matchesType(ASTContext& C, QualType argTy) const { case WCStrTy: return ArgTypeResult(ArgTypeResult::WCStrTy).matchesType(C, argTy); case PtrToArgTypeResultTy: { - const PointerType *PT = argTy->getAs(); - if (!PT) - return false; return A.matchesType(C, PT->getPointeeType()); } } diff --git a/clang/test/Sema/format-strings-scanf.c b/clang/test/Sema/format-strings-scanf.c index 2ce94840a6f1..6f6cb10eb384 100644 --- a/clang/test/Sema/format-strings-scanf.c +++ b/clang/test/Sema/format-strings-scanf.c @@ -126,3 +126,21 @@ void test_writeback(int *x) { scanf("%n", (void*)0); // expected-warning{{format specifies type 'int *' but the argument has type 'void *'}} scanf("%n %c", x, x); // expected-warning{{format specifies type 'char *' but the argument has type 'int *'}} } + +void test_qualifiers(const int *cip, volatile int* vip, + const char *ccp, volatile char* vcp, + const volatile int *cvip) { + scanf("%d", cip); // expected-warning{{format specifies type 'int *' but the argument has type 'const int *'}} + scanf("%n", cip); // expected-warning{{format specifies type 'int *' but the argument has type 'const int *'}} + scanf("%s", ccp); // expected-warning{{format specifies type 'char *' but the argument has type 'const char *'}} + scanf("%d", cvip); // expected-warning{{format specifies type 'int *' but the argument has type 'const volatile int *'}} + + scanf("%d", vip); // No warning. + scanf("%n", vip); // No warning. + scanf("%c", vcp); // No warning. + + typedef int* ip_t; + typedef const int* cip_t; + scanf("%d", (ip_t)0); // No warning. + scanf("%d", (cip_t)0); // expected-warning{{format specifies type 'int *' but the argument has type 'cip_t' (aka 'const int *')}} +} diff --git a/clang/test/Sema/format-strings.c b/clang/test/Sema/format-strings.c index d35125833c2b..aff996fed3ee 100644 --- a/clang/test/Sema/format-strings.c +++ b/clang/test/Sema/format-strings.c @@ -555,3 +555,19 @@ void test14_zed(int *p) { test14_foo("%", "%d", p); // expected-warning{{incomplete format specifier}} test14_bar("%", "%d", p); // expected-warning{{incomplete format specifier}} } + +void test_qualifiers(volatile int *vip, const int *cip, + const volatile int *cvip) { + printf("%n", cip); // expected-warning{{format specifies type 'int *' but the argument has type 'const int *'}} + printf("%n", cvip); // expected-warning{{format specifies type 'int *' but the argument has type 'const volatile int *'}} + + printf("%n", vip); // No warning. + printf("%p", cip); // No warning. + printf("%p", cvip); // No warning. + + + typedef int* ip_t; + typedef const int* cip_t; + printf("%n", (ip_t)0); // No warning. + printf("%n", (cip_t)0); // expected-warning{{format specifies type 'int *' but the argument has type 'cip_t' (aka 'const int *')}} +}