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