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