1 | //===-- sanitizer_printf.cpp ----------------------------------------------===// |
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 is shared between AddressSanitizer and ThreadSanitizer. |
10 | // |
11 | // Internal printf function, used inside run-time libraries. |
12 | // We can't use libc printf because we intercept some of the functions used |
13 | // inside it. |
14 | //===----------------------------------------------------------------------===// |
15 | |
16 | #include "sanitizer_common.h" |
17 | #include "sanitizer_flags.h" |
18 | #include "sanitizer_libc.h" |
19 | |
20 | #include <stdio.h> |
21 | #include <stdarg.h> |
22 | |
23 | #if SANITIZER_WINDOWS && defined(_MSC_VER) && _MSC_VER < 1800 && \ |
24 | !defined(va_copy) |
25 | # define va_copy(dst, src) ((dst) = (src)) |
26 | #endif |
27 | |
28 | namespace __sanitizer { |
29 | |
30 | static int AppendChar(char **buff, const char *buff_end, char c) { |
31 | if (*buff < buff_end) { |
32 | **buff = c; |
33 | (*buff)++; |
34 | } |
35 | return 1; |
36 | } |
37 | |
38 | // Appends number in a given base to buffer. If its length is less than |
39 | // |minimal_num_length|, it is padded with leading zeroes or spaces, depending |
40 | // on the value of |pad_with_zero|. |
41 | static int AppendNumber(char **buff, const char *buff_end, u64 absolute_value, |
42 | u8 base, u8 minimal_num_length, bool pad_with_zero, |
43 | bool negative, bool uppercase) { |
44 | uptr const kMaxLen = 30; |
45 | RAW_CHECK(base == 10 || base == 16); |
46 | RAW_CHECK(base == 10 || !negative); |
47 | RAW_CHECK(absolute_value || !negative); |
48 | RAW_CHECK(minimal_num_length < kMaxLen); |
49 | int result = 0; |
50 | if (negative && minimal_num_length) |
51 | --minimal_num_length; |
52 | if (negative && pad_with_zero) |
53 | result += AppendChar(buff, buff_end, c: '-'); |
54 | uptr num_buffer[kMaxLen]; |
55 | int pos = 0; |
56 | do { |
57 | RAW_CHECK_MSG((uptr)pos < kMaxLen, "AppendNumber buffer overflow" ,); |
58 | num_buffer[pos++] = absolute_value % base; |
59 | absolute_value /= base; |
60 | } while (absolute_value > 0); |
61 | if (pos < minimal_num_length) { |
62 | // Make sure compiler doesn't insert call to memset here. |
63 | internal_memset(s: &num_buffer[pos], c: 0, |
64 | n: sizeof(num_buffer[0]) * (minimal_num_length - pos)); |
65 | pos = minimal_num_length; |
66 | } |
67 | RAW_CHECK(pos > 0); |
68 | pos--; |
69 | for (; pos >= 0 && num_buffer[pos] == 0; pos--) { |
70 | char c = (pad_with_zero || pos == 0) ? '0' : ' '; |
71 | result += AppendChar(buff, buff_end, c); |
72 | } |
73 | if (negative && !pad_with_zero) result += AppendChar(buff, buff_end, c: '-'); |
74 | for (; pos >= 0; pos--) { |
75 | char digit = static_cast<char>(num_buffer[pos]); |
76 | digit = (digit < 10) ? '0' + digit : (uppercase ? 'A' : 'a') + digit - 10; |
77 | result += AppendChar(buff, buff_end, c: digit); |
78 | } |
79 | return result; |
80 | } |
81 | |
82 | static int AppendUnsigned(char **buff, const char *buff_end, u64 num, u8 base, |
83 | u8 minimal_num_length, bool pad_with_zero, |
84 | bool uppercase) { |
85 | return AppendNumber(buff, buff_end, absolute_value: num, base, minimal_num_length, |
86 | pad_with_zero, negative: false /* negative */, uppercase); |
87 | } |
88 | |
89 | static int AppendSignedDecimal(char **buff, const char *buff_end, s64 num, |
90 | u8 minimal_num_length, bool pad_with_zero) { |
91 | bool negative = (num < 0); |
92 | return AppendNumber(buff, buff_end, absolute_value: (u64)(negative ? -num : num), base: 10, |
93 | minimal_num_length, pad_with_zero, negative, |
94 | uppercase: false /* uppercase */); |
95 | } |
96 | |
97 | |
98 | // Use the fact that explicitly requesting 0 width (%0s) results in UB and |
99 | // interpret width == 0 as "no width requested": |
100 | // width == 0 - no width requested |
101 | // width < 0 - left-justify s within and pad it to -width chars, if necessary |
102 | // width > 0 - right-justify s, not implemented yet |
103 | static int AppendString(char **buff, const char *buff_end, int width, |
104 | int max_chars, const char *s) { |
105 | if (!s) |
106 | s = "<null>" ; |
107 | int result = 0; |
108 | for (; *s; s++) { |
109 | if (max_chars >= 0 && result >= max_chars) |
110 | break; |
111 | result += AppendChar(buff, buff_end, c: *s); |
112 | } |
113 | // Only the left justified strings are supported. |
114 | while (width < -result) |
115 | result += AppendChar(buff, buff_end, c: ' '); |
116 | return result; |
117 | } |
118 | |
119 | static int AppendPointer(char **buff, const char *buff_end, u64 ptr_value) { |
120 | int result = 0; |
121 | result += AppendString(buff, buff_end, width: 0, max_chars: -1, s: "0x" ); |
122 | result += AppendUnsigned(buff, buff_end, num: ptr_value, base: 16, |
123 | SANITIZER_POINTER_FORMAT_LENGTH, |
124 | pad_with_zero: true /* pad_with_zero */, uppercase: false /* uppercase */); |
125 | return result; |
126 | } |
127 | |
128 | int VSNPrintf(char *buff, int buff_length, |
129 | const char *format, va_list args) { |
130 | static const char *kPrintfFormatsHelp = |
131 | "Supported Printf formats: %([0-9]*)?(z|l|ll)?{d,u,x,X}; %p; " |
132 | "%[-]([0-9]*)?(\\.\\*)?s; %c\nProvided format: " ; |
133 | RAW_CHECK(format); |
134 | RAW_CHECK(buff_length > 0); |
135 | const char *buff_end = &buff[buff_length - 1]; |
136 | const char *cur = format; |
137 | int result = 0; |
138 | for (; *cur; cur++) { |
139 | if (*cur != '%') { |
140 | result += AppendChar(buff: &buff, buff_end, c: *cur); |
141 | continue; |
142 | } |
143 | cur++; |
144 | bool left_justified = *cur == '-'; |
145 | if (left_justified) |
146 | cur++; |
147 | bool have_width = (*cur >= '0' && *cur <= '9'); |
148 | bool pad_with_zero = (*cur == '0'); |
149 | int width = 0; |
150 | if (have_width) { |
151 | while (*cur >= '0' && *cur <= '9') { |
152 | width = width * 10 + *cur++ - '0'; |
153 | } |
154 | } |
155 | bool have_precision = (cur[0] == '.' && cur[1] == '*'); |
156 | int precision = -1; |
157 | if (have_precision) { |
158 | cur += 2; |
159 | precision = va_arg(args, int); |
160 | } |
161 | bool have_z = (*cur == 'z'); |
162 | cur += have_z; |
163 | bool have_l = cur[0] == 'l' && cur[1] != 'l'; |
164 | cur += have_l; |
165 | bool have_ll = cur[0] == 'l' && cur[1] == 'l'; |
166 | cur += have_ll * 2; |
167 | const bool have_length = have_z || have_l || have_ll; |
168 | const bool have_flags = have_width || have_length; |
169 | // At the moment only %s supports precision and left-justification. |
170 | CHECK(!((precision >= 0 || left_justified) && *cur != 's')); |
171 | switch (*cur) { |
172 | case 'd': { |
173 | s64 dval = have_ll ? va_arg(args, s64) |
174 | : have_z ? va_arg(args, sptr) |
175 | : have_l ? va_arg(args, long) |
176 | : va_arg(args, int); |
177 | result += AppendSignedDecimal(buff: &buff, buff_end, num: dval, minimal_num_length: width, |
178 | pad_with_zero); |
179 | break; |
180 | } |
181 | case 'u': |
182 | case 'x': |
183 | case 'X': { |
184 | u64 uval = have_ll ? va_arg(args, u64) |
185 | : have_z ? va_arg(args, uptr) |
186 | : have_l ? va_arg(args, unsigned long) |
187 | : va_arg(args, unsigned); |
188 | bool uppercase = (*cur == 'X'); |
189 | result += AppendUnsigned(buff: &buff, buff_end, num: uval, base: (*cur == 'u') ? 10 : 16, |
190 | minimal_num_length: width, pad_with_zero, uppercase); |
191 | break; |
192 | } |
193 | case 'p': { |
194 | RAW_CHECK_VA(!have_flags, kPrintfFormatsHelp, format); |
195 | result += AppendPointer(buff: &buff, buff_end, va_arg(args, uptr)); |
196 | break; |
197 | } |
198 | case 's': { |
199 | RAW_CHECK_VA(!have_length, kPrintfFormatsHelp, format); |
200 | // Only left-justified width is supported. |
201 | CHECK(!have_width || left_justified); |
202 | result += AppendString(buff: &buff, buff_end, width: left_justified ? -width : width, |
203 | max_chars: precision, va_arg(args, char*)); |
204 | break; |
205 | } |
206 | case 'c': { |
207 | RAW_CHECK_VA(!have_flags, kPrintfFormatsHelp, format); |
208 | result += AppendChar(buff: &buff, buff_end, va_arg(args, int)); |
209 | break; |
210 | } |
211 | case '%' : { |
212 | RAW_CHECK_VA(!have_flags, kPrintfFormatsHelp, format); |
213 | result += AppendChar(buff: &buff, buff_end, c: '%'); |
214 | break; |
215 | } |
216 | default: { |
217 | RAW_CHECK_VA(false, kPrintfFormatsHelp, format); |
218 | } |
219 | } |
220 | } |
221 | RAW_CHECK(buff <= buff_end); |
222 | AppendChar(buff: &buff, buff_end: buff_end + 1, c: '\0'); |
223 | return result; |
224 | } |
225 | |
226 | static void (*PrintfAndReportCallback)(const char *); |
227 | void SetPrintfAndReportCallback(void (*callback)(const char *)) { |
228 | PrintfAndReportCallback = callback; |
229 | } |
230 | |
231 | // Can be overriden in frontend. |
232 | #if SANITIZER_GO && defined(TSAN_EXTERNAL_HOOKS) |
233 | // Implementation must be defined in frontend. |
234 | extern "C" void __sanitizer_on_print(const char *str); |
235 | #else |
236 | SANITIZER_INTERFACE_WEAK_DEF(void, __sanitizer_on_print, const char *str) { |
237 | (void)str; |
238 | } |
239 | #endif |
240 | |
241 | static void CallPrintfAndReportCallback(const char *str) { |
242 | __sanitizer_on_print(str); |
243 | if (PrintfAndReportCallback) |
244 | PrintfAndReportCallback(str); |
245 | } |
246 | |
247 | static void NOINLINE SharedPrintfCodeNoBuffer(bool append_pid, |
248 | char *local_buffer, |
249 | int buffer_size, |
250 | const char *format, |
251 | va_list args) { |
252 | va_list args2; |
253 | va_copy(args2, args); |
254 | InternalMmapVector<char> v; |
255 | int needed_length = 0; |
256 | char *buffer = local_buffer; |
257 | // First try to print a message using a local buffer, and then fall back to |
258 | // mmaped buffer. |
259 | for (int use_mmap = 0;; use_mmap++) { |
260 | if (use_mmap) { |
261 | va_end(args); |
262 | va_copy(args, args2); |
263 | v.resize(new_size: needed_length + 1); |
264 | buffer_size = v.capacity(); |
265 | v.resize(new_size: buffer_size); |
266 | buffer = &v[0]; |
267 | } |
268 | needed_length = 0; |
269 | // Fuchsia's logging infrastructure always keeps track of the logging |
270 | // process, thread, and timestamp, so never prepend such information. |
271 | if (!SANITIZER_FUCHSIA && append_pid) { |
272 | int pid = internal_getpid(); |
273 | const char *exe_name = GetProcessName(); |
274 | if (common_flags()->log_exe_name && exe_name) { |
275 | needed_length += internal_snprintf(buffer, length: buffer_size, |
276 | format: "==%s" , exe_name); |
277 | if (needed_length >= buffer_size) |
278 | continue; |
279 | } |
280 | needed_length += internal_snprintf( |
281 | buffer: buffer + needed_length, length: buffer_size - needed_length, format: "==%d==" , pid); |
282 | if (needed_length >= buffer_size) |
283 | continue; |
284 | } |
285 | needed_length += VSNPrintf(buff: buffer + needed_length, |
286 | buff_length: buffer_size - needed_length, format, args); |
287 | if (needed_length >= buffer_size) |
288 | continue; |
289 | // If the message fit into the buffer, print it and exit. |
290 | break; |
291 | } |
292 | RawWrite(buffer); |
293 | |
294 | // Remove color sequences from the message. |
295 | RemoveANSIEscapeSequencesFromString(buffer); |
296 | CallPrintfAndReportCallback(str: buffer); |
297 | LogMessageOnPrintf(str: buffer); |
298 | |
299 | va_end(args2); |
300 | } |
301 | |
302 | static void NOINLINE SharedPrintfCode(bool append_pid, const char *format, |
303 | va_list args) { |
304 | // |local_buffer| is small enough not to overflow the stack and/or violate |
305 | // the stack limit enforced by TSan (-Wframe-larger-than=512). On the other |
306 | // hand, the bigger the buffer is, the more the chance the error report will |
307 | // fit into it. |
308 | char local_buffer[400]; |
309 | SharedPrintfCodeNoBuffer(append_pid, local_buffer, ARRAY_SIZE(local_buffer), |
310 | format, args); |
311 | } |
312 | |
313 | void Printf(const char *format, ...) { |
314 | va_list args; |
315 | va_start(args, format); |
316 | SharedPrintfCode(append_pid: false, format, args); |
317 | va_end(args); |
318 | } |
319 | |
320 | // Like Printf, but prints the current PID before the output string. |
321 | void Report(const char *format, ...) { |
322 | va_list args; |
323 | va_start(args, format); |
324 | SharedPrintfCode(append_pid: true, format, args); |
325 | va_end(args); |
326 | } |
327 | |
328 | // Writes at most "length" symbols to "buffer" (including trailing '\0'). |
329 | // Returns the number of symbols that should have been written to buffer |
330 | // (not including trailing '\0'). Thus, the string is truncated |
331 | // iff return value is not less than "length". |
332 | int internal_snprintf(char *buffer, uptr length, const char *format, ...) { |
333 | va_list args; |
334 | va_start(args, format); |
335 | int needed_length = VSNPrintf(buff: buffer, buff_length: length, format, args); |
336 | va_end(args); |
337 | return needed_length; |
338 | } |
339 | |
340 | void InternalScopedString::Append(const char *str) { |
341 | uptr prev_len = length(); |
342 | uptr str_len = internal_strlen(s: str); |
343 | buffer_.resize(new_size: prev_len + str_len + 1); |
344 | internal_memcpy(dest: buffer_.data() + prev_len, src: str, n: str_len + 1); |
345 | } |
346 | |
347 | void InternalScopedString::AppendF(const char *format, ...) { |
348 | uptr prev_len = length(); |
349 | |
350 | while (true) { |
351 | buffer_.resize(new_size: buffer_.capacity()); |
352 | |
353 | va_list args; |
354 | va_start(args, format); |
355 | uptr sz = VSNPrintf(buff: buffer_.data() + prev_len, buff_length: buffer_.size() - prev_len, |
356 | format, args); |
357 | va_end(args); |
358 | if (sz < buffer_.size() - prev_len) { |
359 | buffer_.resize(new_size: prev_len + sz + 1); |
360 | break; |
361 | } |
362 | |
363 | buffer_.reserve(new_size: buffer_.capacity() * 2); |
364 | } |
365 | CHECK_EQ(buffer_[length()], '\0'); |
366 | } |
367 | |
368 | } // namespace __sanitizer |
369 | |