1 /*
2 * Copyright (C) 2011,2016 Red Hat, Inc.
3 *
4 * All rights reserved.
5 *
6 * Author: Angus Salkeld <asalkeld@redhat.com>
7 *
8 * libqb is free software: you can redistribute it and/or modify
9 * it under the terms of the GNU Lesser General Public License as published by
10 * the Free Software Foundation, either version 2.1 of the License, or
11 * (at your option) any later version.
12 *
13 * libqb is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU Lesser General Public License for more details.
17 *
18 * You should have received a copy of the GNU Lesser General Public License
19 * along with libqb. If not, see <http://www.gnu.org/licenses/>.
20 */
21 #include "os_base.h"
22 #include <ctype.h>
23
24 #include <qb/qbdefs.h>
25 #include "log_int.h"
26
27 static qb_log_tags_stringify_fn _user_tags_stringify_fn;
28
29 /*
30 * syslog prioritynames, facility names to value mapping
31 * Some C libraries build this in to their headers, but it is non-portable
32 * so logsys supplies its own version.
33 */
34 struct syslog_names {
35 const char *c_name;
36 int32_t c_val;
37 };
38
39 static struct syslog_names prioritynames[] = {
40 {"emerg", LOG_EMERG},
41 {"alert", LOG_ALERT},
42 {"crit", LOG_CRIT},
43 {"error", LOG_ERR},
44 {"warning", LOG_WARNING},
45 {"notice", LOG_NOTICE},
46 {"info", LOG_INFO},
47 {"debug", LOG_DEBUG},
48 {"trace", LOG_TRACE},
49 {NULL, -1}
50 };
51
52 static struct syslog_names facilitynames[] = {
53 {"auth", LOG_AUTH},
54 #if defined(LOG_AUTHPRIV)
55 {"authpriv", LOG_AUTHPRIV},
56 #endif
57 {"cron", LOG_CRON},
58 {"daemon", LOG_DAEMON},
59 #if defined(LOG_FTP)
60 {"ftp", LOG_FTP},
61 #endif
62 {"kern", LOG_KERN},
63 {"lpr", LOG_LPR},
64 {"mail", LOG_MAIL},
65 {"news", LOG_NEWS},
66 {"syslog", LOG_SYSLOG},
67 {"user", LOG_USER},
68 {"uucp", LOG_UUCP},
69 {"local0", LOG_LOCAL0},
70 {"local1", LOG_LOCAL1},
71 {"local2", LOG_LOCAL2},
72 {"local3", LOG_LOCAL3},
73 {"local4", LOG_LOCAL4},
74 {"local5", LOG_LOCAL5},
75 {"local6", LOG_LOCAL6},
76 {"local7", LOG_LOCAL7},
77 {NULL, -1}
78 };
79
80 static const char log_month_name[][4] = {
81 "Jan", "Feb", "Mar", "Apr", "May", "Jun",
82 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
83 };
84
85 static pthread_rwlock_t _formatlock;
86
87 void
88 qb_log_format_init(void)
89 {
90 int32_t l;
91 struct qb_log_target *t;
92 enum qb_log_target_slot i;
93
94 l = pthread_rwlock_init(&_formatlock, NULL);
95 assert(l == 0);
96
97 for (i = QB_LOG_TARGET_START; i < QB_LOG_TARGET_MAX; i++) {
98 t = qb_log_target_get(i);
99 t->format = strdup("[%p] %b");
100 }
101 }
102
103 void
104 qb_log_format_fini(void)
105 {
106 struct qb_log_target *t;
107 enum qb_log_target_slot i;
108
109 pthread_rwlock_destroy(&_formatlock);
110
111 for (i = QB_LOG_TARGET_START; i < QB_LOG_TARGET_MAX; i++) {
112 t = qb_log_target_get(i);
113 free(t->format);
114 }
115 }
116
117 void
118 qb_log_format_set(int32_t target, const char *format)
119 {
120 char modified_format[256];
121 struct qb_log_target *t = qb_log_target_get(target);
122
123 pthread_rwlock_wrlock(&_formatlock);
124
125 free(t->format);
126
127 if (format) {
128 qb_log_target_format_static(target, format, modified_format);
129 t->format = strdup(modified_format);
130 } else {
131 t->format = strdup("[%p] %b");
132 }
133 assert(t->format != NULL);
134
135 pthread_rwlock_unlock(&_formatlock);
136 }
137
138 /* Convert string "auth" to equivalent number "LOG_AUTH" etc. */
139 int32_t
140 qb_log_facility2int(const char *fname)
141 {
142 int32_t i;
143
144 if (fname == NULL) {
145 return -EINVAL;
146 }
147
148 for (i = 0; facilitynames[i].c_name != NULL; i++) {
149 if (strcmp(fname, facilitynames[i].c_name) == 0) {
150 return facilitynames[i].c_val;
151 }
152 }
153 return -EINVAL;
154 }
155
156 /* Convert number "LOG_AUTH" to equivalent string "auth" etc. */
157 const char *
158 qb_log_facility2str(int32_t fnum)
159 {
160 int32_t i;
161
162 for (i = 0; facilitynames[i].c_name != NULL; i++) {
163 if (facilitynames[i].c_val == fnum) {
164 return facilitynames[i].c_name;
165 }
166 }
167 return NULL;
168 }
169
170 const char *
171 qb_log_priority2str(uint8_t priority)
172 {
173 if (priority > LOG_TRACE) {
174 return prioritynames[LOG_TRACE].c_name;
175 }
176 return prioritynames[priority].c_name;
177 }
178
179 void
180 qb_log_tags_stringify_fn_set(qb_log_tags_stringify_fn fn)
181 {
182 _user_tags_stringify_fn = fn;
183 }
184
185 static int
186 _strcpy_cutoff(char *dest, const char *src, size_t cutoff, int ralign,
187 size_t buf_len)
188 {
189 size_t len = strlen(src);
190 if (buf_len <= 1) {
191 if (buf_len == 0)
192 dest[0] = 0;
193 return 0;
194 }
195
196 if (cutoff == 0) {
197 cutoff = len;
198 }
199
200 cutoff = QB_MIN(cutoff, buf_len - 1);
201 len = QB_MIN(len, cutoff);
202 if (ralign) {
203 memset(dest, ' ', cutoff - len);
204 memcpy(dest + cutoff - len, src, len);
205 } else {
206 memcpy(dest, src, len);
207 memset(dest + len, ' ', cutoff - len);
208 }
209
210 dest[cutoff] = '\0';
211
212 return cutoff;
213 }
214
215 /*
216 * This function will do static formatting (for things that don't
217 * change on each log message).
218 *
219 * %P PID
220 * %N name passed into qb_log_init
221 * %H hostname
222 *
223 * any number between % and character specify field length to pad or chop
224 */
225 void
226 qb_log_target_format_static(int32_t target, const char * format,
227 char *output_buffer)
228 {
229 char tmp_buf[255];
230 unsigned int format_buffer_idx = 0;
231 unsigned int output_buffer_idx = 0;
232 size_t cutoff;
233 uint32_t len;
234 int ralign;
235 int c;
236 struct qb_log_target *t = qb_log_target_get(target);
237
238 if (format == NULL) {
239 return;
240 }
241
242 while ((c = format[format_buffer_idx])) {
243 cutoff = 0;
244 ralign = QB_FALSE;
245 if (c != '%') {
246 output_buffer[output_buffer_idx++] = c;
247 format_buffer_idx++;
248 } else {
249 const char *p;
250 unsigned int percent_buffer_idx = format_buffer_idx;
251
252 format_buffer_idx += 1;
253 if (format[format_buffer_idx] == '-') {
254 ralign = QB_TRUE;
255 format_buffer_idx += 1;
256 }
257
258 if (isdigit(format[format_buffer_idx])) {
259 cutoff = atoi(&format[format_buffer_idx]);
260 }
261 while (isdigit(format[format_buffer_idx])) {
262 format_buffer_idx += 1;
263 }
264
265 switch (format[format_buffer_idx]) {
266 case 'P':
267 snprintf(tmp_buf, 30, "%d", getpid());
268 p = tmp_buf;
269 break;
270
271 case 'N':
272 p = t->name;
273 break;
274
275 case 'H':
276 if (gethostname(tmp_buf, sizeof(tmp_buf)) == 0) {
277 tmp_buf[sizeof(tmp_buf) - 1] = '\0';
278 } else {
279 (void)strlcpy(tmp_buf, "localhost",
280 sizeof(tmp_buf));
281 }
282 p = tmp_buf;
283 break;
284
285 default:
286 p = &format[percent_buffer_idx];
287 cutoff = (format_buffer_idx - percent_buffer_idx + 1);
288 ralign = QB_FALSE;
289 break;
290 }
291 len = _strcpy_cutoff(output_buffer + output_buffer_idx,
292 p, cutoff, ralign,
293 (t->max_line_length -
294 output_buffer_idx));
295 output_buffer_idx += len;
296 format_buffer_idx += 1;
297 }
298 if (output_buffer_idx >= t->max_line_length - 1) {
299 break;
300 }
301 }
302
303 output_buffer[output_buffer_idx] = '\0';
304 }
305
306 /*
307 * %n FUNCTION NAME
308 * %f FILENAME
309 * %l FILELINE
310 * %p PRIORITY
311 * %t TIMESTAMP
312 * %T TIMESTAMP with milliseconds
313 * %b BUFFER
314 * %g SUBSYSTEM
315 *
316 * any number between % and character specify field length to pad or chop
317 */
318 void
319 qb_log_target_format(int32_t target,
320 struct qb_log_callsite *cs,
321 struct timespec *the_ts,
322 const char *formatted_message, char *output_buffer)
323 {
324 char tmp_buf[128];
325 struct tm tm_res;
326 time_t time_sec;
327 unsigned int format_buffer_idx = 0;
328 unsigned int output_buffer_idx = 0;
329 size_t cutoff;
330 uint32_t len;
331 int ralign;
332 int c;
333 struct qb_log_target *t = qb_log_target_get(target);
334
335 pthread_rwlock_rdlock(&_formatlock);
336 if (t->format == NULL) {
337 pthread_rwlock_unlock(&_formatlock);
338 return;
339 }
340
341 while ((c = t->format[format_buffer_idx])) {
342 cutoff = 0;
343 ralign = QB_FALSE;
344 if (c != '%') {
345 output_buffer[output_buffer_idx++] = c;
346 format_buffer_idx++;
347 } else {
348 const char *p;
349
350 format_buffer_idx += 1;
351 if (t->format[format_buffer_idx] == '-') {
352 ralign = QB_TRUE;
353 format_buffer_idx += 1;
354 }
355
356 if (isdigit(t->format[format_buffer_idx])) {
357 cutoff = atoi(&t->format[format_buffer_idx]);
358 }
359 while (isdigit(t->format[format_buffer_idx])) {
360 format_buffer_idx += 1;
361 }
362
363 switch (t->format[format_buffer_idx]) {
364 case 'g':
365 if (_user_tags_stringify_fn) {
366 p = _user_tags_stringify_fn(cs->tags);
367 } else {
368 p = "";
369 }
370 break;
371
372 case 'n':
373 p = cs->function;
374 break;
375
376 case 'f':
377 #ifdef BUILDING_IN_PLACE
378 p = cs->filename;
379 #else
380 p = strrchr(cs->filename, '/');
381 if (p == NULL) {
382 p = cs->filename;
383 } else {
384 p++; /* move past the "/" */
385 }
386 #endif /* BUILDING_IN_PLACE */
387 break;
388
389 case 'l':
390 #ifndef S_SPLINT_S
391 snprintf(tmp_buf, 30, "%" PRIu32, cs->lineno);
392 #endif /* S_SPLINT_S */
393 p = tmp_buf;
394 break;
395
396 case 't':
397 time_sec = the_ts->tv_sec;
398 (void)localtime_r(&time_sec, &tm_res);
399 snprintf(tmp_buf, TIME_STRING_SIZE,
400 "%s %02d %02d:%02d:%02d",
401 log_month_name[tm_res.tm_mon],
402 tm_res.tm_mday, tm_res.tm_hour,
403 tm_res.tm_min, tm_res.tm_sec);
404 p = tmp_buf;
405 break;
406
407 case 'T':
408 time_sec = the_ts->tv_sec;
409 (void)localtime_r(&time_sec, &tm_res);
410 snprintf(tmp_buf, TIME_STRING_SIZE,
411 "%s %02d %02d:%02d:%02d.%03llu",
412 log_month_name[tm_res.tm_mon],
413 tm_res.tm_mday, tm_res.tm_hour,
414 tm_res.tm_min, tm_res.tm_sec,
415 the_ts->tv_nsec/QB_TIME_NS_IN_MSEC);
416 p = tmp_buf;
417 break;
418
419 case 'b':
420 p = formatted_message;
421 break;
422
423 case 'p':
424 if (cs->priority > LOG_TRACE) {
425 p = prioritynames[LOG_TRACE].c_name;
426 } else {
427 p = prioritynames[cs->priority].c_name;
428 }
429 break;
430
431 default:
432 p = "";
433 break;
434 }
435 len = _strcpy_cutoff(output_buffer + output_buffer_idx,
436 p, cutoff, ralign,
437 (t->max_line_length -
438 output_buffer_idx));
439 output_buffer_idx += len;
440 format_buffer_idx += 1;
441 }
442 if (output_buffer_idx >= t->max_line_length - 1) {
443 break;
444 }
445 }
446 pthread_rwlock_unlock(&_formatlock);
447
448 if (output_buffer[output_buffer_idx - 1] == '\n') {
449 output_buffer[output_buffer_idx - 1] = '\0';
450 } else {
451 output_buffer[output_buffer_idx] = '\0';
452 }
453
454 /* Indicate truncation */
455 if (t->ellipsis && output_buffer_idx >= t->max_line_length-1) {
456 output_buffer[output_buffer_idx-3] = '.';
457 output_buffer[output_buffer_idx-2] = '.';
458 output_buffer[output_buffer_idx-1] = '.';
459 }
460 }
461
462
463 /*
464 * These wrappers around strl* functions just return the
465 * number of characters written, not the number of characters
466 * requested to be written.
467 */
468 static size_t
469 my_strlcpy(char *dest, const char * src, size_t maxlen)
470 {
471 size_t rc = strlcpy(dest, src, maxlen);
472 /* maxlen includes NUL, so -1 */
473 return QB_MIN(rc, maxlen-1);
474 }
475
476 static size_t
477 my_strlcat(char *dest, const char * src, size_t maxlen)
478 {
479 size_t rc = strlcat(dest, src, maxlen);
480 return QB_MIN(rc, maxlen-1);
481 }
482
483 size_t
484 qb_vsnprintf_serialize(char *serialize, size_t max_len,
485 const char *fmt, va_list ap)
486 {
487 char *format;
488 char *p;
489 char *qb_xc;
490 int type_long = QB_FALSE;
491 int type_longlong = QB_FALSE;
492 size_t sformat_length = 0;
493 int sformat_precision = QB_FALSE;
494 uint32_t location = my_strlcpy(serialize, fmt, max_len) + 1;
495
496 /* Assume serialized output always wants extended information
497 * (@todo: add variant of this function that takes argument for whether
498 * to print extended information, and make this a macro with that
499 * argument set to QB_TRUE, so callers can honor extended setting)
500 */
|
(1) Event cond_true: |
Condition "(qb_xc = strchr(serialize, 7)) != NULL", taking true branch. |
501 if ((qb_xc = strchr(serialize, QB_XC)) != NULL) {
|
(2) Event cond_true: |
Condition "qb_xc[1]", taking true branch. |
502 *qb_xc = *(qb_xc + 1)? '|' : '\0';
503 }
504
505 format = (char *)fmt;
|
(39) Event loop_begin: |
Jumped back to beginning of loop. |
|
(53) Event loop_begin: |
Jumped back to beginning of loop. |
506 for (;;) {
507 type_long = QB_FALSE;
508 type_longlong = QB_FALSE;
509 p = strchrnul((const char *)format, '%');
|
(3) Event cond_false: |
Condition "*p == 0", taking false branch. |
|
(40) Event cond_false: |
Condition "*p == 0", taking false branch. |
|
(54) Event cond_true: |
Condition "*p == 0", taking true branch. |
510 if (*p == '\0') {
|
(55) Event break: |
Breaking from loop. |
511 break;
|
(4) Event if_end: |
End of if statement. |
|
(41) Event if_end: |
End of if statement. |
512 }
513 format = p + 1;
|
(8) Event label: |
Reached label "reprocess". |
|
(13) Event label: |
Reached label "reprocess". |
|
(19) Event label: |
Reached label "reprocess". |
|
(24) Event label: |
Reached label "reprocess". |
|
(30) Event label: |
Reached label "reprocess". |
514 reprocess:
|
(5) Event switch: |
Switch case value "'.'". |
|
(9) Event switch: |
Switch case value "'0'". |
|
(14) Event switch: |
Switch case value "'*'". |
|
(20) Event switch: |
Switch case value "'l'". |
|
(25) Event switch: |
Switch case value "'l'". |
|
(31) Event switch: |
Switch case value "'d'". |
|
(42) Event switch: |
Switch case value "'s'". |
515 switch (format[0]) {
516 case '#': /* alternate form conversion, ignore */
517 case '-': /* left adjust, ignore */
518 case ' ': /* a space, ignore */
519 case '+': /* a sign should be used, ignore */
520 case '\'': /* group in thousands, ignore */
521 case 'I': /* glibc-ism locale alternative, ignore */
522 format++;
523 goto reprocess;
|
(6) Event switch_case: |
Reached case "'.'". |
524 case '.': /* precision, ignore */
525 format++;
526 sformat_precision = QB_TRUE;
|
(7) Event goto: |
Jumping to label "reprocess". |
527 goto reprocess;
|
(10) Event switch_case: |
Reached case "'0'". |
528 case '0': /* field width, ignore */
529 case '1': /* field width, ignore */
530 case '2': /* field width, ignore */
531 case '3': /* field width, ignore */
532 case '4': /* field width, ignore */
533 case '5': /* field width, ignore */
534 case '6': /* field width, ignore */
535 case '7': /* field width, ignore */
536 case '8': /* field width, ignore */
537 case '9': /* field width, ignore */
|
(11) Event cond_true: |
Condition "sformat_precision", taking true branch. |
538 if (sformat_precision) {
539 sformat_length *= 10;
540 sformat_length += (format[0] - '0');
541 }
542 format++;
|
(12) Event goto: |
Jumping to label "reprocess". |
543 goto reprocess;
|
(15) Event switch_case: |
Reached case "'*'". |
544 case '*': /* variable field width, save */ {
545 int arg_int = va_arg(ap, int);
|
(16) Event cond_false: |
Condition "location + 4UL /* sizeof (int) */ > max_len", taking false branch. |
546 if (location + sizeof (int) > max_len) {
547 return max_len;
|
(17) Event if_end: |
End of if statement. |
548 }
549 memcpy(&serialize[location], &arg_int, sizeof (int));
550 location += sizeof(int);
551 format++;
|
(18) Event goto: |
Jumping to label "reprocess". |
552 goto reprocess;
553 }
|
(21) Event switch_case: |
Reached case "'l'". |
|
(26) Event switch_case: |
Reached case "'l'". |
554 case 'l':
555 format++;
556 type_long = QB_TRUE;
|
(22) Event cond_true: |
Condition "*format == 'l'", taking true branch. |
|
(27) Event cond_false: |
Condition "*format == 'l'", taking false branch. |
557 if (*format == 'l') {
558 type_long = QB_FALSE;
559 type_longlong = QB_TRUE;
560 format++;
|
(28) Event if_end: |
End of if statement. |
561 }
|
(23) Event goto: |
Jumping to label "reprocess". |
|
(29) Event goto: |
Jumping to label "reprocess". |
562 goto reprocess;
563 case 'z':
564 format++;
565 if (sizeof(size_t) == sizeof(long long)) {
566 type_longlong = QB_TRUE;
567 } else {
568 type_long = QB_TRUE;
569 }
570 goto reprocess;
571 case 't':
572 format++;
573 if (sizeof(ptrdiff_t) == sizeof(long long)) {
574 type_longlong = QB_TRUE;
575 } else {
576 type_long = QB_TRUE;
577 }
578 goto reprocess;
579 case 'j':
580 format++;
581 if (sizeof(intmax_t) == sizeof(long long)) {
582 type_longlong = QB_TRUE;
583 } else {
584 type_long = QB_TRUE;
585 }
586 goto reprocess;
|
(32) Event switch_case: |
Reached case "'d'". |
587 case 'd': /* int argument */
588 case 'i': /* int argument */
589 case 'o': /* unsigned int argument */
590 case 'u':
591 case 'x':
592 case 'X':
|
(33) Event cond_true: |
Condition "type_long", taking true branch. |
593 if (type_long) {
594 long int arg_int;
595
|
(34) Event cond_false: |
Condition "location + 8UL /* sizeof (long) */ > max_len", taking false branch. |
596 if (location + sizeof (long int) > max_len) {
597 return max_len;
|
(35) Event if_end: |
End of if statement. |
598 }
599 arg_int = va_arg(ap, long int);
600 memcpy(&serialize[location], &arg_int,
601 sizeof(long int));
602 location += sizeof(long int);
603 format++;
|
(36) Event break: |
Breaking from switch. |
604 break;
605 } else if (type_longlong) {
606 long long int arg_int;
607
608 if (location + sizeof (long long int) > max_len) {
609 return max_len;
610 }
611 arg_int = va_arg(ap, long long int);
612 memcpy(&serialize[location], &arg_int,
613 sizeof(long long int));
614 location += sizeof(long long int);
615 format++;
616 break;
617 } else {
618 int arg_int;
619
620 if (location + sizeof (int) > max_len) {
621 return max_len;
622 }
623 arg_int = va_arg(ap, int);
624 memcpy(&serialize[location], &arg_int,
625 sizeof(int));
626 location += sizeof(int);
627 format++;
628 break;
629 }
630 case 'e':
631 case 'E':
632 case 'f':
633 case 'F':
634 case 'g':
635 case 'G':
636 case 'a':
637 case 'A':
638 {
639 double arg_double;
640
641 if (location + sizeof (double) > max_len) {
642 return max_len;
643 }
644 arg_double = va_arg(ap, double);
645 memcpy (&serialize[location], &arg_double, sizeof (double));
646 location += sizeof(double);
647 format++;
648 break;
649 }
650 case 'c':
651 {
652 int arg_int;
653 unsigned char arg_char;
654
655 if (location + sizeof (unsigned char) > max_len) {
656 return max_len;
657 }
658 /* va_arg only takes fully promoted types */
659 arg_int = va_arg(ap, unsigned int);
660 arg_char = (unsigned char)arg_int;
661 memcpy (&serialize[location], &arg_char, sizeof (unsigned char));
662 location += sizeof(unsigned char);
663 break;
664 }
|
(43) Event switch_case: |
Reached case "'s'". |
665 case 's':
666 {
667 char *arg_string;
668 arg_string = va_arg(ap, char *);
|
(44) Event cond_true: |
Condition "arg_string == NULL", taking true branch. |
669 if (arg_string == NULL) {
|
(45) Event cond_true: |
Condition "strlen("(null)") + 1 < max_len - location", taking true branch. |
|
(46) Event tainted_data_return: |
Called function "my_strlcpy(&serialize[location], "(null)", ((strlen("(null)") + 1UL < max_len - location) ? strlen("(null)") + 1UL : (max_len - location)))", and a possible return value is known to be less than zero. |
|
(47) Event overflow: |
The expression "location" is considered to have possibly overflowed. |
| Also see events: |
[return_overflow] |
670 location += my_strlcpy(&serialize[location],
671 "(null)",
672 QB_MIN(strlen("(null)") + 1,
673 max_len - location));
|
(48) Event if_fallthrough: |
Falling through to end of if statement. |
674 } else if (sformat_length) {
675 location += my_strlcpy(&serialize[location],
676 arg_string,
677 QB_MIN(sformat_length + 1,
678 (max_len - location)));
679 } else {
680 location += my_strlcpy(&serialize[location],
681 arg_string,
682 QB_MIN(strlen(arg_string) + 1,
683 max_len - location));
|
(49) Event if_end: |
End of if statement. |
684 }
685 location++;
|
(50) Event break: |
Breaking from switch. |
686 break;
687 }
688 case 'p':
689 {
690 ptrdiff_t arg_pointer = va_arg(ap, ptrdiff_t);
691 if (location + sizeof (ptrdiff_t) > max_len) {
692 return max_len;
693 }
694 memcpy(&serialize[location], &arg_pointer, sizeof(ptrdiff_t));
695 location += sizeof(ptrdiff_t);
696 break;
697 }
698 case '%':
699 if (location + 1 > max_len) {
700 return max_len;
701 }
702 serialize[location++] = '%';
703 sformat_length = 0;
704 sformat_precision = QB_FALSE;
705 break;
706
|
(37) Event switch_end: |
Reached end of switch. |
|
(51) Event switch_end: |
Reached end of switch. |
707 }
|
(38) Event loop: |
Jumping back to the beginning of the loop. |
|
(52) Event loop: |
Jumping back to the beginning of the loop. |
|
(56) Event loop_end: |
Reached end of loop. |
708 }
|
(57) Event return_overflow: |
"location", which might have overflowed, is returned from the function. |
| Also see events: |
[tainted_data_return][overflow] |
709 return (location);
710 }
711
712 #define MINI_FORMAT_STR_LEN 20
713
714 size_t
715 qb_vsnprintf_deserialize(char *string, size_t str_len, const char *buf)
716 {
717 char *p;
718 char *format;
719 char fmt[MINI_FORMAT_STR_LEN];
720 int fmt_pos;
721
722 uint32_t location = 0;
723 uint32_t data_pos = strlen(buf) + 1;
724 int type_long = QB_FALSE;
725 int type_longlong = QB_FALSE;
726 int len;
727
728 string[0] = '\0';
729 format = (char *)buf;
730 for (;;) {
731 type_long = QB_FALSE;
732 type_longlong = QB_FALSE;
733 p = strchrnul((const char *)format, '%');
734 if (*p == '\0') {
735 return my_strlcat(string, format, str_len) + 1;
736 }
737 /* copy from current to the next % */
738 len = p - format;
739 memcpy(&string[location], format, len);
740 location += len;
741 format = p;
742
743 /* start building up the format for snprintf */
744 fmt_pos = 0;
745 fmt[fmt_pos++] = *format;
746 format++;
747 reprocess:
748 switch (format[0]) {
749 case '#': /* alternate form conversion, ignore */
750 case '-': /* left adjust, ignore */
751 case ' ': /* a space, ignore */
752 case '+': /* a sign should be used, ignore */
753 case '\'': /* group in thousands, ignore */
754 case 'I': /* glibc-ism locale alternative, ignore */
755 case '.': /* precision, ignore */
756 case '0': /* field width, ignore */
757 case '1': /* field width, ignore */
758 case '2': /* field width, ignore */
759 case '3': /* field width, ignore */
760 case '4': /* field width, ignore */
761 case '5': /* field width, ignore */
762 case '6': /* field width, ignore */
763 case '7': /* field width, ignore */
764 case '8': /* field width, ignore */
765 case '9': /* field width, ignore */
766 fmt[fmt_pos++] = *format;
767 format++;
768 goto reprocess;
769
770 case '*': {
771 int arg_int;
772 memcpy(&arg_int, &buf[data_pos], sizeof(int));
773 data_pos += sizeof(int);
774 fmt_pos += snprintf(&fmt[fmt_pos],
775 MINI_FORMAT_STR_LEN - fmt_pos,
776 "%d", arg_int);
777 format++;
778 goto reprocess;
779 }
780 case 'l':
781 fmt[fmt_pos++] = *format;
782 format++;
783 type_long = QB_TRUE;
784 if (*format == 'l') {
785 type_long = QB_FALSE;
786 type_longlong = QB_TRUE;
787 }
788 goto reprocess;
789 case 'z':
790 fmt[fmt_pos++] = *format;
791 format++;
792 if (sizeof(size_t) == sizeof(long long)) {
793 type_long = QB_FALSE;
794 type_longlong = QB_TRUE;
795 } else {
796 type_longlong = QB_FALSE;
797 type_long = QB_TRUE;
798 }
799 goto reprocess;
800 case 't':
801 fmt[fmt_pos++] = *format;
802 format++;
803 if (sizeof(ptrdiff_t) == sizeof(long long)) {
804 type_longlong = QB_TRUE;
805 } else {
806 type_long = QB_TRUE;
807 }
808 goto reprocess;
809 case 'j':
810 fmt[fmt_pos++] = *format;
811 format++;
812 if (sizeof(intmax_t) == sizeof(long long)) {
813 type_longlong = QB_TRUE;
814 } else {
815 type_long = QB_TRUE;
816 }
817 goto reprocess;
818 case 'd': /* int argument */
819 case 'i': /* int argument */
820 case 'o': /* unsigned int argument */
821 case 'u':
822 case 'x':
823 case 'X':
824 if (type_long) {
825 long int arg_int;
826
827 fmt[fmt_pos++] = *format;
828 fmt[fmt_pos++] = '\0';
829 memcpy(&arg_int, &buf[data_pos], sizeof(long int));
830 location += snprintf(&string[location],
831 str_len - location,
832 fmt, arg_int);
833 data_pos += sizeof(long int);
834 format++;
835 break;
836 } else if (type_longlong) {
837 long long int arg_int;
838
839 fmt[fmt_pos++] = *format;
840 fmt[fmt_pos++] = '\0';
841 memcpy(&arg_int, &buf[data_pos], sizeof(long long int));
842 location += snprintf(&string[location],
843 str_len - location,
844 fmt, arg_int);
845 data_pos += sizeof(long long int);
846 format++;
847 break;
848 } else {
849 int arg_int;
850
851 fmt[fmt_pos++] = *format;
852 fmt[fmt_pos++] = '\0';
853 memcpy(&arg_int, &buf[data_pos], sizeof(int));
854 location += snprintf(&string[location],
855 str_len - location,
856 fmt, arg_int);
857 data_pos += sizeof(int);
858 format++;
859 break;
860 }
861 case 'e':
862 case 'E':
863 case 'f':
864 case 'F':
865 case 'g':
866 case 'G':
867 case 'a':
868 case 'A':
869 {
870 double arg_double;
871
872 fmt[fmt_pos++] = *format;
873 fmt[fmt_pos++] = '\0';
874 memcpy(&arg_double, &buf[data_pos], sizeof(double));
875 location += snprintf(&string[location],
876 str_len - location,
877 fmt, arg_double);
878 data_pos += sizeof(double);
879 format++;
880 break;
881 }
882 case 'c':
883 {
884 unsigned char *arg_char;
885
886 fmt[fmt_pos++] = *format;
887 fmt[fmt_pos++] = '\0';
888 arg_char = (unsigned char*)&buf[data_pos];
889 location += snprintf(&string[location],
890 str_len - location,
891 fmt, *arg_char);
892 data_pos += sizeof(unsigned char);
893 format++;
894 break;
895 }
896 case 's':
897 {
898 fmt[fmt_pos++] = *format;
899 fmt[fmt_pos++] = '\0';
900 len = snprintf(&string[location],
901 str_len - location,
902 fmt, &buf[data_pos]);
903 location += len;
904 /* don't use len as there might be a len modifier */
905 data_pos += strlen(&buf[data_pos]) + 1;
906 format++;
907 break;
908 }
909 case 'p':
910 {
911 ptrdiff_t pt;
912 memcpy(&pt, &buf[data_pos],
913 sizeof(ptrdiff_t));
914 fmt[fmt_pos++] = *format;
915 fmt[fmt_pos++] = '\0';
916 location += snprintf(&string[location],
917 str_len - location,
918 fmt, pt);
919 data_pos += sizeof(void*);
920 format++;
921 break;
922 }
923 case '%':
924 string[location++] = '%';
925 format++;
926 break;
927
928 }
929 }
930 return location;
931 }
932
933