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