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