r229921 - Add support for analyzing FreeBSD kernel printf extensions.

Dimitry Andric dimitry at andric.com
Thu Feb 19 14:32:34 PST 2015


Author: dim
Date: Thu Feb 19 16:32:33 2015
New Revision: 229921

URL: http://llvm.org/viewvc/llvm-project?rev=229921&view=rev
Log:
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

Added:
    cfe/trunk/test/Sema/format-strings-freebsd.c
Modified:
    cfe/trunk/include/clang/Analysis/Analyses/FormatString.h
    cfe/trunk/include/clang/Sema/Sema.h
    cfe/trunk/lib/Analysis/FormatString.cpp
    cfe/trunk/lib/Analysis/PrintfFormatString.cpp
    cfe/trunk/lib/Sema/SemaChecking.cpp
    cfe/trunk/lib/Sema/SemaDeclAttr.cpp
    cfe/trunk/test/Sema/attr-format.c

Modified: cfe/trunk/include/clang/Analysis/Analyses/FormatString.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Analysis/Analyses/FormatString.h?rev=229921&r1=229920&r2=229921&view=diff
==============================================================================
--- cfe/trunk/include/clang/Analysis/Analyses/FormatString.h (original)
+++ cfe/trunk/include/clang/Analysis/Analyses/FormatString.h Thu Feb 19 16:32:33 2015
@@ -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);

Modified: cfe/trunk/include/clang/Sema/Sema.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Sema/Sema.h?rev=229921&r1=229920&r2=229921&view=diff
==============================================================================
--- cfe/trunk/include/clang/Sema/Sema.h (original)
+++ cfe/trunk/include/clang/Sema/Sema.h Thu Feb 19 16:32:33 2015
@@ -8591,6 +8591,7 @@ public:
     FST_Strftime,
     FST_Strfmon,
     FST_Kprintf,
+    FST_FreeBSDKPrintf,
     FST_Unknown
   };
   static FormatStringType GetFormatStringType(const FormatAttr *Format);

Modified: cfe/trunk/lib/Analysis/FormatString.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Analysis/FormatString.cpp?rev=229921&r1=229920&r2=229921&view=diff
==============================================================================
--- cfe/trunk/lib/Analysis/FormatString.cpp (original)
+++ cfe/trunk/lib/Analysis/FormatString.cpp Thu Feb 19 16:32:33 2015
@@ -552,6 +552,12 @@ const char *ConversionSpecifier::toStrin
   // 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::hasValidLengthModi
         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::hasValidLengthModi
         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::hasStandardConvers
     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:

Modified: cfe/trunk/lib/Analysis/PrintfFormatString.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Analysis/PrintfFormatString.cpp?rev=229921&r1=229920&r2=229921&view=diff
==============================================================================
--- cfe/trunk/lib/Analysis/PrintfFormatString.cpp (original)
+++ cfe/trunk/lib/Analysis/PrintfFormatString.cpp Thu Feb 19 16:32:33 2015
@@ -55,7 +55,8 @@ static PrintfSpecifierResult ParsePrintf
                                                   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 ParsePrintf
     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 ParsePrintf
   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::Parse
                                                      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::Parse
   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
   case ConversionSpecifier::GArg:
   case ConversionSpecifier::aArg:
   case ConversionSpecifier::AArg:
+  case ConversionSpecifier::FreeBSDrArg:
+  case ConversionSpecifier::FreeBSDyArg:
     return true;
 
   default:
@@ -699,6 +724,8 @@ bool PrintfSpecifier::hasValidAlternativ
   case ConversionSpecifier::FArg:
   case ConversionSpecifier::gArg:
   case ConversionSpecifier::GArg:
+  case ConversionSpecifier::FreeBSDrArg:
+  case ConversionSpecifier::FreeBSDyArg:
     return true;
 
   default:
@@ -729,6 +756,8 @@ bool PrintfSpecifier::hasValidLeadingZer
   case ConversionSpecifier::FArg:
   case ConversionSpecifier::gArg:
   case ConversionSpecifier::GArg:
+  case ConversionSpecifier::FreeBSDrArg:
+  case ConversionSpecifier::FreeBSDyArg:
     return true;
 
   default:
@@ -753,6 +782,8 @@ bool PrintfSpecifier::hasValidSpacePrefi
   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(
   case ConversionSpecifier::gArg:
   case ConversionSpecifier::GArg:
   case ConversionSpecifier::sArg:
+  case ConversionSpecifier::FreeBSDrArg:
+  case ConversionSpecifier::FreeBSDyArg:
     return true;
 
   default:

Modified: cfe/trunk/lib/Sema/SemaChecking.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaChecking.cpp?rev=229921&r1=229920&r2=229921&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaChecking.cpp (original)
+++ cfe/trunk/lib/Sema/SemaChecking.cpp Thu Feb 19 16:32:33 2015
@@ -2680,6 +2680,7 @@ Sema::FormatStringType Sema::GetFormatSt
   .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::HandlePrintfSpecifie
     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 Strin
     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 Strin
   
     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,

Modified: cfe/trunk/lib/Sema/SemaDeclAttr.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaDeclAttr.cpp?rev=229921&r1=229920&r2=229921&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaDeclAttr.cpp (original)
+++ cfe/trunk/lib/Sema/SemaDeclAttr.cpp Thu Feb 19 16:32:33 2015
@@ -2493,6 +2493,7 @@ static FormatAttrKind getFormatAttrKind(
     .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);

Modified: cfe/trunk/test/Sema/attr-format.c
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Sema/attr-format.c?rev=229921&r1=229920&r2=229921&view=diff
==============================================================================
--- cfe/trunk/test/Sema/attr-format.c (original)
+++ cfe/trunk/test/Sema/attr-format.c Thu Feb 19 16:32:33 2015
@@ -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

Added: cfe/trunk/test/Sema/format-strings-freebsd.c
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Sema/format-strings-freebsd.c?rev=229921&view=auto
==============================================================================
--- cfe/trunk/test/Sema/format-strings-freebsd.c (added)
+++ cfe/trunk/test/Sema/format-strings-freebsd.c Thu Feb 19 16:32:33 2015
@@ -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
+}





More information about the cfe-commits mailing list