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