Coverage Report

Created: 2024-01-17 10:31

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