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
13using namespace clang;
14
15using clang::analyze_os_log::OSLogBufferItem;
16using clang::analyze_os_log::OSLogBufferLayout;
17
18namespace {
19class OSLogFormatStringHandler
20 : public analyze_format_string::FormatStringHandler {
21private:
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
54public:
55 OSLogFormatStringHandler(ArrayRef<const Expr *> Args) : Args(Args) {
56 ArgsData.reserve(N: 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(K: 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(Quantity: 8);
141 Layout.Items.emplace_back(Args: OSLogBufferItem::MaskKind, Args: nullptr,
142 Args&: Size, Args: 0, Args: Data.MaskType);
143 }
144
145 if (Data.FieldWidth) {
146 CharUnits Size = Ctx.getTypeSizeInChars(T: (*Data.FieldWidth)->getType());
147 Layout.Items.emplace_back(Args: OSLogBufferItem::ScalarKind, Args: *Data.FieldWidth,
148 Args&: Size, Args: 0);
149 }
150 if (Data.Precision) {
151 CharUnits Size = Ctx.getTypeSizeInChars(T: (*Data.Precision)->getType());
152 Layout.Items.emplace_back(Args: OSLogBufferItem::ScalarKind, Args: *Data.Precision,
153 Args&: Size, Args: 0);
154 }
155 if (Data.Count) {
156 // "%.*P" has an extra "count" that we insert before the argument.
157 CharUnits Size = Ctx.getTypeSizeInChars(T: (*Data.Count)->getType());
158 Layout.Items.emplace_back(Args: OSLogBufferItem::CountKind, Args: *Data.Count, Args&: Size,
159 Args: 0);
160 }
161 if (Data.Size)
162 Layout.Items.emplace_back(Args&: Ctx, Args: CharUnits::fromQuantity(Quantity: *Data.Size),
163 Args: 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(T: Data.E->getType());
170 Layout.Items.emplace_back(Args: *Data.Kind, Args: Data.E, Args&: Size, Args: Data.Flags);
171 } else {
172 auto Size = Ctx.getTypeSizeInChars(T: Data.E->getType());
173 Layout.Items.emplace_back(Args: OSLogBufferItem::ScalarKind, Args: Data.E, Args&: Size,
174 Args: Data.Flags);
175 }
176 }
177 }
178};
179} // end anonymous namespace
180
181bool 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(Arg: 0);
192 VarArgs = Args.slice(N: 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(Arg: 1);
198 VarArgs = Args.slice(N: 2);
199 break;
200 default:
201 llvm_unreachable("non-os_log builtin passed to computeOSLogBufferLayout");
202 }
203
204 const StringLiteral *Lit = cast<StringLiteral>(Val: StringArg->IgnoreParenCasts());
205 assert(Lit && (Lit->isOrdinary() || Lit->isUTF8()));
206 StringRef Data = Lit->getString();
207 OSLogFormatStringHandler H(VarArgs);
208 ParsePrintfString(H, beg: Data.begin(), end: Data.end(), LO: Ctx.getLangOpts(),
209 Target: Ctx.getTargetInfo(), /*isFreeBSDKPrintf*/ false);
210
211 H.computeLayout(Ctx, Layout);
212 return true;
213}
214