| 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 <optional> | 
|---|
| 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 | std::optional<OSLogBufferItem::Kind> Kind; | 
|---|
| 24 | std::optional<unsigned> Size; | 
|---|
| 25 | std::optional<const Expr *> Count; | 
|---|
| 26 | std::optional<const Expr *> Precision; | 
|---|
| 27 | std::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(N: Args.size()); | 
|---|
| 56 | } | 
|---|
| 57 |  | 
|---|
| 58 | bool HandlePrintfSpecifier(const analyze_printf::PrintfSpecifier &FS, | 
|---|
| 59 | const char *StartSpecifier, unsigned SpecifierLen, | 
|---|
| 60 | const TargetInfo &) 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(K: 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(Quantity: 8); | 
|---|
| 140 | Layout.Items.emplace_back(Args: OSLogBufferItem::MaskKind, Args: nullptr, | 
|---|
| 141 | Args&: Size, Args: 0, Args: Data.MaskType); | 
|---|
| 142 | } | 
|---|
| 143 |  | 
|---|
| 144 | if (Data.FieldWidth) { | 
|---|
| 145 | CharUnits Size = Ctx.getTypeSizeInChars(T: (*Data.FieldWidth)->getType()); | 
|---|
| 146 | Layout.Items.emplace_back(Args: OSLogBufferItem::ScalarKind, Args: *Data.FieldWidth, | 
|---|
| 147 | Args&: Size, Args: 0); | 
|---|
| 148 | } | 
|---|
| 149 | if (Data.Precision) { | 
|---|
| 150 | CharUnits Size = Ctx.getTypeSizeInChars(T: (*Data.Precision)->getType()); | 
|---|
| 151 | Layout.Items.emplace_back(Args: OSLogBufferItem::ScalarKind, Args: *Data.Precision, | 
|---|
| 152 | Args&: Size, Args: 0); | 
|---|
| 153 | } | 
|---|
| 154 | if (Data.Count) { | 
|---|
| 155 | // "%.*P" has an extra "count" that we insert before the argument. | 
|---|
| 156 | CharUnits Size = Ctx.getTypeSizeInChars(T: (*Data.Count)->getType()); | 
|---|
| 157 | Layout.Items.emplace_back(Args: OSLogBufferItem::CountKind, Args: *Data.Count, Args&: Size, | 
|---|
| 158 | Args: 0); | 
|---|
| 159 | } | 
|---|
| 160 | if (Data.Size) | 
|---|
| 161 | Layout.Items.emplace_back(Args&: Ctx, Args: CharUnits::fromQuantity(Quantity: *Data.Size), | 
|---|
| 162 | Args: 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(T: Data.E->getType()); | 
|---|
| 169 | Layout.Items.emplace_back(Args: *Data.Kind, Args: Data.E, Args&: Size, Args: Data.Flags); | 
|---|
| 170 | } else { | 
|---|
| 171 | auto Size = Ctx.getTypeSizeInChars(T: Data.E->getType()); | 
|---|
| 172 | Layout.Items.emplace_back(Args: OSLogBufferItem::ScalarKind, Args: Data.E, Args&: Size, | 
|---|
| 173 | Args: 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(Arg: 0); | 
|---|
| 191 | VarArgs = Args.slice(N: 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(Arg: 1); | 
|---|
| 197 | VarArgs = Args.slice(N: 2); | 
|---|
| 198 | break; | 
|---|
| 199 | default: | 
|---|
| 200 | llvm_unreachable( "non-os_log builtin passed to computeOSLogBufferLayout"); | 
|---|
| 201 | } | 
|---|
| 202 |  | 
|---|
| 203 | const StringLiteral *Lit = cast<StringLiteral>(Val: StringArg->IgnoreParenCasts()); | 
|---|
| 204 | assert(Lit && (Lit->isOrdinary() || Lit->isUTF8())); | 
|---|
| 205 | StringRef Data = Lit->getString(); | 
|---|
| 206 | OSLogFormatStringHandler H(VarArgs); | 
|---|
| 207 | ParsePrintfString(H, beg: Data.begin(), end: Data.end(), LO: Ctx.getLangOpts(), | 
|---|
| 208 | Target: Ctx.getTargetInfo(), /*isFreeBSDKPrintf*/ false); | 
|---|
| 209 |  | 
|---|
| 210 | H.computeLayout(Ctx, Layout); | 
|---|
| 211 | return true; | 
|---|
| 212 | } | 
|---|
| 213 |  | 
|---|