brintos

brintos / llvm-project-archived public Read only

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