tmpl

Simple key-value templator
git clone https://git.sinitax.com/sinitax/tmpl
Log | Files | Refs | README | LICENSE | sfeed.txt

commit 839d8742c8fe85d6cffd60c4d39853779c5a1e16
parent f1e6447562f8521696af876a0f7c7fe3c0dcd51b
Author: Louis Burda <quent.burda@gmail.com>
Date:   Sun, 18 Jun 2023 17:34:06 +0200

Read input file-line wise and fix UAF

Diffstat:
Mmain.c | 147+++++++++++++++++++++++++++++++++++++------------------------------------------
Dtest | 10----------
2 files changed, 69 insertions(+), 88 deletions(-)

diff --git a/main.c b/main.c @@ -1,10 +1,10 @@ #define _XOPEN_SOURCE 500 -#include <err.h> #include <unistd.h> #include <fcntl.h> #include <stdio.h> #include <string.h> +#include <stdarg.h> #include <stdlib.h> enum { @@ -24,9 +24,33 @@ struct var { struct var *next; }; -struct var *vars, **vars_end; +static struct var *vars = NULL; +static struct var **vars_end = &vars; -const char * +static struct block stack[32] = { 0 }; +static int stack_top = 0; +static size_t lineno = 0; + +static void +die(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + fputs("tmpl: ", stderr); + vfprintf(stderr, fmt, ap); + if (*fmt && fmt[strlen(fmt)-1] == ':') { + fputc(' ', stderr); + perror(NULL); + } else { + fputc('\n', stderr); + } + va_end(ap); + + exit(1); +} + +static const char * getvar(const char *name) { struct var *var; @@ -39,18 +63,18 @@ getvar(const char *name) return getenv(name); } -void +static void assign(char *line) { char *sep; struct var *var; sep = strchr(line, '='); - if (!sep) err(1, "invalid assign: '%s'", line); + if (!sep) die("invalid assign '%s'", line); *sep = '\0'; var = malloc(sizeof(struct var)); - if (!var) err(1, "malloc"); + if (!var) die("malloc:"); var->name = line; var->value = sep + 1; var->next = NULL; @@ -59,20 +83,17 @@ assign(char *line) vars_end = &var->next; } -void +static void template(char *line) { - static struct block stack[32] = { 0 }; - static int stack_top = 0; - static size_t lineno = 0; const char *value; char *open, *close; - char *sep; + char *sep, *dup; lineno++; if (!strncmp(line, "#ifdef ", 7)) { - if (stack_top == 32) errx(1, "too much nesting"); + if (stack_top == 32) die("too deeply nested"); stack[stack_top].active = 0; stack[stack_top].type = IFDEF; if (!stack_top || stack[stack_top-1].active) { @@ -81,13 +102,13 @@ template(char *line) } stack_top++; } else if (!strncmp(line, "#ifeq ", 6)) { - if (stack_top == 32) errx(1, "too much nesting"); + if (stack_top == 32) die("too deeply nested"); stack[stack_top].active = 0; stack[stack_top].type = IFEQ; if (!stack_top || stack[stack_top-1].active) { value = getvar(line + 6); sep = strchr(line + 6, ' '); - if (!sep) err(1, "invalid #ifeq: %lu:%s", lineno, line); + if (!sep) die("invalid #ifeq\n%lu: %s", lineno, line); if (!strcmp(value, sep + 1)) { stack[stack_top].active = 1; } @@ -96,31 +117,35 @@ template(char *line) } else if (!strcmp(line, "#else")) { if (!stack_top || (stack[stack_top-1].type != IFDEF && stack[stack_top-1].type != IFEQ)) - err(1, "invalid #else: %lu:%s", lineno, line); + die("invalid #else\n%lu: %s", lineno, line); stack[stack_top-1].active ^= 1; stack[stack_top-1].type = ELSE; } else if (!strcmp(line, "#endif")) { - if (!stack_top) err(1, "invalid #endif: %lu:%s", lineno, line); + if (!stack_top) die("invalid #endif\n%lu: %s", lineno, line); stack_top--; } else if (!strncmp(line, "#define ", 8)) { if (stack_top && !stack[stack_top-1].active) return; sep = strchr(line + 8, ' '); - if (!sep) err(1, "invalid #default: %lu:%s", lineno, line); + if (!sep) die("invalid #default\n%lu: %s", lineno, line); *sep = '='; - assign(line + 8); + dup = strdup(line + 8); + if (!dup) die("strdup:"); + assign(dup); } else if (!strncmp(line, "#default ", 9)) { if (stack_top && !stack[stack_top-1].active) return; sep = strchr(line + 9, ' '); - if (!sep) err(1, "invalid #default: %lu:%s", lineno, line); + if (!sep) die("invalid #default\n%lu: %s", lineno, line); *sep = '\0'; value = getvar(line + 9); if (!value) { *sep = '='; - assign(line + 9); + dup = strdup(line + 9); + if (!dup) die("strdup:"); + assign(dup); } } else if (!strncmp(line, "#-- ", 4)) { return; /* comment */ @@ -157,76 +182,44 @@ template(char *line) } } -void -perline(char *contents, int nonempty, void (*process)(char *)) -{ - char *line, *sep, *end; - - line = contents; - do { - sep = strchr(line, '\n'); - end = sep ? sep : line + strlen(line); - *end = '\0'; - if (!nonempty || end > line + 1) - process(line); - line = end + 1; - } while (sep); -} - -char * -readall(const char *path) +static void +perline(const char *path, int nonempty, void (*process)(char *)) { - char buf[BUFSIZ]; - char *contents; - ssize_t ret; - size_t len, cap; - int fd; - - fd = open(path, O_RDONLY); - if (fd < 0) err(1, "open '%s'", path); - - cap = BUFSIZ + 1; - contents = malloc(cap); - if (!contents) err(1, "malloc"); - - len = 0; - while ((ret = read(fd, buf, BUFSIZ)) > 0) { - if (len + (size_t) ret >= cap) { - cap *= 2; - contents = realloc(contents, cap); - if (!contents) err(1, "realloc"); + char linebuf[4096]; + size_t len; + FILE *f; + + f = fopen(path, "r"); + if (!f) die("fopen '%s':", path); + while (fgets(linebuf, 4096, f)) { + len = strlen(linebuf); + if (len && linebuf[len - 1] == '\n') { + linebuf[len - 1] = '\0'; + } else if (len && !feof(f)) { + die("fgets '%s': line too long", path); } - memcpy(contents + len, buf, (size_t) ret); - len += (size_t) ret; + if (len || !nonempty) + process(linebuf); } - contents[len] = '\0'; - - return contents; + fclose(f); } int -main(int argc, const char **argv) +main(int argc, char **argv) { - const char **arg; const char *path; - char *str, *contents; - - vars = NULL; - vars_end = &vars; + char **arg; if (!argc) return 1; for (arg = argv + 1; *arg; arg++) { if (!strcmp(*arg, "-h") || !strcmp(*arg, "--help")) { - fprintf(stderr, "Usage: tmpl [-c CONFIG].. [-D NAME=VALUE].. FILE..\n"); + fprintf(stderr, "Usage: tmpl [-c CONFIG].. " + "[-D NAME=VALUE].. FILE..\n"); return 1; } else if (!strcmp(*arg, "-D") || !strcmp(*arg, "--define")) { - str = strdup(*++arg); - if (!str) err(1, "strdup"); - assign(str); + assign(*++arg); } else if (!strcmp(*arg, "-c") || !strcmp(*arg, "--config")) { - contents = readall(*++arg); - perline(contents, 1, assign); - free(contents); + perline(*++arg, 1, assign); } else { break; } @@ -234,8 +227,6 @@ main(int argc, const char **argv) for (; *arg; arg++) { path = !strcmp(*arg, "-") ? "/dev/stdin" : *arg; - contents = readall(path); - perline(contents, 0, template); - free(contents); + perline(path, 0, template); } } diff --git a/test b/test @@ -1,10 +0,0 @@ -#default PREFIX /usr/local -#default BINDIR /bin - -#ifdef DEBUG -yes debug -#else -no debug -#endif - -install -m755 "#{TARGET}" -t "#{DESTDIR}#{PREFIX}#{BINDIR}"