Michael Savage wrote: > I found an integer overflow in syslogd which can be triggered by > compiling and running: > > #include <err.h> > #include <string.h> > #include <sys/types.h> > > int main( int argc, char ** argv ) { > const char * msg = "<999999999999> hello"; > return sendsyslog( msg, strlen( msg ) ); > } > > > The problematic code is a hand-rolled int parser in printline/printsys: > > pri = 0; > while (isdigit((unsigned char)*++p)) > pri = 10 * pri + (*p - '0'); > if (*p == '>') > ++p; > > > I've attached a patch which converts the loop to strtonum, but doesn't > exactly mirror the behaviour of the old code in funny cases, like > sendsyslog("<1no closing angle bracket").
Thanks for this. A few comments, some trivial: > Index: syslogd.c > =================================================================== > RCS file: /cvs/src/usr.sbin/syslogd/syslogd.c,v > retrieving revision 1.177 > diff -u -p -r1.177 syslogd.c > --- syslogd.c 20 Jul 2015 19:49:33 -0000 1.177 > +++ syslogd.c 11 Feb 2016 20:31:55 -0000 > @@ -1331,18 +1331,23 @@ usage(void) > void > printline(char *hname, char *msg) > { > - int pri; > - char *p, *q, line[MAXLINE + 4 + 1]; /* message, encoding, NUL */ > + int pri, possiblepri; A shorter name would be nice. pos_pri, or something like that. > + char *p, *q, line[MAXLINE + 4 + 1], *gt; /* message, encoding, NUL */ > + const char *errstr; > > /* test for special codes */ > pri = DEFUPRI; > p = msg; > if (*p == '<') { > - pri = 0; > - while (isdigit((unsigned char)*++p)) > - pri = 10 * pri + (*p - '0'); > - if (*p == '>') > - ++p; > + gt = strchr(p, '>'); > + if (gt) { if (gt != NULL) { > + *gt = '\0'; > + possiblepri = strtonum(p + 1, 0, INT_MAX, &errstr); > + if (!errstr) if (errstr == NULL) > + pri = possiblepri; > + *gt = '>'; > + p = gt + 1; > + } This block modifies msg. It seems that the existing code doesn't. Are you sure that won't have consequences elsewhere? > } > if (pri &~ (LOG_FACMASK|LOG_PRIMASK)) > pri = DEFUPRI; > @@ -1372,8 +1377,9 @@ printline(char *hname, char *msg) > void > printsys(char *msg) > { > - int c, pri, flags; > - char *lp, *p, *q, line[MAXLINE + 1]; > + int c, pri, flags, possiblepri; > + char *lp, *p, *q, line[MAXLINE + 1], *gt; > + const char *errstr; > > (void)snprintf(line, sizeof line, "%s: ", _PATH_UNIX); > lp = line + strlen(line); > @@ -1381,11 +1387,15 @@ printsys(char *msg) > flags = SYNC_FILE | ADDDATE; /* fsync file after write */ > pri = DEFSPRI; > if (*p == '<') { > - pri = 0; > - while (isdigit((unsigned char)*++p)) > - pri = 10 * pri + (*p - '0'); > - if (*p == '>') > - ++p; > + gt = strchr(p, '>'); > + if (gt) { > + *gt = '\0'; > + possiblepri = strtonum(p + 1, 0, INT_MAX, > &errstr); > + if (!errstr) > + pri = possiblepri; > + *gt = '>'; > + p = gt + 1; > + } Same as above. Also, maybe this should be broken out into a helper function? Maybe more robust logic would look something like this: size_t nlen; char nstr[11]; if (*p++ == '<') { nlen = strspn(p, "0123456789") if (nlen > 0 && nlen < 11 && p[nlen] == '>') { strlcpy(nstr, p, sizeof(nstr)); [strtonum logic here] } }