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