1 /*
2 * Copyright 2014, General Dynamics C4 Systems
3 *
4 * SPDX-License-Identifier: GPL-2.0-only
5 *
6 * Portions derived from musl:
7 *
8 * Copyright © 2005-2020 Rich Felker, et al.
9 *
10 * SPDX-License-Identifier: MIT
11 */
12
13 #include <config.h>
14 #include <machine/io.h>
15
16 #ifdef CONFIG_PRINTING
17
18 #include <stdarg.h>
19 #include <stdint.h>
20
21 /*
22 *------------------------------------------------------------------------------
23 * printf() core output channel management
24 *------------------------------------------------------------------------------
25 */
26
27 typedef struct _out_wrap_t out_wrap_t;
28
29 /* handler defining how/where to actually output a buffer */
30 typedef void (*out_write_fn)(out_wrap_t *out, const char *buf, word_t len);
31
32 struct _out_wrap_t {
33 const out_write_fn write;
34 char *const buf;
35 const word_t maxlen;
36 word_t used;
37 };
38
39 /* printf_core() and its helpers call this to actually output something. The
40 * parameter 'out_wrap' cam be NULL, e.g. when printf_core() is just caller to
41 * validate the format string. In this case we do nothing.
42 */
out(out_wrap_t * out_wrap,const char * buf,word_t len)43 static void out(out_wrap_t *out_wrap, const char *buf, word_t len)
44 {
45 if (out_wrap) {
46 out_wrap->write(out_wrap, buf, len);
47 }
48 }
49
50 /* An out_write_fn implementation to print the characters via putchar(). It is
51 * guaranteed here that 'out' is not NULL. The current implementation also never
52 * passes NULL for 'buf'. */
do_output_to_putchar(UNUSED out_wrap_t * out,const char * buf,word_t len)53 static void do_output_to_putchar(
54 UNUSED out_wrap_t *out,
55 const char *buf,
56 word_t len)
57 {
58 if (buf) {
59 while (len-- > 0) {
60 putchar(*buf++);
61 }
62 }
63 }
64
65 /* An out_write_fn implementation to copy the buffer into the out buffer. It is
66 * guaranteed here that 'out' is not NULL. The current implementation also never
67 * passes NULL for 'buf'. */
do_output_to_buffer(out_wrap_t * out,const char * buf,word_t len)68 static void do_output_to_buffer(
69 out_wrap_t *out,
70 const char *buf,
71 word_t len)
72 {
73 /* It's guaranteed here that 'out' is not NULL. The current implementation
74 * also never passes NULL for 'buf'. */
75 if (buf && (out->used < out->maxlen)) {
76 /* there is still space in the buffer*/
77 word_t free = out->maxlen - out->used;
78 if (len > free) {
79 len = free;
80 }
81 memcpy(&out->buf[out->used], buf, len);
82 out->used += len;
83 }
84 }
85
86 /*
87 *------------------------------------------------------------------------------
88 * printf() core implementation
89 *------------------------------------------------------------------------------
90 */
91
isdigit(char c)92 static inline bool_t isdigit(char c)
93 {
94 return c >= '0' &&
95 c <= '9';
96 }
97
98 /* Convenient bit representation for modifier flags, which all fall within 31
99 * codepoints of the space character.
100 */
101 #define MASK_TYPE(a) (1U<<( a -' '))
102
103 #define ALT_FORM (1U<<('#'-' '))
104 #define ZERO_PAD (1U<<('0'-' '))
105 #define LEFT_ADJ (1U<<('-'-' '))
106 #define PAD_POS (1U<<(' '-' '))
107 #define MARK_POS (1U<<('+'-' '))
108 #define GROUPED (1U<<('\''-' '))
109
110 #define FLAGMASK (ALT_FORM|ZERO_PAD|LEFT_ADJ|PAD_POS|MARK_POS|GROUPED)
111
112 #define INTMAX_MAX INT32_MAX
113 #define INT_MAX 0x7fffffff
114
115 #define ULONG_MAX ((unsigned long)(-1))
116
117 /* State machine to accept length modifiers + conversion specifiers.
118 * Result is 0 on failure, or an argument type to pop on success.
119 */
120
121 enum {
122 BARE, LPRE, LLPRE, HPRE, HHPRE, BIGLPRE,
123 ZTPRE, JPRE,
124 STOP,
125 PTR, INT, UINT, ULLONG,
126 LONG, ULONG,
127 SHORT, USHORT, CHAR, UCHAR,
128 WORDT, LLONG,
129 #define IMAX LLONG
130 #define UMAX ULLONG
131 #define PDIFF LONG
132 #define UIPTR ULONG
133 NOARG,
134 MAXSTATE
135 };
136
137 #define S(x) [(x)-'A']
138
139 static const unsigned char states[]['z' - 'A' + 1] = {
140 { /* 0: bare types */
141 S('d') = INT, S('i') = INT,
142 S('o') = UINT, S('u') = UINT, S('x') = UINT, S('X') = UINT,
143 S('c') = CHAR,
144 S('s') = PTR, S('p') = UIPTR, S('n') = PTR,
145 S('l') = LPRE, S('h') = HPRE,
146 S('z') = ZTPRE, S('j') = JPRE, S('t') = ZTPRE,
147 }, { /* 1: l-prefixed */
148 S('d') = LONG, S('i') = LONG,
149 S('o') = ULONG, S('u') = ULONG, S('x') = ULONG, S('X') = ULONG,
150 S('n') = PTR,
151 S('l') = LLPRE,
152 }, { /* 2: ll-prefixed */
153 S('d') = LLONG, S('i') = LLONG,
154 S('o') = ULLONG, S('u') = ULLONG,
155 S('x') = ULLONG, S('X') = ULLONG,
156 S('n') = PTR,
157 }, { /* 3: h-prefixed */
158 S('d') = SHORT, S('i') = SHORT,
159 S('o') = USHORT, S('u') = USHORT,
160 S('x') = USHORT, S('X') = USHORT,
161 S('n') = PTR,
162 S('h') = HHPRE,
163 }, { /* 4: hh-prefixed */
164 S('d') = CHAR, S('i') = CHAR,
165 S('o') = UCHAR, S('u') = UCHAR,
166 S('x') = UCHAR, S('X') = UCHAR,
167 S('n') = PTR,
168 }, { /* 5: L-prefixed not supported */
169 }, { /* 6: z- or t-prefixed (assumed to be same size) */
170 S('d') = PDIFF, S('i') = PDIFF,
171 S('o') = WORDT, S('u') = WORDT,
172 S('x') = WORDT, S('X') = WORDT,
173 S('n') = PTR,
174 }, { /* 7: j-prefixed */
175 S('d') = IMAX, S('i') = IMAX,
176 S('o') = UMAX, S('u') = UMAX,
177 S('x') = UMAX, S('X') = UMAX,
178 S('n') = PTR,
179 }
180 };
181
182 #define OOB(x) ((unsigned)(x)-'A' > 'z'-'A')
183 #define DIGIT(c) (c - '0')
184
185 union arg {
186 word_t i;
187 long double f;
188 void *p;
189 };
190
pop_arg(union arg * arg,int type,va_list * ap)191 static void pop_arg(union arg *arg, int type, va_list *ap)
192 {
193 switch (type) {
194 case PTR:
195 arg->p = va_arg(*ap, void *);
196 break;
197 case INT:
198 arg->i = va_arg(*ap, int);
199 break;
200 case UINT:
201 arg->i = va_arg(*ap, unsigned int);
202 break;
203 case LONG:
204 arg->i = va_arg(*ap, long);
205 break;
206 case ULONG:
207 arg->i = va_arg(*ap, unsigned long);
208 break;
209 case LLONG:
210 arg->i = va_arg(*ap, long long);
211 break;
212 case ULLONG:
213 arg->i = va_arg(*ap, unsigned long long);
214 break;
215 case SHORT:
216 arg->i = (short)va_arg(*ap, int);
217 break;
218 case USHORT:
219 arg->i = (unsigned short)va_arg(*ap, int);
220 break;
221 case CHAR:
222 arg->i = (signed char)va_arg(*ap, int);
223 break;
224 case UCHAR:
225 arg->i = (unsigned char)va_arg(*ap, int);
226 break;
227 case WORDT:
228 arg->i = va_arg(*ap, word_t);
229 }
230 }
231
232
pad(out_wrap_t * f,char c,int w,int l,int fl)233 static void pad(out_wrap_t *f, char c, int w, int l, int fl)
234 {
235 char pad[32]; /* good enough for what the kernel prints */
236 if (fl & (LEFT_ADJ | ZERO_PAD) || l >= w) {
237 return;
238 }
239 l = w - l;
240 memset(pad, c, l > sizeof(pad) ? sizeof(pad) : l);
241 for (; l >= sizeof(pad); l -= sizeof(pad)) {
242 out(f, pad, sizeof(pad));
243 }
244 out(f, pad, l);
245 }
246
247 static const char xdigits[16] = {
248 "0123456789ABCDEF"
249 };
250
fmt_x(word_t x,char * s,int lower)251 static char *fmt_x(word_t x, char *s, int lower)
252 {
253 for (; x; x >>= 4) {
254 *--s = xdigits[(x & 15)] | lower;
255 }
256 return s;
257 }
258
fmt_o(word_t x,char * s)259 static char *fmt_o(word_t x, char *s)
260 {
261 for (; x; x >>= 3) {
262 *--s = '0' + (x & 7);
263 }
264 return s;
265 }
266
fmt_u(word_t x,char * s)267 static char *fmt_u(word_t x, char *s)
268 {
269 unsigned long y;
270 for (; x > ULONG_MAX; x /= 10) {
271 *--s = '0' + x % 10;
272 }
273 for (y = x; y; y /= 10) {
274 *--s = '0' + y % 10;
275 }
276 return s;
277 }
278
279 /* Maximum buffer size taken to ensure correct adaptation. However, it could be
280 * reduced/removed if we could measure the buf length under all code paths
281 */
282 #define LDBL_MANT_DIG 113
283
284 #define NL_ARGMAX 9
285
getint(char ** s)286 static int getint(char **s)
287 {
288 int i;
289 for (i = 0; isdigit(**s); (*s)++) {
290 if (i > INT_MAX / 10U || DIGIT(**s) > INT_MAX - 10 * i) {
291 i = -1;
292 } else {
293 i = 10 * i + DIGIT(**s);
294 }
295 }
296 return i;
297 }
298
printf_core(out_wrap_t * f,const char * fmt,va_list * ap,union arg * nl_arg,int * nl_type)299 static int printf_core(out_wrap_t *f, const char *fmt, va_list *ap, union arg *nl_arg, int *nl_type)
300 {
301 char *a, *z, *s = (char *)fmt;
302 unsigned l10n = 0, fl;
303 int w, p, xp;
304 union arg arg;
305 int argpos;
306 unsigned st, ps;
307 int cnt = 0, l = 0;
308 word_t i;
309 char buf[sizeof(word_t) * 3 + 3 + LDBL_MANT_DIG / 4];
310 const char *prefix;
311 int t, pl;
312
313 for (;;) {
314 if (l > INT_MAX - cnt) {
315 /* This error is only specified for snprintf, for other function
316 * from the printf() family the behavior is unspecified. Stopping
317 * immediately here also seems sane, otherwise %n could produce
318 * wrong results.
319 */
320 return -1; /* overflow */
321 }
322
323 /* Update output count, end loop when fmt is exhausted */
324 cnt += l;
325 if (!*s) {
326 break;
327 }
328
329 /* Handle literal text and %% format specifiers */
330 for (a = s; *s && *s != '%'; s++);
331 for (z = s; s[0] == '%' && s[1] == '%'; z++, s += 2);
332 if (z - a > INT_MAX - cnt) {
333 return -1; /* overflow */
334 }
335 l = z - a;
336 out(f, a, l);
337 if (l) {
338 continue;
339 }
340
341 if (isdigit(s[1]) && s[2] == '$') {
342 l10n = 1;
343 argpos = DIGIT(s[1]);
344 s += 3;
345 } else {
346 argpos = -1;
347 s++;
348 }
349
350 /* Read modifier flags */
351 for (fl = 0; (unsigned)*s - ' ' < 32 && (FLAGMASK & MASK_TYPE(*s)); s++) {
352 fl |= MASK_TYPE(*s);
353 }
354
355 /* Read field width */
356 if (*s == '*') {
357 if (isdigit(s[1]) && s[2] == '$') {
358 l10n = 1;
359 nl_type[DIGIT(s[1])] = INT;
360 w = nl_arg[DIGIT(s[1])].i;
361 s += 3;
362 } else if (!l10n) {
363 w = f ? va_arg(*ap, int) : 0;
364 s++;
365 } else {
366 return -1; /* invalid */
367 }
368 if (w < 0) {
369 fl |= LEFT_ADJ;
370 w = -w;
371 }
372 } else if ((w = getint(&s)) < 0) {
373 return -1; /* overflow */
374 }
375
376 /* Read precision */
377 if (*s == '.' && s[1] == '*') {
378 if (isdigit(s[2]) && s[3] == '$') {
379 nl_type[DIGIT(s[2])] = INT;
380 p = nl_arg[DIGIT(s[2])].i;
381 s += 4;
382 } else if (!l10n) {
383 p = f ? va_arg(*ap, int) : 0;
384 s += 2;
385 } else {
386 return -1;/* invalid */
387 }
388 xp = (p >= 0);
389 } else if (*s == '.') {
390 s++;
391 p = getint(&s);
392 xp = 1;
393 } else {
394 p = -1;
395 xp = 0;
396 }
397
398 /* Format specifier state machine */
399 st = 0;
400 do {
401 if (OOB(*s)) {
402 return -1; /* invalid */
403 }
404 ps = st;
405 st = states[st]S(*s++);
406 } while (st - 1 < STOP);
407 if (!st) {
408 return -1; /* invalid */
409 }
410
411 /* Check validity of argument type (nl/normal) */
412 if (st == NOARG) {
413 if (argpos >= 0) {
414 return -1; /* invalid */
415 }
416 } else {
417 if (argpos >= 0) {
418 nl_type[argpos] = st;
419 arg = nl_arg[argpos];
420 } else if (f) {
421 pop_arg(&arg, st, ap);
422 } else {
423 return 0;
424 }
425 }
426
427 if (!f) {
428 continue;
429 }
430
431 z = buf + sizeof(buf);
432 prefix = "-+ 0X0x";
433 pl = 0;
434 t = s[-1];
435
436 /* - and 0 flags are mutually exclusive */
437 if (fl & LEFT_ADJ) {
438 fl &= ~ZERO_PAD;
439 }
440
441 if (t == 'n') {
442 if (!arg.p) {
443 continue;
444 }
445 switch (ps) {
446 case BARE:
447 *(int *)arg.p = cnt;
448 break;
449 case LPRE:
450 *(long *)arg.p = cnt;
451 break;
452 case LLPRE:
453 *(long long *)arg.p = cnt;
454 break;
455 case HPRE:
456 *(unsigned short *)arg.p = cnt;
457 break;
458 case HHPRE:
459 *(unsigned char *)arg.p = cnt;
460 break;
461 case ZTPRE:
462 *(word_t *)arg.p = cnt;
463 break;
464 case JPRE:
465 *(word_t *)arg.p = cnt;
466 break;
467 }
468 continue;
469 } else if (t == 'c') {
470 p = 1;
471 a = z - p;
472 *a = arg.i;
473 fl &= ~ZERO_PAD;
474 } else if (t == 's') {
475 a = arg.p ? arg.p : "(null)";
476 z = a + strnlen(a, p < 0 ? INT_MAX : p);
477 if (p < 0 && *z) {
478 return -1; /* overflow */
479 }
480 p = z - a;
481 fl &= ~ZERO_PAD;
482 } else {
483 switch (t) {
484 case 'p':
485 p = MAX(p, 2 * sizeof(void *));
486 t = 'x';
487 fl |= ALT_FORM;
488 case 'x':
489 case 'X':
490 a = fmt_x(arg.i, z, t & 32);
491 if (arg.i && (fl & ALT_FORM)) {
492 prefix += (t >> 4);
493 pl = 2;
494 }
495 break;
496 case 'o':
497 a = fmt_o(arg.i, z);
498 if ((fl & ALT_FORM) && p < (z - a + 1)) {
499 p = z - a + 1;
500 }
501 break;
502 case 'd':
503 case 'i':
504 pl = 1;
505 if (arg.i > INTMAX_MAX) {
506 arg.i = -arg.i;
507 } else if (fl & MARK_POS) {
508 prefix++;
509 } else if (fl & PAD_POS) {
510 prefix += 2;
511 } else {
512 pl = 0;
513 }
514 case 'u':
515 a = fmt_u(arg.i, z);
516 break;
517 }
518 if (xp && p < 0) {
519 return -1; /* overflow */
520 }
521 if (xp) {
522 fl &= ~ZERO_PAD;
523 }
524 if (!arg.i && !p) {
525 a = z;
526 } else {
527 p = MAX(p, z - a + !arg.i);
528 }
529 }
530
531 if (p < z - a) {
532 p = z - a;
533 }
534 if (p > INT_MAX - pl) {
535 return -1; /* overflow */
536 }
537 if (w < pl + p) {
538 w = pl + p;
539 }
540 if (w > INT_MAX - cnt) {
541 return -1; /* overflow */
542 }
543
544 pad(f, ' ', w, pl + p, fl);
545 out(f, prefix, pl);
546 pad(f, '0', w, pl + p, fl ^ ZERO_PAD);
547 pad(f, '0', p, z - a, 0);
548 out(f, a, z - a);
549 pad(f, ' ', w, pl + p, fl ^ LEFT_ADJ);
550
551 l = w;
552 }
553
554 if (f) {
555 return cnt;
556 }
557 if (!l10n) {
558 return 0;
559 }
560
561 for (i = 1; i <= NL_ARGMAX && nl_type[i]; i++) {
562 pop_arg(nl_arg + i, nl_type[i], ap);
563 }
564 for (; i <= NL_ARGMAX && !nl_type[i]; i++);
565 if (i <= NL_ARGMAX) {
566 return -1; /* invalid */
567 }
568
569 return 1;
570 }
571
vprintf(out_wrap_t * out,const char * fmt,va_list ap)572 static int vprintf(out_wrap_t *out, const char *fmt, va_list ap)
573 {
574 va_list ap2;
575 int nl_type[NL_ARGMAX + 1] = {0};
576 union arg nl_arg[NL_ARGMAX + 1];
577 int ret;
578
579 /* validate format string */
580 va_copy(ap2, ap);
581 if (printf_core(NULL, fmt, &ap2, nl_arg, nl_type) < 0) {
582 va_end(ap2);
583 return -1;
584 }
585
586 ret = printf_core(out, fmt, &ap2, nl_arg, nl_type);
587 va_end(ap2);
588 return ret;
589 }
590
591 /*
592 *------------------------------------------------------------------------------
593 * Kernel printing API
594 *------------------------------------------------------------------------------
595 */
596
impl_kvprintf(const char * format,va_list ap)597 int impl_kvprintf(const char *format, va_list ap)
598 {
599 out_wrap_t out_wrap = {
600 .write = do_output_to_putchar,
601 .buf = NULL,
602 .maxlen = 0,
603 .used = 0
604 };
605
606 return vprintf(&out_wrap, format, ap);
607 }
608
impl_ksnvprintf(char * str,word_t size,const char * format,va_list ap)609 int impl_ksnvprintf(char *str, word_t size, const char *format, va_list ap)
610 {
611 if (!str) {
612 size = 0;
613 }
614
615 out_wrap_t out_wrap = {
616 .write = do_output_to_buffer,
617 .buf = str,
618 .maxlen = size,
619 .used = 0
620 };
621
622 int ret = vprintf(&out_wrap, format, ap);
623
624 /* We return the number of characters written into the buffer, excluding the
625 * terminating null char. However, we do never write more than 'size' bytes,
626 * that includes the terminating null char. If the output was truncated due
627 * to this limit, then the return value is the number of chars excluding the
628 * terminating null byte, which would have been written to the buffer, if
629 * enough space had been available. Thus, a return value of 'size' or more
630 * means that the output was truncated.
631 */
632 if ((ret > 0) && (size > 0)) {
633 str[(ret < size) ? ret : size - 1] = '\0';
634 }
635
636 return ret;
637 }
638
639 #endif /* CONFIG_PRINTING */
640