1*0b57cec5SDimitry Andric // TODO: header template 2*0b57cec5SDimitry Andric 3*0b57cec5SDimitry Andric #include "clang/AST/OSLog.h" 4*0b57cec5SDimitry Andric #include "clang/AST/Attr.h" 5*0b57cec5SDimitry Andric #include "clang/AST/Decl.h" 6*0b57cec5SDimitry Andric #include "clang/AST/DeclCXX.h" 7*0b57cec5SDimitry Andric #include "clang/AST/ExprObjC.h" 8*0b57cec5SDimitry Andric #include "clang/AST/FormatString.h" 9*0b57cec5SDimitry Andric #include "clang/Basic/Builtins.h" 10*0b57cec5SDimitry Andric #include "llvm/ADT/SmallBitVector.h" 11*0b57cec5SDimitry Andric 12*0b57cec5SDimitry Andric using namespace clang; 13*0b57cec5SDimitry Andric 14*0b57cec5SDimitry Andric using clang::analyze_os_log::OSLogBufferItem; 15*0b57cec5SDimitry Andric using clang::analyze_os_log::OSLogBufferLayout; 16*0b57cec5SDimitry Andric 17*0b57cec5SDimitry Andric namespace { 18*0b57cec5SDimitry Andric class OSLogFormatStringHandler 19*0b57cec5SDimitry Andric : public analyze_format_string::FormatStringHandler { 20*0b57cec5SDimitry Andric private: 21*0b57cec5SDimitry Andric struct ArgData { 22*0b57cec5SDimitry Andric const Expr *E = nullptr; 23*0b57cec5SDimitry Andric Optional<OSLogBufferItem::Kind> Kind; 24*0b57cec5SDimitry Andric Optional<unsigned> Size; 25*0b57cec5SDimitry Andric Optional<const Expr *> Count; 26*0b57cec5SDimitry Andric Optional<const Expr *> Precision; 27*0b57cec5SDimitry Andric Optional<const Expr *> FieldWidth; 28*0b57cec5SDimitry Andric unsigned char Flags = 0; 29*0b57cec5SDimitry Andric StringRef MaskType; 30*0b57cec5SDimitry Andric }; 31*0b57cec5SDimitry Andric SmallVector<ArgData, 4> ArgsData; 32*0b57cec5SDimitry Andric ArrayRef<const Expr *> Args; 33*0b57cec5SDimitry Andric 34*0b57cec5SDimitry Andric OSLogBufferItem::Kind 35*0b57cec5SDimitry Andric getKind(analyze_format_string::ConversionSpecifier::Kind K) { 36*0b57cec5SDimitry Andric switch (K) { 37*0b57cec5SDimitry Andric case clang::analyze_format_string::ConversionSpecifier::sArg: // "%s" 38*0b57cec5SDimitry Andric return OSLogBufferItem::StringKind; 39*0b57cec5SDimitry Andric case clang::analyze_format_string::ConversionSpecifier::SArg: // "%S" 40*0b57cec5SDimitry Andric return OSLogBufferItem::WideStringKind; 41*0b57cec5SDimitry Andric case clang::analyze_format_string::ConversionSpecifier::PArg: { // "%P" 42*0b57cec5SDimitry Andric return OSLogBufferItem::PointerKind; 43*0b57cec5SDimitry Andric case clang::analyze_format_string::ConversionSpecifier::ObjCObjArg: // "%@" 44*0b57cec5SDimitry Andric return OSLogBufferItem::ObjCObjKind; 45*0b57cec5SDimitry Andric case clang::analyze_format_string::ConversionSpecifier::PrintErrno: // "%m" 46*0b57cec5SDimitry Andric return OSLogBufferItem::ErrnoKind; 47*0b57cec5SDimitry Andric default: 48*0b57cec5SDimitry Andric return OSLogBufferItem::ScalarKind; 49*0b57cec5SDimitry Andric } 50*0b57cec5SDimitry Andric } 51*0b57cec5SDimitry Andric } 52*0b57cec5SDimitry Andric 53*0b57cec5SDimitry Andric public: 54*0b57cec5SDimitry Andric OSLogFormatStringHandler(ArrayRef<const Expr *> Args) : Args(Args) { 55*0b57cec5SDimitry Andric ArgsData.reserve(Args.size()); 56*0b57cec5SDimitry Andric } 57*0b57cec5SDimitry Andric 58*0b57cec5SDimitry Andric virtual bool HandlePrintfSpecifier(const analyze_printf::PrintfSpecifier &FS, 59*0b57cec5SDimitry Andric const char *StartSpecifier, 60*0b57cec5SDimitry Andric unsigned SpecifierLen) { 61*0b57cec5SDimitry Andric if (!FS.consumesDataArgument() && 62*0b57cec5SDimitry Andric FS.getConversionSpecifier().getKind() != 63*0b57cec5SDimitry Andric clang::analyze_format_string::ConversionSpecifier::PrintErrno) 64*0b57cec5SDimitry Andric return true; 65*0b57cec5SDimitry Andric 66*0b57cec5SDimitry Andric ArgsData.emplace_back(); 67*0b57cec5SDimitry Andric unsigned ArgIndex = FS.getArgIndex(); 68*0b57cec5SDimitry Andric if (ArgIndex < Args.size()) 69*0b57cec5SDimitry Andric ArgsData.back().E = Args[ArgIndex]; 70*0b57cec5SDimitry Andric 71*0b57cec5SDimitry Andric // First get the Kind 72*0b57cec5SDimitry Andric ArgsData.back().Kind = getKind(FS.getConversionSpecifier().getKind()); 73*0b57cec5SDimitry Andric if (ArgsData.back().Kind != OSLogBufferItem::ErrnoKind && 74*0b57cec5SDimitry Andric !ArgsData.back().E) { 75*0b57cec5SDimitry Andric // missing argument 76*0b57cec5SDimitry Andric ArgsData.pop_back(); 77*0b57cec5SDimitry Andric return false; 78*0b57cec5SDimitry Andric } 79*0b57cec5SDimitry Andric 80*0b57cec5SDimitry Andric switch (FS.getConversionSpecifier().getKind()) { 81*0b57cec5SDimitry Andric case clang::analyze_format_string::ConversionSpecifier::sArg: // "%s" 82*0b57cec5SDimitry Andric case clang::analyze_format_string::ConversionSpecifier::SArg: { // "%S" 83*0b57cec5SDimitry Andric auto &precision = FS.getPrecision(); 84*0b57cec5SDimitry Andric switch (precision.getHowSpecified()) { 85*0b57cec5SDimitry Andric case clang::analyze_format_string::OptionalAmount::NotSpecified: // "%s" 86*0b57cec5SDimitry Andric break; 87*0b57cec5SDimitry Andric case clang::analyze_format_string::OptionalAmount::Constant: // "%.16s" 88*0b57cec5SDimitry Andric ArgsData.back().Size = precision.getConstantAmount(); 89*0b57cec5SDimitry Andric break; 90*0b57cec5SDimitry Andric case clang::analyze_format_string::OptionalAmount::Arg: // "%.*s" 91*0b57cec5SDimitry Andric ArgsData.back().Count = Args[precision.getArgIndex()]; 92*0b57cec5SDimitry Andric break; 93*0b57cec5SDimitry Andric case clang::analyze_format_string::OptionalAmount::Invalid: 94*0b57cec5SDimitry Andric return false; 95*0b57cec5SDimitry Andric } 96*0b57cec5SDimitry Andric break; 97*0b57cec5SDimitry Andric } 98*0b57cec5SDimitry Andric case clang::analyze_format_string::ConversionSpecifier::PArg: { // "%P" 99*0b57cec5SDimitry Andric auto &precision = FS.getPrecision(); 100*0b57cec5SDimitry Andric switch (precision.getHowSpecified()) { 101*0b57cec5SDimitry Andric case clang::analyze_format_string::OptionalAmount::NotSpecified: // "%P" 102*0b57cec5SDimitry Andric return false; // length must be supplied with pointer format specifier 103*0b57cec5SDimitry Andric case clang::analyze_format_string::OptionalAmount::Constant: // "%.16P" 104*0b57cec5SDimitry Andric ArgsData.back().Size = precision.getConstantAmount(); 105*0b57cec5SDimitry Andric break; 106*0b57cec5SDimitry Andric case clang::analyze_format_string::OptionalAmount::Arg: // "%.*P" 107*0b57cec5SDimitry Andric ArgsData.back().Count = Args[precision.getArgIndex()]; 108*0b57cec5SDimitry Andric break; 109*0b57cec5SDimitry Andric case clang::analyze_format_string::OptionalAmount::Invalid: 110*0b57cec5SDimitry Andric return false; 111*0b57cec5SDimitry Andric } 112*0b57cec5SDimitry Andric break; 113*0b57cec5SDimitry Andric } 114*0b57cec5SDimitry Andric default: 115*0b57cec5SDimitry Andric if (FS.getPrecision().hasDataArgument()) { 116*0b57cec5SDimitry Andric ArgsData.back().Precision = Args[FS.getPrecision().getArgIndex()]; 117*0b57cec5SDimitry Andric } 118*0b57cec5SDimitry Andric break; 119*0b57cec5SDimitry Andric } 120*0b57cec5SDimitry Andric if (FS.getFieldWidth().hasDataArgument()) { 121*0b57cec5SDimitry Andric ArgsData.back().FieldWidth = Args[FS.getFieldWidth().getArgIndex()]; 122*0b57cec5SDimitry Andric } 123*0b57cec5SDimitry Andric 124*0b57cec5SDimitry Andric if (FS.isSensitive()) 125*0b57cec5SDimitry Andric ArgsData.back().Flags |= OSLogBufferItem::IsSensitive; 126*0b57cec5SDimitry Andric else if (FS.isPrivate()) 127*0b57cec5SDimitry Andric ArgsData.back().Flags |= OSLogBufferItem::IsPrivate; 128*0b57cec5SDimitry Andric else if (FS.isPublic()) 129*0b57cec5SDimitry Andric ArgsData.back().Flags |= OSLogBufferItem::IsPublic; 130*0b57cec5SDimitry Andric 131*0b57cec5SDimitry Andric ArgsData.back().MaskType = FS.getMaskType(); 132*0b57cec5SDimitry Andric return true; 133*0b57cec5SDimitry Andric } 134*0b57cec5SDimitry Andric 135*0b57cec5SDimitry Andric void computeLayout(ASTContext &Ctx, OSLogBufferLayout &Layout) const { 136*0b57cec5SDimitry Andric Layout.Items.clear(); 137*0b57cec5SDimitry Andric for (auto &Data : ArgsData) { 138*0b57cec5SDimitry Andric if (!Data.MaskType.empty()) { 139*0b57cec5SDimitry Andric CharUnits Size = CharUnits::fromQuantity(8); 140*0b57cec5SDimitry Andric Layout.Items.emplace_back(OSLogBufferItem::MaskKind, nullptr, 141*0b57cec5SDimitry Andric Size, 0, Data.MaskType); 142*0b57cec5SDimitry Andric } 143*0b57cec5SDimitry Andric 144*0b57cec5SDimitry Andric if (Data.FieldWidth) { 145*0b57cec5SDimitry Andric CharUnits Size = Ctx.getTypeSizeInChars((*Data.FieldWidth)->getType()); 146*0b57cec5SDimitry Andric Layout.Items.emplace_back(OSLogBufferItem::ScalarKind, *Data.FieldWidth, 147*0b57cec5SDimitry Andric Size, 0); 148*0b57cec5SDimitry Andric } 149*0b57cec5SDimitry Andric if (Data.Precision) { 150*0b57cec5SDimitry Andric CharUnits Size = Ctx.getTypeSizeInChars((*Data.Precision)->getType()); 151*0b57cec5SDimitry Andric Layout.Items.emplace_back(OSLogBufferItem::ScalarKind, *Data.Precision, 152*0b57cec5SDimitry Andric Size, 0); 153*0b57cec5SDimitry Andric } 154*0b57cec5SDimitry Andric if (Data.Count) { 155*0b57cec5SDimitry Andric // "%.*P" has an extra "count" that we insert before the argument. 156*0b57cec5SDimitry Andric CharUnits Size = Ctx.getTypeSizeInChars((*Data.Count)->getType()); 157*0b57cec5SDimitry Andric Layout.Items.emplace_back(OSLogBufferItem::CountKind, *Data.Count, Size, 158*0b57cec5SDimitry Andric 0); 159*0b57cec5SDimitry Andric } 160*0b57cec5SDimitry Andric if (Data.Size) 161*0b57cec5SDimitry Andric Layout.Items.emplace_back(Ctx, CharUnits::fromQuantity(*Data.Size), 162*0b57cec5SDimitry Andric Data.Flags); 163*0b57cec5SDimitry Andric if (Data.Kind) { 164*0b57cec5SDimitry Andric CharUnits Size; 165*0b57cec5SDimitry Andric if (*Data.Kind == OSLogBufferItem::ErrnoKind) 166*0b57cec5SDimitry Andric Size = CharUnits::Zero(); 167*0b57cec5SDimitry Andric else 168*0b57cec5SDimitry Andric Size = Ctx.getTypeSizeInChars(Data.E->getType()); 169*0b57cec5SDimitry Andric Layout.Items.emplace_back(*Data.Kind, Data.E, Size, Data.Flags); 170*0b57cec5SDimitry Andric } else { 171*0b57cec5SDimitry Andric auto Size = Ctx.getTypeSizeInChars(Data.E->getType()); 172*0b57cec5SDimitry Andric Layout.Items.emplace_back(OSLogBufferItem::ScalarKind, Data.E, Size, 173*0b57cec5SDimitry Andric Data.Flags); 174*0b57cec5SDimitry Andric } 175*0b57cec5SDimitry Andric } 176*0b57cec5SDimitry Andric } 177*0b57cec5SDimitry Andric }; 178*0b57cec5SDimitry Andric } // end anonymous namespace 179*0b57cec5SDimitry Andric 180*0b57cec5SDimitry Andric bool clang::analyze_os_log::computeOSLogBufferLayout( 181*0b57cec5SDimitry Andric ASTContext &Ctx, const CallExpr *E, OSLogBufferLayout &Layout) { 182*0b57cec5SDimitry Andric ArrayRef<const Expr *> Args(E->getArgs(), E->getArgs() + E->getNumArgs()); 183*0b57cec5SDimitry Andric 184*0b57cec5SDimitry Andric const Expr *StringArg; 185*0b57cec5SDimitry Andric ArrayRef<const Expr *> VarArgs; 186*0b57cec5SDimitry Andric switch (E->getBuiltinCallee()) { 187*0b57cec5SDimitry Andric case Builtin::BI__builtin_os_log_format_buffer_size: 188*0b57cec5SDimitry Andric assert(E->getNumArgs() >= 1 && 189*0b57cec5SDimitry Andric "__builtin_os_log_format_buffer_size takes at least 1 argument"); 190*0b57cec5SDimitry Andric StringArg = E->getArg(0); 191*0b57cec5SDimitry Andric VarArgs = Args.slice(1); 192*0b57cec5SDimitry Andric break; 193*0b57cec5SDimitry Andric case Builtin::BI__builtin_os_log_format: 194*0b57cec5SDimitry Andric assert(E->getNumArgs() >= 2 && 195*0b57cec5SDimitry Andric "__builtin_os_log_format takes at least 2 arguments"); 196*0b57cec5SDimitry Andric StringArg = E->getArg(1); 197*0b57cec5SDimitry Andric VarArgs = Args.slice(2); 198*0b57cec5SDimitry Andric break; 199*0b57cec5SDimitry Andric default: 200*0b57cec5SDimitry Andric llvm_unreachable("non-os_log builtin passed to computeOSLogBufferLayout"); 201*0b57cec5SDimitry Andric } 202*0b57cec5SDimitry Andric 203*0b57cec5SDimitry Andric const StringLiteral *Lit = cast<StringLiteral>(StringArg->IgnoreParenCasts()); 204*0b57cec5SDimitry Andric assert(Lit && (Lit->isAscii() || Lit->isUTF8())); 205*0b57cec5SDimitry Andric StringRef Data = Lit->getString(); 206*0b57cec5SDimitry Andric OSLogFormatStringHandler H(VarArgs); 207*0b57cec5SDimitry Andric ParsePrintfString(H, Data.begin(), Data.end(), Ctx.getLangOpts(), 208*0b57cec5SDimitry Andric Ctx.getTargetInfo(), /*isFreeBSDKPrintf*/ false); 209*0b57cec5SDimitry Andric 210*0b57cec5SDimitry Andric H.computeLayout(Ctx, Layout); 211*0b57cec5SDimitry Andric return true; 212*0b57cec5SDimitry Andric } 213