1 //===-- string_utils.cpp ----------------------------------------*- 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 #include "string_utils.h" 10 #include "common.h" 11 12 #include <stdarg.h> 13 #include <string.h> 14 15 namespace scudo { 16 17 // Appends number in a given Base to buffer. If its length is less than 18 // |MinNumberLength|, it is padded with leading zeroes or spaces, depending 19 // on the value of |PadWithZero|. 20 void ScopedString::appendNumber(u64 AbsoluteValue, u8 Base, u8 MinNumberLength, 21 bool PadWithZero, bool Negative, bool Upper) { 22 constexpr uptr MaxLen = 30; 23 RAW_CHECK(Base == 10 || Base == 16); 24 RAW_CHECK(Base == 10 || !Negative); 25 RAW_CHECK(AbsoluteValue || !Negative); 26 RAW_CHECK(MinNumberLength < MaxLen); 27 if (Negative && MinNumberLength) 28 --MinNumberLength; 29 if (Negative && PadWithZero) { 30 String.push_back('-'); 31 } 32 uptr NumBuffer[MaxLen]; 33 int Pos = 0; 34 do { 35 RAW_CHECK_MSG(static_cast<uptr>(Pos) < MaxLen, 36 "appendNumber buffer overflow"); 37 NumBuffer[Pos++] = static_cast<uptr>(AbsoluteValue % Base); 38 AbsoluteValue /= Base; 39 } while (AbsoluteValue > 0); 40 if (Pos < MinNumberLength) { 41 memset(&NumBuffer[Pos], 0, 42 sizeof(NumBuffer[0]) * static_cast<uptr>(MinNumberLength - Pos)); 43 Pos = MinNumberLength; 44 } 45 RAW_CHECK(Pos > 0); 46 Pos--; 47 for (; Pos >= 0 && NumBuffer[Pos] == 0; Pos--) { 48 char c = (PadWithZero || Pos == 0) ? '0' : ' '; 49 String.push_back(c); 50 } 51 if (Negative && !PadWithZero) 52 String.push_back('-'); 53 for (; Pos >= 0; Pos--) { 54 char Digit = static_cast<char>(NumBuffer[Pos]); 55 Digit = static_cast<char>((Digit < 10) ? '0' + Digit 56 : (Upper ? 'A' : 'a') + Digit - 10); 57 String.push_back(Digit); 58 } 59 } 60 61 void ScopedString::appendUnsigned(u64 Num, u8 Base, u8 MinNumberLength, 62 bool PadWithZero, bool Upper) { 63 appendNumber(Num, Base, MinNumberLength, PadWithZero, /*Negative=*/false, 64 Upper); 65 } 66 67 void ScopedString::appendSignedDecimal(s64 Num, u8 MinNumberLength, 68 bool PadWithZero) { 69 const bool Negative = (Num < 0); 70 const u64 UnsignedNum = (Num == INT64_MIN) 71 ? static_cast<u64>(INT64_MAX) + 1 72 : static_cast<u64>(Negative ? -Num : Num); 73 appendNumber(UnsignedNum, 10, MinNumberLength, PadWithZero, Negative, 74 /*Upper=*/false); 75 } 76 77 // Use the fact that explicitly requesting 0 Width (%0s) results in UB and 78 // interpret Width == 0 as "no Width requested": 79 // Width == 0 - no Width requested 80 // Width < 0 - left-justify S within and pad it to -Width chars, if necessary 81 // Width > 0 - right-justify S, not implemented yet 82 void ScopedString::appendString(int Width, int MaxChars, const char *S) { 83 if (!S) 84 S = "<null>"; 85 int NumChars = 0; 86 for (; *S; S++) { 87 if (MaxChars >= 0 && NumChars >= MaxChars) 88 break; 89 String.push_back(*S); 90 NumChars++; 91 } 92 if (Width < 0) { 93 // Only left justification supported. 94 Width = -Width - NumChars; 95 while (Width-- > 0) 96 String.push_back(' '); 97 } 98 } 99 100 void ScopedString::appendPointer(u64 ptr_value) { 101 appendString(0, -1, "0x"); 102 appendUnsigned(ptr_value, 16, SCUDO_POINTER_FORMAT_LENGTH, 103 /*PadWithZero=*/true, 104 /*Upper=*/false); 105 } 106 107 void ScopedString::vappend(const char *Format, va_list &Args) { 108 // Since the string contains the '\0' terminator, put our size before it 109 // so that push_back calls work correctly. 110 DCHECK(String.size() > 0); 111 String.resize(String.size() - 1); 112 113 static const char *PrintfFormatsHelp = 114 "Supported formats: %([0-9]*)?(z|ll)?{d,u,x,X}; %p; " 115 "%[-]([0-9]*)?(\\.\\*)?s; %c\n"; 116 RAW_CHECK(Format); 117 const char *Cur = Format; 118 for (; *Cur; Cur++) { 119 if (*Cur != '%') { 120 String.push_back(*Cur); 121 continue; 122 } 123 Cur++; 124 const bool LeftJustified = *Cur == '-'; 125 if (LeftJustified) 126 Cur++; 127 bool HaveWidth = (*Cur >= '0' && *Cur <= '9'); 128 const bool PadWithZero = (*Cur == '0'); 129 u8 Width = 0; 130 if (HaveWidth) { 131 while (*Cur >= '0' && *Cur <= '9') 132 Width = static_cast<u8>(Width * 10 + *Cur++ - '0'); 133 } 134 const bool HavePrecision = (Cur[0] == '.' && Cur[1] == '*'); 135 int Precision = -1; 136 if (HavePrecision) { 137 Cur += 2; 138 Precision = va_arg(Args, int); 139 } 140 const bool HaveZ = (*Cur == 'z'); 141 Cur += HaveZ; 142 const bool HaveLL = !HaveZ && (Cur[0] == 'l' && Cur[1] == 'l'); 143 Cur += HaveLL * 2; 144 s64 DVal; 145 u64 UVal; 146 const bool HaveLength = HaveZ || HaveLL; 147 const bool HaveFlags = HaveWidth || HaveLength; 148 // At the moment only %s supports precision and left-justification. 149 CHECK(!((Precision >= 0 || LeftJustified) && *Cur != 's')); 150 switch (*Cur) { 151 case 'd': { 152 DVal = HaveLL ? va_arg(Args, s64) 153 : HaveZ ? va_arg(Args, sptr) 154 : va_arg(Args, int); 155 appendSignedDecimal(DVal, Width, PadWithZero); 156 break; 157 } 158 case 'u': 159 case 'x': 160 case 'X': { 161 UVal = HaveLL ? va_arg(Args, u64) 162 : HaveZ ? va_arg(Args, uptr) 163 : va_arg(Args, unsigned); 164 const bool Upper = (*Cur == 'X'); 165 appendUnsigned(UVal, (*Cur == 'u') ? 10 : 16, Width, PadWithZero, Upper); 166 break; 167 } 168 case 'p': { 169 RAW_CHECK_MSG(!HaveFlags, PrintfFormatsHelp); 170 appendPointer(va_arg(Args, uptr)); 171 break; 172 } 173 case 's': { 174 RAW_CHECK_MSG(!HaveLength, PrintfFormatsHelp); 175 // Only left-justified Width is supported. 176 CHECK(!HaveWidth || LeftJustified); 177 appendString(LeftJustified ? -Width : Width, Precision, 178 va_arg(Args, char *)); 179 break; 180 } 181 case 'c': { 182 RAW_CHECK_MSG(!HaveFlags, PrintfFormatsHelp); 183 String.push_back(static_cast<char>(va_arg(Args, int))); 184 break; 185 } 186 // In Scudo, `s64`/`u64` are supposed to use `lld` and `llu` respectively. 187 // However, `-Wformat` doesn't know we have a different parser for those 188 // placeholders and it keeps complaining the type mismatch on 64-bit 189 // platform which uses `ld`/`lu` for `s64`/`u64`. Therefore, in order to 190 // silence the warning, we turn to use `PRId64`/`PRIu64` for printing 191 // `s64`/`u64` and handle the `ld`/`lu` here. 192 case 'l': { 193 ++Cur; 194 RAW_CHECK(*Cur == 'd' || *Cur == 'u'); 195 196 if (*Cur == 'd') { 197 DVal = va_arg(Args, s64); 198 appendSignedDecimal(DVal, Width, PadWithZero); 199 } else { 200 UVal = va_arg(Args, u64); 201 appendUnsigned(UVal, 10, Width, PadWithZero, false); 202 } 203 204 break; 205 } 206 case '%': { 207 RAW_CHECK_MSG(!HaveFlags, PrintfFormatsHelp); 208 String.push_back('%'); 209 break; 210 } 211 default: { 212 RAW_CHECK_MSG(false, PrintfFormatsHelp); 213 } 214 } 215 } 216 String.push_back('\0'); 217 if (String.back() != '\0') { 218 // String truncated, make sure the string is terminated properly. 219 // This can happen if there is no more memory when trying to resize 220 // the string. 221 String.back() = '\0'; 222 } 223 } 224 225 void ScopedString::append(const char *Format, ...) { 226 va_list Args; 227 va_start(Args, Format); 228 vappend(Format, Args); 229 va_end(Args); 230 } 231 232 void Printf(const char *Format, ...) { 233 va_list Args; 234 va_start(Args, Format); 235 ScopedString Msg; 236 Msg.vappend(Format, Args); 237 outputRaw(Msg.data()); 238 va_end(Args); 239 } 240 241 } // namespace scudo 242