xref: /freebsd/contrib/llvm-project/clang/lib/AST/OSLog.cpp (revision d4eeb02986980bf33dd56c41ceb9fc5f180c0d47)
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 
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: // "%s"
38       return OSLogBufferItem::StringKind;
39     case clang::analyze_format_string::ConversionSpecifier::SArg: // "%S"
40       return OSLogBufferItem::WideStringKind;
41     case clang::analyze_format_string::ConversionSpecifier::PArg: { // "%P"
42       return OSLogBufferItem::PointerKind;
43     case clang::analyze_format_string::ConversionSpecifier::ObjCObjArg: // "%@"
44       return OSLogBufferItem::ObjCObjKind;
45     case clang::analyze_format_string::ConversionSpecifier::PrintErrno: // "%m"
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   bool HandlePrintfSpecifier(const analyze_printf::PrintfSpecifier &FS,
59                              const char *StartSpecifier, unsigned SpecifierLen,
60                              const TargetInfo &) override {
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     // First get the Kind
72     ArgsData.back().Kind = getKind(FS.getConversionSpecifier().getKind());
73     if (ArgsData.back().Kind != OSLogBufferItem::ErrnoKind &&
74         !ArgsData.back().E) {
75       // missing argument
76       ArgsData.pop_back();
77       return false;
78     }
79 
80     switch (FS.getConversionSpecifier().getKind()) {
81     case clang::analyze_format_string::ConversionSpecifier::sArg:   // "%s"
82     case clang::analyze_format_string::ConversionSpecifier::SArg: { // "%S"
83       auto &precision = FS.getPrecision();
84       switch (precision.getHowSpecified()) {
85       case clang::analyze_format_string::OptionalAmount::NotSpecified: // "%s"
86         break;
87       case clang::analyze_format_string::OptionalAmount::Constant: // "%.16s"
88         ArgsData.back().Size = precision.getConstantAmount();
89         break;
90       case clang::analyze_format_string::OptionalAmount::Arg: // "%.*s"
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: { // "%P"
99       auto &precision = FS.getPrecision();
100       switch (precision.getHowSpecified()) {
101       case clang::analyze_format_string::OptionalAmount::NotSpecified: // "%P"
102         return false; // length must be supplied with pointer format specifier
103       case clang::analyze_format_string::OptionalAmount::Constant: // "%.16P"
104         ArgsData.back().Size = precision.getConstantAmount();
105         break;
106       case clang::analyze_format_string::OptionalAmount::Arg: // "%.*P"
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         // "%.*P" has an extra "count" that we insert before the argument.
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 } // end anonymous namespace
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     assert(E->getNumArgs() >= 1 &&
189            "__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     assert(E->getNumArgs() >= 2 &&
195            "__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   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(), /*isFreeBSDKPrintf*/ false);
209 
210   H.computeLayout(Ctx, Layout);
211   return true;
212 }
213