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