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