forked from OSchip/llvm-project
Add support for analyzing FreeBSD kernel printf extensions.
This adds a new __freebsd_kprintf__ format string type, which enables checking when used in __attribute__((format(...))) attributes. It can check the FreeBSD kernel specific %b, %D, %r and %y specifiers, using existing diagnostic messages. Also adds test cases for all these specifiers. Differential Revision: http://reviews.llvm.org/D7154 llvm-svn: 229921
This commit is contained in:
parent
f567e1aab7
commit
6b5ed34a40
|
@ -161,6 +161,12 @@ public:
|
|||
ObjCObjArg, // '@'
|
||||
ObjCBeg = ObjCObjArg, ObjCEnd = ObjCObjArg,
|
||||
|
||||
// FreeBSD kernel specific specifiers.
|
||||
FreeBSDbArg,
|
||||
FreeBSDDArg,
|
||||
FreeBSDrArg,
|
||||
FreeBSDyArg,
|
||||
|
||||
// GlibC specific specifiers.
|
||||
PrintErrno, // 'm'
|
||||
|
||||
|
@ -204,7 +210,8 @@ public:
|
|||
return EndScanList ? EndScanList - Position : 1;
|
||||
}
|
||||
|
||||
bool isIntArg() const { return kind >= IntArgBeg && kind <= IntArgEnd; }
|
||||
bool isIntArg() const { return (kind >= IntArgBeg && kind <= IntArgEnd) ||
|
||||
kind == FreeBSDrArg || kind == FreeBSDyArg; }
|
||||
bool isUIntArg() const { return kind >= UIntArgBeg && kind <= UIntArgEnd; }
|
||||
bool isAnyIntArg() const { return kind >= IntArgBeg && kind <= UIntArgEnd; }
|
||||
const char *toString() const;
|
||||
|
@ -646,7 +653,7 @@ public:
|
|||
|
||||
bool ParsePrintfString(FormatStringHandler &H,
|
||||
const char *beg, const char *end, const LangOptions &LO,
|
||||
const TargetInfo &Target);
|
||||
const TargetInfo &Target, bool isFreeBSDKPrintf);
|
||||
|
||||
bool ParseFormatStringHasSArg(const char *beg, const char *end, const LangOptions &LO,
|
||||
const TargetInfo &Target);
|
||||
|
|
|
@ -8591,6 +8591,7 @@ public:
|
|||
FST_Strftime,
|
||||
FST_Strfmon,
|
||||
FST_Kprintf,
|
||||
FST_FreeBSDKPrintf,
|
||||
FST_Unknown
|
||||
};
|
||||
static FormatStringType GetFormatStringType(const FormatAttr *Format);
|
||||
|
|
|
@ -552,6 +552,12 @@ const char *ConversionSpecifier::toString() const {
|
|||
// Objective-C specific specifiers.
|
||||
case ObjCObjArg: return "@";
|
||||
|
||||
// FreeBSD kernel specific specifiers.
|
||||
case FreeBSDbArg: return "b";
|
||||
case FreeBSDDArg: return "D";
|
||||
case FreeBSDrArg: return "r";
|
||||
case FreeBSDyArg: return "y";
|
||||
|
||||
// GlibC specific specifiers.
|
||||
case PrintErrno: return "m";
|
||||
|
||||
|
@ -647,6 +653,9 @@ bool FormatSpecifier::hasValidLengthModifier(const TargetInfo &Target) const {
|
|||
case ConversionSpecifier::XArg:
|
||||
case ConversionSpecifier::nArg:
|
||||
return true;
|
||||
case ConversionSpecifier::FreeBSDrArg:
|
||||
case ConversionSpecifier::FreeBSDyArg:
|
||||
return Target.getTriple().isOSFreeBSD();
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
@ -677,6 +686,9 @@ bool FormatSpecifier::hasValidLengthModifier(const TargetInfo &Target) const {
|
|||
case ConversionSpecifier::ScanListArg:
|
||||
case ConversionSpecifier::ZArg:
|
||||
return true;
|
||||
case ConversionSpecifier::FreeBSDrArg:
|
||||
case ConversionSpecifier::FreeBSDyArg:
|
||||
return Target.getTriple().isOSFreeBSD();
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
@ -807,6 +819,10 @@ bool FormatSpecifier::hasStandardConversionSpecifier(const LangOptions &LangOpt)
|
|||
case ConversionSpecifier::SArg:
|
||||
return LangOpt.ObjC1 || LangOpt.ObjC2;
|
||||
case ConversionSpecifier::InvalidSpecifier:
|
||||
case ConversionSpecifier::FreeBSDbArg:
|
||||
case ConversionSpecifier::FreeBSDDArg:
|
||||
case ConversionSpecifier::FreeBSDrArg:
|
||||
case ConversionSpecifier::FreeBSDyArg:
|
||||
case ConversionSpecifier::PrintErrno:
|
||||
case ConversionSpecifier::DArg:
|
||||
case ConversionSpecifier::OArg:
|
||||
|
|
|
@ -55,7 +55,8 @@ static PrintfSpecifierResult ParsePrintfSpecifier(FormatStringHandler &H,
|
|||
unsigned &argIndex,
|
||||
const LangOptions &LO,
|
||||
const TargetInfo &Target,
|
||||
bool Warn) {
|
||||
bool Warn,
|
||||
bool isFreeBSDKPrintf) {
|
||||
|
||||
using namespace clang::analyze_format_string;
|
||||
using namespace clang::analyze_printf;
|
||||
|
@ -206,9 +207,24 @@ static PrintfSpecifierResult ParsePrintfSpecifier(FormatStringHandler &H,
|
|||
case '@': k = ConversionSpecifier::ObjCObjArg; break;
|
||||
// Glibc specific.
|
||||
case 'm': k = ConversionSpecifier::PrintErrno; break;
|
||||
// FreeBSD kernel specific.
|
||||
case 'b':
|
||||
if (isFreeBSDKPrintf)
|
||||
k = ConversionSpecifier::FreeBSDbArg; // int followed by char *
|
||||
break;
|
||||
case 'r':
|
||||
if (isFreeBSDKPrintf)
|
||||
k = ConversionSpecifier::FreeBSDrArg; // int
|
||||
break;
|
||||
case 'y':
|
||||
if (isFreeBSDKPrintf)
|
||||
k = ConversionSpecifier::FreeBSDyArg; // int
|
||||
break;
|
||||
// Apple-specific.
|
||||
case 'D':
|
||||
if (Target.getTriple().isOSDarwin())
|
||||
if (isFreeBSDKPrintf)
|
||||
k = ConversionSpecifier::FreeBSDDArg; // void * followed by char *
|
||||
else if (Target.getTriple().isOSDarwin())
|
||||
k = ConversionSpecifier::DArg;
|
||||
break;
|
||||
case 'O':
|
||||
|
@ -228,6 +244,10 @@ static PrintfSpecifierResult ParsePrintfSpecifier(FormatStringHandler &H,
|
|||
FS.setConversionSpecifier(CS);
|
||||
if (CS.consumesDataArgument() && !FS.usesPositionalArg())
|
||||
FS.setArgIndex(argIndex++);
|
||||
// FreeBSD kernel specific.
|
||||
if (k == ConversionSpecifier::FreeBSDbArg ||
|
||||
k == ConversionSpecifier::FreeBSDDArg)
|
||||
argIndex++;
|
||||
|
||||
if (k == ConversionSpecifier::InvalidSpecifier) {
|
||||
// Assume the conversion takes one argument.
|
||||
|
@ -240,14 +260,16 @@ bool clang::analyze_format_string::ParsePrintfString(FormatStringHandler &H,
|
|||
const char *I,
|
||||
const char *E,
|
||||
const LangOptions &LO,
|
||||
const TargetInfo &Target) {
|
||||
const TargetInfo &Target,
|
||||
bool isFreeBSDKPrintf) {
|
||||
|
||||
unsigned argIndex = 0;
|
||||
|
||||
// Keep looking for a format specifier until we have exhausted the string.
|
||||
while (I != E) {
|
||||
const PrintfSpecifierResult &FSR = ParsePrintfSpecifier(H, I, E, argIndex,
|
||||
LO, Target, true);
|
||||
LO, Target, true,
|
||||
isFreeBSDKPrintf);
|
||||
// Did a fail-stop error of any kind occur when parsing the specifier?
|
||||
// If so, don't do any more processing.
|
||||
if (FSR.shouldStop())
|
||||
|
@ -276,7 +298,8 @@ bool clang::analyze_format_string::ParseFormatStringHasSArg(const char *I,
|
|||
FormatStringHandler H;
|
||||
while (I != E) {
|
||||
const PrintfSpecifierResult &FSR = ParsePrintfSpecifier(H, I, E, argIndex,
|
||||
LO, Target, false);
|
||||
LO, Target, false,
|
||||
false);
|
||||
// Did a fail-stop error of any kind occur when parsing the specifier?
|
||||
// If so, don't do any more processing.
|
||||
if (FSR.shouldStop())
|
||||
|
@ -674,6 +697,8 @@ bool PrintfSpecifier::hasValidPlusPrefix() const {
|
|||
case ConversionSpecifier::GArg:
|
||||
case ConversionSpecifier::aArg:
|
||||
case ConversionSpecifier::AArg:
|
||||
case ConversionSpecifier::FreeBSDrArg:
|
||||
case ConversionSpecifier::FreeBSDyArg:
|
||||
return true;
|
||||
|
||||
default:
|
||||
|
@ -699,6 +724,8 @@ bool PrintfSpecifier::hasValidAlternativeForm() const {
|
|||
case ConversionSpecifier::FArg:
|
||||
case ConversionSpecifier::gArg:
|
||||
case ConversionSpecifier::GArg:
|
||||
case ConversionSpecifier::FreeBSDrArg:
|
||||
case ConversionSpecifier::FreeBSDyArg:
|
||||
return true;
|
||||
|
||||
default:
|
||||
|
@ -729,6 +756,8 @@ bool PrintfSpecifier::hasValidLeadingZeros() const {
|
|||
case ConversionSpecifier::FArg:
|
||||
case ConversionSpecifier::gArg:
|
||||
case ConversionSpecifier::GArg:
|
||||
case ConversionSpecifier::FreeBSDrArg:
|
||||
case ConversionSpecifier::FreeBSDyArg:
|
||||
return true;
|
||||
|
||||
default:
|
||||
|
@ -753,6 +782,8 @@ bool PrintfSpecifier::hasValidSpacePrefix() const {
|
|||
case ConversionSpecifier::GArg:
|
||||
case ConversionSpecifier::aArg:
|
||||
case ConversionSpecifier::AArg:
|
||||
case ConversionSpecifier::FreeBSDrArg:
|
||||
case ConversionSpecifier::FreeBSDyArg:
|
||||
return true;
|
||||
|
||||
default:
|
||||
|
@ -818,6 +849,8 @@ bool PrintfSpecifier::hasValidPrecision() const {
|
|||
case ConversionSpecifier::gArg:
|
||||
case ConversionSpecifier::GArg:
|
||||
case ConversionSpecifier::sArg:
|
||||
case ConversionSpecifier::FreeBSDrArg:
|
||||
case ConversionSpecifier::FreeBSDyArg:
|
||||
return true;
|
||||
|
||||
default:
|
||||
|
|
|
@ -2680,6 +2680,7 @@ Sema::FormatStringType Sema::GetFormatStringType(const FormatAttr *Format) {
|
|||
.Case("strftime", FST_Strftime)
|
||||
.Case("strfmon", FST_Strfmon)
|
||||
.Cases("kprintf", "cmn_err", "vcmn_err", "zcmn_err", FST_Kprintf)
|
||||
.Case("freebsd_kprintf", FST_FreeBSDKPrintf)
|
||||
.Default(FST_Unknown);
|
||||
}
|
||||
|
||||
|
@ -3461,6 +3462,43 @@ CheckPrintfHandler::HandlePrintfSpecifier(const analyze_printf::PrintfSpecifier
|
|||
CoveredArgs.set(argIndex);
|
||||
}
|
||||
|
||||
// FreeBSD kernel extensions.
|
||||
if (CS.getKind() == ConversionSpecifier::FreeBSDbArg ||
|
||||
CS.getKind() == ConversionSpecifier::FreeBSDDArg) {
|
||||
// We need at least two arguments.
|
||||
if (!CheckNumArgs(FS, CS, startSpecifier, specifierLen, argIndex + 1))
|
||||
return false;
|
||||
|
||||
// Claim the second argument.
|
||||
CoveredArgs.set(argIndex + 1);
|
||||
|
||||
// Type check the first argument (int for %b, pointer for %D)
|
||||
const Expr *Ex = getDataArg(argIndex);
|
||||
const analyze_printf::ArgType &AT =
|
||||
(CS.getKind() == ConversionSpecifier::FreeBSDbArg) ?
|
||||
ArgType(S.Context.IntTy) : ArgType::CPointerTy;
|
||||
if (AT.isValid() && !AT.matchesType(S.Context, Ex->getType()))
|
||||
EmitFormatDiagnostic(
|
||||
S.PDiag(diag::warn_format_conversion_argument_type_mismatch)
|
||||
<< AT.getRepresentativeTypeName(S.Context) << Ex->getType()
|
||||
<< false << Ex->getSourceRange(),
|
||||
Ex->getLocStart(), /*IsStringLocation*/false,
|
||||
getSpecifierRange(startSpecifier, specifierLen));
|
||||
|
||||
// Type check the second argument (char * for both %b and %D)
|
||||
Ex = getDataArg(argIndex + 1);
|
||||
const analyze_printf::ArgType &AT2 = ArgType::CStrTy;
|
||||
if (AT2.isValid() && !AT2.matchesType(S.Context, Ex->getType()))
|
||||
EmitFormatDiagnostic(
|
||||
S.PDiag(diag::warn_format_conversion_argument_type_mismatch)
|
||||
<< AT2.getRepresentativeTypeName(S.Context) << Ex->getType()
|
||||
<< false << Ex->getSourceRange(),
|
||||
Ex->getLocStart(), /*IsStringLocation*/false,
|
||||
getSpecifierRange(startSpecifier, specifierLen));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check for using an Objective-C specific conversion specifier
|
||||
// in a non-ObjC literal.
|
||||
if (!ObjCContext && CS.isObjCArg()) {
|
||||
|
@ -4084,7 +4122,8 @@ void Sema::CheckFormatString(const StringLiteral *FExpr,
|
|||
return;
|
||||
}
|
||||
|
||||
if (Type == FST_Printf || Type == FST_NSString) {
|
||||
if (Type == FST_Printf || Type == FST_NSString ||
|
||||
Type == FST_FreeBSDKPrintf) {
|
||||
CheckPrintfHandler H(*this, FExpr, OrigFormatExpr, firstDataArg,
|
||||
numDataArgs, (Type == FST_NSString),
|
||||
Str, HasVAListArg, Args, format_idx,
|
||||
|
@ -4092,7 +4131,8 @@ void Sema::CheckFormatString(const StringLiteral *FExpr,
|
|||
|
||||
if (!analyze_format_string::ParsePrintfString(H, Str, Str + StrLen,
|
||||
getLangOpts(),
|
||||
Context.getTargetInfo()))
|
||||
Context.getTargetInfo(),
|
||||
Type == FST_FreeBSDKPrintf))
|
||||
H.DoneProcessing();
|
||||
} else if (Type == FST_Scanf) {
|
||||
CheckScanfHandler H(*this, FExpr, OrigFormatExpr, firstDataArg, numDataArgs,
|
||||
|
|
|
@ -2493,6 +2493,7 @@ static FormatAttrKind getFormatAttrKind(StringRef Format) {
|
|||
.Cases("scanf", "printf", "printf0", "strfmon", SupportedFormat)
|
||||
.Cases("cmn_err", "vcmn_err", "zcmn_err", SupportedFormat)
|
||||
.Case("kprintf", SupportedFormat) // OpenBSD.
|
||||
.Case("freebsd_kprintf", SupportedFormat) // FreeBSD.
|
||||
|
||||
.Cases("gcc_diag", "gcc_cdiag", "gcc_cxxdiag", "gcc_tdiag", IgnoredFormat)
|
||||
.Default(InvalidFormat);
|
||||
|
|
|
@ -57,6 +57,13 @@ void callnull(void){
|
|||
null(0, (int*)0); // expected-warning {{incompatible pointer types}}
|
||||
}
|
||||
|
||||
// FreeBSD kernel extensions
|
||||
void a3(const char *a, ...) __attribute__((format(freebsd_kprintf, 1,2))); // no-error
|
||||
void b3(const char *a, ...) __attribute__((format(freebsd_kprintf, 1,1))); // expected-error {{'format' attribute parameter 3 is out of bounds}}
|
||||
void c3(const char *a, ...) __attribute__((format(freebsd_kprintf, 0,2))); // expected-error {{'format' attribute parameter 2 is out of bounds}}
|
||||
void d3(const char *a, int c) __attribute__((format(freebsd_kprintf, 1,2))); // expected-error {{format attribute requires variadic function}}
|
||||
void e3(char *str, int c, ...) __attribute__((format(freebsd_kprintf, 2,3))); // expected-error {{format argument not a string type}}
|
||||
|
||||
|
||||
|
||||
// PR4470
|
||||
|
|
|
@ -0,0 +1,40 @@
|
|||
// RUN: %clang_cc1 -fsyntax-only -verify -triple i386-unknown-freebsd %s
|
||||
// RUN: %clang_cc1 -fsyntax-only -verify -triple x86_64-unknown-freebsd %s
|
||||
|
||||
// Test FreeBSD kernel printf extensions.
|
||||
int freebsd_kernel_printf(const char *, ...) __attribute__((__format__(__freebsd_kprintf__, 1, 2)));
|
||||
|
||||
void check_freebsd_kernel_extensions(int i, long l, char *s)
|
||||
{
|
||||
// %b expects an int and a char *
|
||||
freebsd_kernel_printf("reg=%b\n", i, "\10\2BITTWO\1BITONE\n"); // no-warning
|
||||
freebsd_kernel_printf("reg=%b\n", l, "\10\2BITTWO\1BITONE\n"); // expected-warning{{format specifies type 'int' but the argument has type 'long'}}
|
||||
freebsd_kernel_printf("reg=%b\n", i, l); // expected-warning{{format specifies type 'char *' but the argument has type 'long'}}
|
||||
freebsd_kernel_printf("reg=%b\n", i); // expected-warning{{more '%' conversions than data arguments}}
|
||||
freebsd_kernel_printf("reg=%b\n", i, "\10\2BITTWO\1BITONE\n", l); // expected-warning{{data argument not used by format string}}
|
||||
|
||||
// %D expects an unsigned char * and a char *
|
||||
freebsd_kernel_printf("%6D", s, ":"); // no-warning
|
||||
freebsd_kernel_printf("%6D", i, ":"); // expected-warning{{format specifies type 'void *' but the argument has type 'int'}}
|
||||
freebsd_kernel_printf("%6D", s, i); // expected-warning{{format specifies type 'char *' but the argument has type 'int'}}
|
||||
freebsd_kernel_printf("%6D", s); // expected-warning{{more '%' conversions than data arguments}}
|
||||
freebsd_kernel_printf("%6D", s, ":", i); // expected-warning{{data argument not used by format string}}
|
||||
|
||||
freebsd_kernel_printf("%*D", 42, s, ":"); // no-warning
|
||||
freebsd_kernel_printf("%*D", 42, i, ":"); // expected-warning{{format specifies type 'void *' but the argument has type 'int'}}
|
||||
freebsd_kernel_printf("%*D", 42, s, i); // expected-warning{{format specifies type 'char *' but the argument has type 'int'}}
|
||||
freebsd_kernel_printf("%*D", 42, s); // expected-warning{{more '%' conversions than data arguments}}
|
||||
freebsd_kernel_printf("%*D", 42, s, ":", i); // expected-warning{{data argument not used by format string}}
|
||||
|
||||
// %r expects an int
|
||||
freebsd_kernel_printf("%r", i); // no-warning
|
||||
freebsd_kernel_printf("%r", l); // expected-warning{{format specifies type 'int' but the argument has type 'long'}}
|
||||
freebsd_kernel_printf("%lr", i); // expected-warning{{format specifies type 'long' but the argument has type 'int'}}
|
||||
freebsd_kernel_printf("%lr", l); // no-warning
|
||||
|
||||
// %y expects an int
|
||||
freebsd_kernel_printf("%y", i); // no-warning
|
||||
freebsd_kernel_printf("%y", l); // expected-warning{{format specifies type 'int' but the argument has type 'long'}}
|
||||
freebsd_kernel_printf("%ly", i); // expected-warning{{format specifies type 'long' but the argument has type 'int'}}
|
||||
freebsd_kernel_printf("%ly", l); // no-warning
|
||||
}
|
Loading…
Reference in New Issue