diff options
Diffstat (limited to 'clang/lib/Analysis')
-rw-r--r-- | clang/lib/Analysis/CMakeLists.txt | 1 | ||||
-rw-r--r-- | clang/lib/Analysis/FormatString.cpp | 3 | ||||
-rw-r--r-- | clang/lib/Analysis/OSLog.cpp | 177 | ||||
-rw-r--r-- | clang/lib/Analysis/PrintfFormatString.cpp | 43 |
4 files changed, 222 insertions, 2 deletions
diff --git a/clang/lib/Analysis/CMakeLists.txt b/clang/lib/Analysis/CMakeLists.txt index 6c74c797d9b..fdc9e6cee8e 100644 --- a/clang/lib/Analysis/CMakeLists.txt +++ b/clang/lib/Analysis/CMakeLists.txt @@ -16,6 +16,7 @@ add_clang_library(clangAnalysis Dominators.cpp FormatString.cpp LiveVariables.cpp + OSLog.cpp ObjCNoReturn.cpp PostOrderCFGView.cpp PrintfFormatString.cpp diff --git a/clang/lib/Analysis/FormatString.cpp b/clang/lib/Analysis/FormatString.cpp index 2a518cac394..c62e537e92d 100644 --- a/clang/lib/Analysis/FormatString.cpp +++ b/clang/lib/Analysis/FormatString.cpp @@ -591,6 +591,8 @@ const char *ConversionSpecifier::toString() const { case cArg: return "c"; case sArg: return "s"; case pArg: return "p"; + case PArg: + return "P"; case nArg: return "n"; case PercentArg: return "%"; case ScanListArg: return "["; @@ -866,6 +868,7 @@ bool FormatSpecifier::hasStandardConversionSpecifier( case ConversionSpecifier::ObjCObjArg: case ConversionSpecifier::ScanListArg: case ConversionSpecifier::PercentArg: + case ConversionSpecifier::PArg: return true; case ConversionSpecifier::CArg: case ConversionSpecifier::SArg: diff --git a/clang/lib/Analysis/OSLog.cpp b/clang/lib/Analysis/OSLog.cpp new file mode 100644 index 00000000000..b57e0d1d13f --- /dev/null +++ b/clang/lib/Analysis/OSLog.cpp @@ -0,0 +1,177 @@ +// TODO: header template + +#include "clang/Analysis/Analyses/OSLog.h" +#include "clang/AST/Attr.h" +#include "clang/AST/Decl.h" +#include "clang/AST/DeclCXX.h" +#include "clang/AST/ExprObjC.h" +#include "clang/Analysis/Analyses/FormatString.h" +#include "clang/Basic/Builtins.h" +#include "llvm/ADT/SmallBitVector.h" + +using namespace clang; +using llvm::APInt; + +using clang::analyze_os_log::OSLogBufferItem; +using clang::analyze_os_log::OSLogBufferLayout; + +class OSLogFormatStringHandler + : public analyze_format_string::FormatStringHandler { +private: + struct ArgData { + const Expr *E = nullptr; + Optional<OSLogBufferItem::Kind> Kind; + Optional<unsigned> Size; + unsigned char Flags = 0; + }; + SmallVector<ArgData, 4> ArgsData; + ArrayRef<const Expr *> Args; + + OSLogBufferItem::Kind + getKind(analyze_format_string::ConversionSpecifier::Kind K) { + switch (K) { + case clang::analyze_format_string::ConversionSpecifier::sArg: // "%s" + return OSLogBufferItem::StringKind; + case clang::analyze_format_string::ConversionSpecifier::SArg: // "%S" + return OSLogBufferItem::WideStringKind; + case clang::analyze_format_string::ConversionSpecifier::PArg: { // "%P" + return OSLogBufferItem::PointerKind; + case clang::analyze_format_string::ConversionSpecifier::ObjCObjArg: // "%@" + return OSLogBufferItem::ObjCObjKind; + case clang::analyze_format_string::ConversionSpecifier::PrintErrno: // "%m" + return OSLogBufferItem::ErrnoKind; + default: + return OSLogBufferItem::ScalarKind; + } + } + } + +public: + OSLogFormatStringHandler(ArrayRef<const Expr *> Args) : Args(Args) { + ArgsData.reserve(Args.size()); + } + + virtual bool HandlePrintfSpecifier(const analyze_printf::PrintfSpecifier &FS, + const char *StartSpecifier, + unsigned SpecifierLen) { + if (!FS.consumesDataArgument() && + FS.getConversionSpecifier().getKind() != + clang::analyze_format_string::ConversionSpecifier::PrintErrno) + return false; + + ArgsData.emplace_back(); + unsigned ArgIndex = FS.getArgIndex(); + if (ArgIndex < Args.size()) + ArgsData.back().E = Args[ArgIndex]; + + // First get the Kind + ArgsData.back().Kind = getKind(FS.getConversionSpecifier().getKind()); + if (ArgsData.back().Kind != OSLogBufferItem::ErrnoKind && + !ArgsData.back().E) { + // missing argument + ArgsData.pop_back(); + return false; + } + + switch (FS.getConversionSpecifier().getKind()) { + case clang::analyze_format_string::ConversionSpecifier::sArg: // "%s" + case clang::analyze_format_string::ConversionSpecifier::SArg: { // "%S" + auto &precision = FS.getPrecision(); + switch (precision.getHowSpecified()) { + case clang::analyze_format_string::OptionalAmount::NotSpecified: // "%s" + break; + case clang::analyze_format_string::OptionalAmount::Constant: // "%.16s" + ArgsData.back().Size = precision.getConstantAmount(); + break; + case clang::analyze_format_string::OptionalAmount::Arg: // "%.*s" + ArgsData.back().Kind = OSLogBufferItem::CountKind; + break; + case clang::analyze_format_string::OptionalAmount::Invalid: + return false; + } + break; + } + case clang::analyze_format_string::ConversionSpecifier::PArg: { // "%P" + auto &precision = FS.getPrecision(); + switch (precision.getHowSpecified()) { + case clang::analyze_format_string::OptionalAmount::NotSpecified: // "%P" + return false; // length must be supplied with pointer format specifier + case clang::analyze_format_string::OptionalAmount::Constant: // "%.16P" + ArgsData.back().Size = precision.getConstantAmount(); + break; + case clang::analyze_format_string::OptionalAmount::Arg: // "%.*P" + ArgsData.back().Kind = OSLogBufferItem::CountKind; + break; + case clang::analyze_format_string::OptionalAmount::Invalid: + return false; + } + break; + } + default: + break; + } + + if (FS.isPrivate()) { + ArgsData.back().Flags |= OSLogBufferItem::IsPrivate; + } + if (FS.isPublic()) { + ArgsData.back().Flags |= OSLogBufferItem::IsPublic; + } + return true; + } + + void computeLayout(ASTContext &Ctx, OSLogBufferLayout &Layout) const { + Layout.Items.clear(); + for (auto &Data : ArgsData) { + if (Data.Size) + Layout.Items.emplace_back(Ctx, CharUnits::fromQuantity(*Data.Size), + Data.Flags); + if (Data.Kind) { + CharUnits Size; + if (*Data.Kind == OSLogBufferItem::ErrnoKind) + Size = CharUnits::Zero(); + else + Size = Ctx.getTypeSizeInChars(Data.E->getType()); + Layout.Items.emplace_back(*Data.Kind, Data.E, Size, Data.Flags); + } else { + auto Size = Ctx.getTypeSizeInChars(Data.E->getType()); + Layout.Items.emplace_back(OSLogBufferItem::ScalarKind, Data.E, Size, + Data.Flags); + } + } + } +}; + +bool clang::analyze_os_log::computeOSLogBufferLayout( + ASTContext &Ctx, const CallExpr *E, OSLogBufferLayout &Layout) { + ArrayRef<const Expr *> Args(E->getArgs(), E->getArgs() + E->getNumArgs()); + + const Expr *StringArg; + ArrayRef<const Expr *> VarArgs; + switch (E->getBuiltinCallee()) { + case Builtin::BI__builtin_os_log_format_buffer_size: + assert(E->getNumArgs() >= 1 && + "__builtin_os_log_format_buffer_size takes at least 1 argument"); + StringArg = E->getArg(0); + VarArgs = Args.slice(1); + break; + case Builtin::BI__builtin_os_log_format: + assert(E->getNumArgs() >= 2 && + "__builtin_os_log_format takes at least 2 arguments"); + StringArg = E->getArg(1); + VarArgs = Args.slice(2); + break; + default: + llvm_unreachable("non-os_log builtin passed to computeOSLogBufferLayout"); + } + + const StringLiteral *Lit = cast<StringLiteral>(StringArg->IgnoreParenCasts()); + assert(Lit && (Lit->isAscii() || Lit->isUTF8())); + StringRef Data = Lit->getString(); + OSLogFormatStringHandler H(VarArgs); + ParsePrintfString(H, Data.begin(), Data.end(), Ctx.getLangOpts(), + Ctx.getTargetInfo(), /*isFreeBSDKPrintf*/ false); + + H.computeLayout(Ctx, Layout); + return true; +} diff --git a/clang/lib/Analysis/PrintfFormatString.cpp b/clang/lib/Analysis/PrintfFormatString.cpp index ac6cef9d084..ed7193ecb43 100644 --- a/clang/lib/Analysis/PrintfFormatString.cpp +++ b/clang/lib/Analysis/PrintfFormatString.cpp @@ -119,6 +119,39 @@ static PrintfSpecifierResult ParsePrintfSpecifier(FormatStringHandler &H, return true; } + const char *OSLogVisibilityFlagsStart = nullptr, + *OSLogVisibilityFlagsEnd = nullptr; + if (*I == '{') { + OSLogVisibilityFlagsStart = I++; + // Find the end of the modifier. + while (I != E && *I != '}') { + I++; + } + if (I == E) { + if (Warn) + H.HandleIncompleteSpecifier(Start, E - Start); + return true; + } + assert(*I == '}'); + OSLogVisibilityFlagsEnd = I++; + + // Just see if 'private' or 'public' is the first word. os_log itself will + // do any further parsing. + const char *P = OSLogVisibilityFlagsStart + 1; + while (P < OSLogVisibilityFlagsEnd && isspace(*P)) + P++; + const char *WordStart = P; + while (P < OSLogVisibilityFlagsEnd && (isalnum(*P) || *P == '_')) + P++; + const char *WordEnd = P; + StringRef Word(WordStart, WordEnd - WordStart); + if (Word == "private") { + FS.setIsPrivate(WordStart); + } else if (Word == "public") { + FS.setIsPublic(WordStart); + } + } + // Look for flags (if any). bool hasMore = true; for ( ; I != E; ++I) { @@ -253,6 +286,10 @@ static PrintfSpecifierResult ParsePrintfSpecifier(FormatStringHandler &H, // POSIX specific. case 'C': k = ConversionSpecifier::CArg; break; case 'S': k = ConversionSpecifier::SArg; break; + // Apple extension for os_log + case 'P': + k = ConversionSpecifier::PArg; + break; // Objective-C. case '@': k = ConversionSpecifier::ObjCObjArg; break; // Glibc specific. @@ -301,7 +338,7 @@ static PrintfSpecifierResult ParsePrintfSpecifier(FormatStringHandler &H, conversionPosition); return true; } - + PrintfConversionSpecifier CS(conversionPosition, k); FS.setConversionSpecifier(CS); if (CS.consumesDataArgument() && !FS.usesPositionalArg()) @@ -541,6 +578,7 @@ ArgType PrintfSpecifier::getArgType(ASTContext &Ctx, return Ctx.IntTy; return ArgType(Ctx.WideCharTy, "wchar_t"); case ConversionSpecifier::pArg: + case ConversionSpecifier::PArg: return ArgType::CPointerTy; case ConversionSpecifier::ObjCObjArg: return ArgType::ObjCPointerTy; @@ -900,7 +938,7 @@ bool PrintfSpecifier::hasValidPrecision() const { if (Precision.getHowSpecified() == OptionalAmount::NotSpecified) return true; - // Precision is only valid with the diouxXaAeEfFgGs conversions + // Precision is only valid with the diouxXaAeEfFgGsP conversions switch (CS.getKind()) { case ConversionSpecifier::dArg: case ConversionSpecifier::DArg: @@ -922,6 +960,7 @@ bool PrintfSpecifier::hasValidPrecision() const { case ConversionSpecifier::sArg: case ConversionSpecifier::FreeBSDrArg: case ConversionSpecifier::FreeBSDyArg: + case ConversionSpecifier::PArg: return true; default: |