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