1 | |
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: |
38 | return OSLogBufferItem::StringKind; |
39 | case clang::analyze_format_string::ConversionSpecifier::SArg: |
40 | return OSLogBufferItem::WideStringKind; |
41 | case clang::analyze_format_string::ConversionSpecifier::PArg: { |
42 | return OSLogBufferItem::PointerKind; |
43 | case clang::analyze_format_string::ConversionSpecifier::ObjCObjArg: |
44 | return OSLogBufferItem::ObjCObjKind; |
45 | case clang::analyze_format_string::ConversionSpecifier::PrintErrno: |
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 | virtual bool HandlePrintfSpecifier(const analyze_printf::PrintfSpecifier &FS, |
59 | const char *StartSpecifier, |
60 | unsigned SpecifierLen) { |
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 | |
72 | ArgsData.back().Kind = getKind(FS.getConversionSpecifier().getKind()); |
73 | if (ArgsData.back().Kind != OSLogBufferItem::ErrnoKind && |
74 | !ArgsData.back().E) { |
75 | |
76 | ArgsData.pop_back(); |
77 | return false; |
78 | } |
79 | |
80 | switch (FS.getConversionSpecifier().getKind()) { |
81 | case clang::analyze_format_string::ConversionSpecifier::sArg: |
82 | case clang::analyze_format_string::ConversionSpecifier::SArg: { |
83 | auto &precision = FS.getPrecision(); |
84 | switch (precision.getHowSpecified()) { |
85 | case clang::analyze_format_string::OptionalAmount::NotSpecified: |
86 | break; |
87 | case clang::analyze_format_string::OptionalAmount::Constant: |
88 | ArgsData.back().Size = precision.getConstantAmount(); |
89 | break; |
90 | case clang::analyze_format_string::OptionalAmount::Arg: |
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: { |
99 | auto &precision = FS.getPrecision(); |
100 | switch (precision.getHowSpecified()) { |
101 | case clang::analyze_format_string::OptionalAmount::NotSpecified: |
102 | return false; |
103 | case clang::analyze_format_string::OptionalAmount::Constant: |
104 | ArgsData.back().Size = precision.getConstantAmount(); |
105 | break; |
106 | case clang::analyze_format_string::OptionalAmount::Arg: |
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 | |
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 | } |
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 | (0) . __assert_fail ("E->getNumArgs() >= 1 && \"__builtin_os_log_format_buffer_size takes at least 1 argument\"", "/home/seafit/code_projects/clang_source/clang/lib/AST/OSLog.cpp", 189, __PRETTY_FUNCTION__))" file_link="../../../include/assert.h.html#88" macro="true">assert(E->getNumArgs() >= 1 && |
189 | (0) . __assert_fail ("E->getNumArgs() >= 1 && \"__builtin_os_log_format_buffer_size takes at least 1 argument\"", "/home/seafit/code_projects/clang_source/clang/lib/AST/OSLog.cpp", 189, __PRETTY_FUNCTION__))" file_link="../../../include/assert.h.html#88" macro="true"> "__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 | (0) . __assert_fail ("E->getNumArgs() >= 2 && \"__builtin_os_log_format takes at least 2 arguments\"", "/home/seafit/code_projects/clang_source/clang/lib/AST/OSLog.cpp", 195, __PRETTY_FUNCTION__))" file_link="../../../include/assert.h.html#88" macro="true">assert(E->getNumArgs() >= 2 && |
195 | (0) . __assert_fail ("E->getNumArgs() >= 2 && \"__builtin_os_log_format takes at least 2 arguments\"", "/home/seafit/code_projects/clang_source/clang/lib/AST/OSLog.cpp", 195, __PRETTY_FUNCTION__))" file_link="../../../include/assert.h.html#88" macro="true"> "__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 | isAscii() || Lit->isUTF8())", "/home/seafit/code_projects/clang_source/clang/lib/AST/OSLog.cpp", 204, __PRETTY_FUNCTION__))" file_link="../../../include/assert.h.html#88" macro="true">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(), false); |
209 | |
210 | H.computeLayout(Ctx, Layout); |
211 | return true; |
212 | } |
213 | |