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