1 #define JEMALLOC_MALLOC_IO_C_
2 #include "jemalloc/internal/jemalloc_preamble.h"
3 #include "jemalloc/internal/jemalloc_internal_includes.h"
4 
5 #include "jemalloc/internal/malloc_io.h"
6 #include "jemalloc/internal/util.h"
7 
8 #ifdef assert
9 #  undef assert
10 #endif
11 #ifdef not_reached
12 #  undef not_reached
13 #endif
14 #ifdef not_implemented
15 #  undef not_implemented
16 #endif
17 #ifdef assert_not_implemented
18 #  undef assert_not_implemented
19 #endif
20 
21 /*
22  * Define simple versions of assertion macros that won't recurse in case
23  * of assertion failures in malloc_*printf().
24  */
25 #define assert(e) do {							\
26 	if (config_debug && !(e)) {					\
27 		malloc_write("<jemalloc>: Failed assertion\n");		\
28 		abort();						\
29 	}								\
30 } while (0)
31 
32 #define not_reached() do {						\
33 	if (config_debug) {						\
34 		malloc_write("<jemalloc>: Unreachable code reached\n");	\
35 		abort();						\
36 	}								\
37 	unreachable();							\
38 } while (0)
39 
40 #define not_implemented() do {						\
41 	if (config_debug) {						\
42 		malloc_write("<jemalloc>: Not implemented\n");		\
43 		abort();						\
44 	}								\
45 } while (0)
46 
47 #define assert_not_implemented(e) do {					\
48 	if (unlikely(config_debug && !(e))) {				\
49 		not_implemented();					\
50 	}								\
51 } while (0)
52 
53 /******************************************************************************/
54 /* Function prototypes for non-inline static functions. */
55 
56 static void wrtmessage(void *cbopaque, const char *s);
57 #define U2S_BUFSIZE ((1U << (LG_SIZEOF_INTMAX_T + 3)) + 1)
58 static char *u2s(uintmax_t x, unsigned base, bool uppercase, char *s,
59     size_t *slen_p);
60 #define D2S_BUFSIZE (1 + U2S_BUFSIZE)
61 static char *d2s(intmax_t x, char sign, char *s, size_t *slen_p);
62 #define O2S_BUFSIZE (1 + U2S_BUFSIZE)
63 static char *o2s(uintmax_t x, bool alt_form, char *s, size_t *slen_p);
64 #define X2S_BUFSIZE (2 + U2S_BUFSIZE)
65 static char *x2s(uintmax_t x, bool alt_form, bool uppercase, char *s,
66     size_t *slen_p);
67 
68 /******************************************************************************/
69 
70 /* malloc_message() setup. */
71 static void
wrtmessage(void * cbopaque,const char * s)72 wrtmessage(void *cbopaque, const char *s) {
73 	malloc_write_fd(STDERR_FILENO, s, strlen(s));
74 }
75 
76 JEMALLOC_EXPORT void	(*je_malloc_message)(void *, const char *s);
77 
78 /*
79  * Wrapper around malloc_message() that avoids the need for
80  * je_malloc_message(...) throughout the code.
81  */
82 void
malloc_write(const char * s)83 malloc_write(const char *s) {
84 	if (je_malloc_message != NULL) {
85 		je_malloc_message(NULL, s);
86 	} else {
87 		wrtmessage(NULL, s);
88 	}
89 }
90 
91 /*
92  * glibc provides a non-standard strerror_r() when _GNU_SOURCE is defined, so
93  * provide a wrapper.
94  */
95 int
buferror(int err,char * buf,size_t buflen)96 buferror(int err, char *buf, size_t buflen) {
97 #ifdef _WIN32
98 	FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, NULL, err, 0,
99 	    (LPSTR)buf, (DWORD)buflen, NULL);
100 	return 0;
101 #elif defined(JEMALLOC_STRERROR_R_RETURNS_CHAR_WITH_GNU_SOURCE) && defined(_GNU_SOURCE)
102 	char *b = strerror_r(err, buf, buflen);
103 	if (b != buf) {
104 		strncpy(buf, b, buflen);
105 		buf[buflen-1] = '\0';
106 	}
107 	return 0;
108 #else
109 	return strerror_r(err, buf, buflen);
110 #endif
111 }
112 
113 uintmax_t
malloc_strtoumax(const char * restrict nptr,char ** restrict endptr,int base)114 malloc_strtoumax(const char *restrict nptr, char **restrict endptr, int base) {
115 	uintmax_t ret, digit;
116 	unsigned b;
117 	bool neg;
118 	const char *p, *ns;
119 
120 	p = nptr;
121 	if (base < 0 || base == 1 || base > 36) {
122 		ns = p;
123 		set_errno(EINVAL);
124 		ret = UINTMAX_MAX;
125 		goto label_return;
126 	}
127 	b = base;
128 
129 	/* Swallow leading whitespace and get sign, if any. */
130 	neg = false;
131 	while (true) {
132 		switch (*p) {
133 		case '\t': case '\n': case '\v': case '\f': case '\r': case ' ':
134 			p++;
135 			break;
136 		case '-':
137 			neg = true;
138 			/* Fall through. */
139 		case '+':
140 			p++;
141 			/* Fall through. */
142 		default:
143 			goto label_prefix;
144 		}
145 	}
146 
147 	/* Get prefix, if any. */
148 	label_prefix:
149 	/*
150 	 * Note where the first non-whitespace/sign character is so that it is
151 	 * possible to tell whether any digits are consumed (e.g., "  0" vs.
152 	 * "  -x").
153 	 */
154 	ns = p;
155 	if (*p == '0') {
156 		switch (p[1]) {
157 		case '0': case '1': case '2': case '3': case '4': case '5':
158 		case '6': case '7':
159 			if (b == 0) {
160 				b = 8;
161 			}
162 			if (b == 8) {
163 				p++;
164 			}
165 			break;
166 		case 'X': case 'x':
167 			switch (p[2]) {
168 			case '0': case '1': case '2': case '3': case '4':
169 			case '5': case '6': case '7': case '8': case '9':
170 			case 'A': case 'B': case 'C': case 'D': case 'E':
171 			case 'F':
172 			case 'a': case 'b': case 'c': case 'd': case 'e':
173 			case 'f':
174 				if (b == 0) {
175 					b = 16;
176 				}
177 				if (b == 16) {
178 					p += 2;
179 				}
180 				break;
181 			default:
182 				break;
183 			}
184 			break;
185 		default:
186 			p++;
187 			ret = 0;
188 			goto label_return;
189 		}
190 	}
191 	if (b == 0) {
192 		b = 10;
193 	}
194 
195 	/* Convert. */
196 	ret = 0;
197 	while ((*p >= '0' && *p <= '9' && (digit = *p - '0') < b)
198 	    || (*p >= 'A' && *p <= 'Z' && (digit = 10 + *p - 'A') < b)
199 	    || (*p >= 'a' && *p <= 'z' && (digit = 10 + *p - 'a') < b)) {
200 		uintmax_t pret = ret;
201 		ret *= b;
202 		ret += digit;
203 		if (ret < pret) {
204 			/* Overflow. */
205 			set_errno(ERANGE);
206 			ret = UINTMAX_MAX;
207 			goto label_return;
208 		}
209 		p++;
210 	}
211 	if (neg) {
212 		ret = (uintmax_t)(-((intmax_t)ret));
213 	}
214 
215 	if (p == ns) {
216 		/* No conversion performed. */
217 		set_errno(EINVAL);
218 		ret = UINTMAX_MAX;
219 		goto label_return;
220 	}
221 
222 label_return:
223 	if (endptr != NULL) {
224 		if (p == ns) {
225 			/* No characters were converted. */
226 			*endptr = (char *)nptr;
227 		} else {
228 			*endptr = (char *)p;
229 		}
230 	}
231 	return ret;
232 }
233 
234 static char *
u2s(uintmax_t x,unsigned base,bool uppercase,char * s,size_t * slen_p)235 u2s(uintmax_t x, unsigned base, bool uppercase, char *s, size_t *slen_p) {
236 	unsigned i;
237 
238 	i = U2S_BUFSIZE - 1;
239 	s[i] = '\0';
240 	switch (base) {
241 	case 10:
242 		do {
243 			i--;
244 			s[i] = "0123456789"[x % (uint64_t)10];
245 			x /= (uint64_t)10;
246 		} while (x > 0);
247 		break;
248 	case 16: {
249 		const char *digits = (uppercase)
250 		    ? "0123456789ABCDEF"
251 		    : "0123456789abcdef";
252 
253 		do {
254 			i--;
255 			s[i] = digits[x & 0xf];
256 			x >>= 4;
257 		} while (x > 0);
258 		break;
259 	} default: {
260 		const char *digits = (uppercase)
261 		    ? "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
262 		    : "0123456789abcdefghijklmnopqrstuvwxyz";
263 
264 		assert(base >= 2 && base <= 36);
265 		do {
266 			i--;
267 			s[i] = digits[x % (uint64_t)base];
268 			x /= (uint64_t)base;
269 		} while (x > 0);
270 	}}
271 
272 	*slen_p = U2S_BUFSIZE - 1 - i;
273 	return &s[i];
274 }
275 
276 static char *
d2s(intmax_t x,char sign,char * s,size_t * slen_p)277 d2s(intmax_t x, char sign, char *s, size_t *slen_p) {
278 	bool neg;
279 
280 	if ((neg = (x < 0))) {
281 		x = -x;
282 	}
283 	s = u2s(x, 10, false, s, slen_p);
284 	if (neg) {
285 		sign = '-';
286 	}
287 	switch (sign) {
288 	case '-':
289 		if (!neg) {
290 			break;
291 		}
292 		/* Fall through. */
293 	case ' ':
294 	case '+':
295 		s--;
296 		(*slen_p)++;
297 		*s = sign;
298 		break;
299 	default: not_reached();
300 	}
301 	return s;
302 }
303 
304 static char *
o2s(uintmax_t x,bool alt_form,char * s,size_t * slen_p)305 o2s(uintmax_t x, bool alt_form, char *s, size_t *slen_p) {
306 	s = u2s(x, 8, false, s, slen_p);
307 	if (alt_form && *s != '0') {
308 		s--;
309 		(*slen_p)++;
310 		*s = '0';
311 	}
312 	return s;
313 }
314 
315 static char *
x2s(uintmax_t x,bool alt_form,bool uppercase,char * s,size_t * slen_p)316 x2s(uintmax_t x, bool alt_form, bool uppercase, char *s, size_t *slen_p) {
317 	s = u2s(x, 16, uppercase, s, slen_p);
318 	if (alt_form) {
319 		s -= 2;
320 		(*slen_p) += 2;
321 		memcpy(s, uppercase ? "0X" : "0x", 2);
322 	}
323 	return s;
324 }
325 
326 size_t
malloc_vsnprintf(char * str,size_t size,const char * format,va_list ap)327 malloc_vsnprintf(char *str, size_t size, const char *format, va_list ap) {
328 	size_t i;
329 	const char *f;
330 
331 #define APPEND_C(c) do {						\
332 	if (i < size) {							\
333 		str[i] = (c);						\
334 	}								\
335 	i++;								\
336 } while (0)
337 #define APPEND_S(s, slen) do {						\
338 	if (i < size) {							\
339 		size_t cpylen = (slen <= size - i) ? slen : size - i;	\
340 		memcpy(&str[i], s, cpylen);				\
341 	}								\
342 	i += slen;							\
343 } while (0)
344 #define APPEND_PADDED_S(s, slen, width, left_justify) do {		\
345 	/* Left padding. */						\
346 	size_t pad_len = (width == -1) ? 0 : ((slen < (size_t)width) ?	\
347 	    (size_t)width - slen : 0);					\
348 	if (!left_justify && pad_len != 0) {				\
349 		size_t j;						\
350 		for (j = 0; j < pad_len; j++) {				\
351 			APPEND_C(' ');					\
352 		}							\
353 	}								\
354 	/* Value. */							\
355 	APPEND_S(s, slen);						\
356 	/* Right padding. */						\
357 	if (left_justify && pad_len != 0) {				\
358 		size_t j;						\
359 		for (j = 0; j < pad_len; j++) {				\
360 			APPEND_C(' ');					\
361 		}							\
362 	}								\
363 } while (0)
364 #define GET_ARG_NUMERIC(val, len) do {					\
365 	switch (len) {							\
366 	case '?':							\
367 		val = va_arg(ap, int);					\
368 		break;							\
369 	case '?' | 0x80:						\
370 		val = va_arg(ap, unsigned int);				\
371 		break;							\
372 	case 'l':							\
373 		val = va_arg(ap, long);					\
374 		break;							\
375 	case 'l' | 0x80:						\
376 		val = va_arg(ap, unsigned long);			\
377 		break;							\
378 	case 'q':							\
379 		val = va_arg(ap, long long);				\
380 		break;							\
381 	case 'q' | 0x80:						\
382 		val = va_arg(ap, unsigned long long);			\
383 		break;							\
384 	case 'j':							\
385 		val = va_arg(ap, intmax_t);				\
386 		break;							\
387 	case 'j' | 0x80:						\
388 		val = va_arg(ap, uintmax_t);				\
389 		break;							\
390 	case 't':							\
391 		val = va_arg(ap, ptrdiff_t);				\
392 		break;							\
393 	case 'z':							\
394 		val = va_arg(ap, ssize_t);				\
395 		break;							\
396 	case 'z' | 0x80:						\
397 		val = va_arg(ap, size_t);				\
398 		break;							\
399 	case 'p': /* Synthetic; used for %p. */				\
400 		val = va_arg(ap, uintptr_t);				\
401 		break;							\
402 	default:							\
403 		not_reached();						\
404 		val = 0;						\
405 	}								\
406 } while (0)
407 
408 	i = 0;
409 	f = format;
410 	while (true) {
411 		switch (*f) {
412 		case '\0': goto label_out;
413 		case '%': {
414 			bool alt_form = false;
415 			bool left_justify = false;
416 			bool plus_space = false;
417 			bool plus_plus = false;
418 			int prec = -1;
419 			int width = -1;
420 			unsigned char len = '?';
421 			char *s;
422 			size_t slen;
423 
424 			f++;
425 			/* Flags. */
426 			while (true) {
427 				switch (*f) {
428 				case '#':
429 					assert(!alt_form);
430 					alt_form = true;
431 					break;
432 				case '-':
433 					assert(!left_justify);
434 					left_justify = true;
435 					break;
436 				case ' ':
437 					assert(!plus_space);
438 					plus_space = true;
439 					break;
440 				case '+':
441 					assert(!plus_plus);
442 					plus_plus = true;
443 					break;
444 				default: goto label_width;
445 				}
446 				f++;
447 			}
448 			/* Width. */
449 			label_width:
450 			switch (*f) {
451 			case '*':
452 				width = va_arg(ap, int);
453 				f++;
454 				if (width < 0) {
455 					left_justify = true;
456 					width = -width;
457 				}
458 				break;
459 			case '0': case '1': case '2': case '3': case '4':
460 			case '5': case '6': case '7': case '8': case '9': {
461 				uintmax_t uwidth;
462 				set_errno(0);
463 				uwidth = malloc_strtoumax(f, (char **)&f, 10);
464 				assert(uwidth != UINTMAX_MAX || get_errno() !=
465 				    ERANGE);
466 				width = (int)uwidth;
467 				break;
468 			} default:
469 				break;
470 			}
471 			/* Width/precision separator. */
472 			if (*f == '.') {
473 				f++;
474 			} else {
475 				goto label_length;
476 			}
477 			/* Precision. */
478 			switch (*f) {
479 			case '*':
480 				prec = va_arg(ap, int);
481 				f++;
482 				break;
483 			case '0': case '1': case '2': case '3': case '4':
484 			case '5': case '6': case '7': case '8': case '9': {
485 				uintmax_t uprec;
486 				set_errno(0);
487 				uprec = malloc_strtoumax(f, (char **)&f, 10);
488 				assert(uprec != UINTMAX_MAX || get_errno() !=
489 				    ERANGE);
490 				prec = (int)uprec;
491 				break;
492 			}
493 			default: break;
494 			}
495 			/* Length. */
496 			label_length:
497 			switch (*f) {
498 			case 'l':
499 				f++;
500 				if (*f == 'l') {
501 					len = 'q';
502 					f++;
503 				} else {
504 					len = 'l';
505 				}
506 				break;
507 			case 'q': case 'j': case 't': case 'z':
508 				len = *f;
509 				f++;
510 				break;
511 			default: break;
512 			}
513 			/* Conversion specifier. */
514 			switch (*f) {
515 			case '%':
516 				/* %% */
517 				APPEND_C(*f);
518 				f++;
519 				break;
520 			case 'd': case 'i': {
521 				intmax_t val JEMALLOC_CC_SILENCE_INIT(0);
522 				char buf[D2S_BUFSIZE];
523 
524 				GET_ARG_NUMERIC(val, len);
525 				s = d2s(val, (plus_plus ? '+' : (plus_space ?
526 				    ' ' : '-')), buf, &slen);
527 				APPEND_PADDED_S(s, slen, width, left_justify);
528 				f++;
529 				break;
530 			} case 'o': {
531 				uintmax_t val JEMALLOC_CC_SILENCE_INIT(0);
532 				char buf[O2S_BUFSIZE];
533 
534 				GET_ARG_NUMERIC(val, len | 0x80);
535 				s = o2s(val, alt_form, buf, &slen);
536 				APPEND_PADDED_S(s, slen, width, left_justify);
537 				f++;
538 				break;
539 			} case 'u': {
540 				uintmax_t val JEMALLOC_CC_SILENCE_INIT(0);
541 				char buf[U2S_BUFSIZE];
542 
543 				GET_ARG_NUMERIC(val, len | 0x80);
544 				s = u2s(val, 10, false, buf, &slen);
545 				APPEND_PADDED_S(s, slen, width, left_justify);
546 				f++;
547 				break;
548 			} case 'x': case 'X': {
549 				uintmax_t val JEMALLOC_CC_SILENCE_INIT(0);
550 				char buf[X2S_BUFSIZE];
551 
552 				GET_ARG_NUMERIC(val, len | 0x80);
553 				s = x2s(val, alt_form, *f == 'X', buf, &slen);
554 				APPEND_PADDED_S(s, slen, width, left_justify);
555 				f++;
556 				break;
557 			} case 'c': {
558 				unsigned char val;
559 				char buf[2];
560 
561 				assert(len == '?' || len == 'l');
562 				assert_not_implemented(len != 'l');
563 				val = va_arg(ap, int);
564 				buf[0] = val;
565 				buf[1] = '\0';
566 				APPEND_PADDED_S(buf, 1, width, left_justify);
567 				f++;
568 				break;
569 			} case 's':
570 				assert(len == '?' || len == 'l');
571 				assert_not_implemented(len != 'l');
572 				s = va_arg(ap, char *);
573 				slen = (prec < 0) ? strlen(s) : (size_t)prec;
574 				APPEND_PADDED_S(s, slen, width, left_justify);
575 				f++;
576 				break;
577 			case 'p': {
578 				uintmax_t val;
579 				char buf[X2S_BUFSIZE];
580 
581 				GET_ARG_NUMERIC(val, 'p');
582 				s = x2s(val, true, false, buf, &slen);
583 				APPEND_PADDED_S(s, slen, width, left_justify);
584 				f++;
585 				break;
586 			} default: not_reached();
587 			}
588 			break;
589 		} default: {
590 			APPEND_C(*f);
591 			f++;
592 			break;
593 		}}
594 	}
595 	label_out:
596 	if (i < size) {
597 		str[i] = '\0';
598 	} else {
599 		str[size - 1] = '\0';
600 	}
601 
602 #undef APPEND_C
603 #undef APPEND_S
604 #undef APPEND_PADDED_S
605 #undef GET_ARG_NUMERIC
606 	return i;
607 }
608 
609 JEMALLOC_FORMAT_PRINTF(3, 4)
610 size_t
malloc_snprintf(char * str,size_t size,const char * format,...)611 malloc_snprintf(char *str, size_t size, const char *format, ...) {
612 	size_t ret;
613 	va_list ap;
614 
615 	va_start(ap, format);
616 	ret = malloc_vsnprintf(str, size, format, ap);
617 	va_end(ap);
618 
619 	return ret;
620 }
621 
622 void
malloc_vcprintf(void (* write_cb)(void *,const char *),void * cbopaque,const char * format,va_list ap)623 malloc_vcprintf(void (*write_cb)(void *, const char *), void *cbopaque,
624     const char *format, va_list ap) {
625 	char buf[MALLOC_PRINTF_BUFSIZE];
626 
627 	if (write_cb == NULL) {
628 		/*
629 		 * The caller did not provide an alternate write_cb callback
630 		 * function, so use the default one.  malloc_write() is an
631 		 * inline function, so use malloc_message() directly here.
632 		 */
633 		write_cb = (je_malloc_message != NULL) ? je_malloc_message :
634 		    wrtmessage;
635 		cbopaque = NULL;
636 	}
637 
638 	malloc_vsnprintf(buf, sizeof(buf), format, ap);
639 	write_cb(cbopaque, buf);
640 }
641 
642 /*
643  * Print to a callback function in such a way as to (hopefully) avoid memory
644  * allocation.
645  */
646 JEMALLOC_FORMAT_PRINTF(3, 4)
647 void
malloc_cprintf(void (* write_cb)(void *,const char *),void * cbopaque,const char * format,...)648 malloc_cprintf(void (*write_cb)(void *, const char *), void *cbopaque,
649     const char *format, ...) {
650 	va_list ap;
651 
652 	va_start(ap, format);
653 	malloc_vcprintf(write_cb, cbopaque, format, ap);
654 	va_end(ap);
655 }
656 
657 /* Print to stderr in such a way as to avoid memory allocation. */
658 JEMALLOC_FORMAT_PRINTF(1, 2)
659 void
malloc_printf(const char * format,...)660 malloc_printf(const char *format, ...) {
661 	va_list ap;
662 
663 	va_start(ap, format);
664 	malloc_vcprintf(NULL, NULL, format, ap);
665 	va_end(ap);
666 }
667 
668 /*
669  * Restore normal assertion macros, in order to make it possible to compile all
670  * C files as a single concatenation.
671  */
672 #undef assert
673 #undef not_reached
674 #undef not_implemented
675 #undef assert_not_implemented
676 #include "jemalloc/internal/assert.h"
677