1 //= OSLog.h - Analysis of calls to os_log builtins --*- C++ -*-===============// 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-exception 6 // 7 //===----------------------------------------------------------------------===// 8 // 9 // This file defines APIs for determining the layout of the data buffer for 10 // os_log() and os_trace(). 11 // 12 //===----------------------------------------------------------------------===// 13 14 #ifndef LLVM_CLANG_ANALYSIS_ANALYSES_OSLOG_H 15 #define LLVM_CLANG_ANALYSIS_ANALYSES_OSLOG_H 16 17 #include "clang/AST/ASTContext.h" 18 #include "clang/AST/Expr.h" 19 20 namespace clang { 21 namespace analyze_os_log { 22 23 /// An OSLogBufferItem represents a single item in the data written by a call 24 /// to os_log() or os_trace(). 25 class OSLogBufferItem { 26 public: 27 enum Kind { 28 // The item is a scalar (int, float, raw pointer, etc.). No further copying 29 // is required. This is the only kind allowed by os_trace(). 30 ScalarKind = 0, 31 32 // The item is a count, which describes the length of the following item to 33 // be copied. A count may only be followed by an item of kind StringKind, 34 // WideStringKind, or PointerKind. 35 CountKind, 36 37 // The item is a pointer to a C string. If preceded by a count 'n', 38 // os_log() will copy at most 'n' bytes from the pointer. 39 StringKind, 40 41 // The item is a pointer to a block of raw data. This item must be preceded 42 // by a count 'n'. os_log() will copy exactly 'n' bytes from the pointer. 43 PointerKind, 44 45 // The item is a pointer to an Objective-C object. os_log() may retain the 46 // object for later processing. 47 ObjCObjKind, 48 49 // The item is a pointer to wide-char string. 50 WideStringKind, 51 52 // The item is corresponding to the '%m' format specifier, no value is 53 // populated in the buffer and the runtime is loading the errno value. 54 ErrnoKind, 55 56 // The item is a mask type. 57 MaskKind 58 }; 59 60 enum { 61 // The item is marked "private" in the format string. 62 IsPrivate = 0x1, 63 64 // The item is marked "public" in the format string. 65 IsPublic = 0x2, 66 67 // The item is marked "sensitive" in the format string. 68 IsSensitive = 0x4 | IsPrivate 69 }; 70 71 private: 72 Kind TheKind = ScalarKind; 73 const Expr *TheExpr = nullptr; 74 CharUnits ConstValue; 75 CharUnits Size; // size of the data, not including the header bytes 76 unsigned Flags = 0; 77 StringRef MaskType; 78 79 public: 80 OSLogBufferItem(Kind kind, const Expr *expr, CharUnits size, unsigned flags, 81 StringRef maskType = StringRef()) TheKind(kind)82 : TheKind(kind), TheExpr(expr), Size(size), Flags(flags), 83 MaskType(maskType) { 84 assert(((Flags == 0) || (Flags == IsPrivate) || (Flags == IsPublic) || 85 (Flags == IsSensitive)) && 86 "unexpected privacy flag"); 87 } 88 OSLogBufferItem(ASTContext & Ctx,CharUnits value,unsigned flags)89 OSLogBufferItem(ASTContext &Ctx, CharUnits value, unsigned flags) 90 : TheKind(CountKind), ConstValue(value), 91 Size(Ctx.getTypeSizeInChars(Ctx.IntTy)), Flags(flags) {} 92 getDescriptorByte()93 unsigned char getDescriptorByte() const { 94 unsigned char result = Flags; 95 result |= ((unsigned)getKind()) << 4; 96 return result; 97 } 98 getSizeByte()99 unsigned char getSizeByte() const { return size().getQuantity(); } 100 getKind()101 Kind getKind() const { return TheKind; } getIsPrivate()102 bool getIsPrivate() const { return (Flags & IsPrivate) != 0; } 103 getExpr()104 const Expr *getExpr() const { return TheExpr; } getConstValue()105 CharUnits getConstValue() const { return ConstValue; } size()106 CharUnits size() const { return Size; } 107 getMaskType()108 StringRef getMaskType() const { return MaskType; } 109 }; 110 111 class OSLogBufferLayout { 112 public: 113 SmallVector<OSLogBufferItem, 4> Items; 114 115 enum Flags { HasPrivateItems = 1, HasNonScalarItems = 1 << 1 }; 116 size()117 CharUnits size() const { 118 CharUnits result; 119 result += CharUnits::fromQuantity(2); // summary byte, num-args byte 120 for (auto &item : Items) { 121 // descriptor byte, size byte 122 result += item.size() + CharUnits::fromQuantity(2); 123 } 124 return result; 125 } 126 hasPrivateItems()127 bool hasPrivateItems() const { 128 return llvm::any_of( 129 Items, [](const OSLogBufferItem &Item) { return Item.getIsPrivate(); }); 130 } 131 hasNonScalarOrMask()132 bool hasNonScalarOrMask() const { 133 return llvm::any_of(Items, [](const OSLogBufferItem &Item) { 134 return Item.getKind() != OSLogBufferItem::ScalarKind || 135 !Item.getMaskType().empty(); 136 }); 137 } 138 getSummaryByte()139 unsigned char getSummaryByte() const { 140 unsigned char result = 0; 141 if (hasPrivateItems()) 142 result |= HasPrivateItems; 143 if (hasNonScalarOrMask()) 144 result |= HasNonScalarItems; 145 return result; 146 } 147 getNumArgsByte()148 unsigned char getNumArgsByte() const { return Items.size(); } 149 }; 150 151 // Given a call 'E' to one of the builtins __builtin_os_log_format() or 152 // __builtin_os_log_format_buffer_size(), compute the layout of the buffer that 153 // the call will write into and store it in 'layout'. Returns 'false' if there 154 // was some error encountered while computing the layout, and 'true' otherwise. 155 bool computeOSLogBufferLayout(clang::ASTContext &Ctx, const clang::CallExpr *E, 156 OSLogBufferLayout &layout); 157 158 } // namespace analyze_os_log 159 } // namespace clang 160 #endif 161