SECURITY: fix integer overflows related to file descriptor parsing

bug initially found by Pawel Wylecial (LP#1440685)
additional bug found and suggested fix by enh (elliott hughes)

This commit also renames struct ioword.flag to ioflag to disambiguate
it from other members named “flag”, changes it to an unsigned type,
and packs ioflag and unit into shorts each, to make the struct smaller
(aligned even: 16 bytes on 32-bit systems) and reviews some of the
code involved in fd handling, though there wasn’t much to be found.
This commit is contained in:
tg 2015-04-11 22:03:32 +00:00
parent 3c5c5d02fd
commit e1cda74d04
7 changed files with 74 additions and 67 deletions

4
eval.c
View File

@ -23,7 +23,7 @@
#include "sh.h"
__RCSID("$MirOS: src/bin/mksh/eval.c,v 1.166 2015/02/20 07:14:29 tg Exp $");
__RCSID("$MirOS: src/bin/mksh/eval.c,v 1.167 2015/04/11 22:03:29 tg Exp $");
/*
* string expansion
@ -1353,7 +1353,7 @@ comsub(Expand *xp, const char *cp, int fn MKSH_A_UNUSED)
struct ioword *io = *t->ioact;
char *name;
if ((io->flag & IOTYPE) != IOREAD)
if ((io->ioflag & IOTYPE) != IOREAD)
errorf("%s: %s", "funny $() command",
snptreef(NULL, 32, "%R", io));
shf = shf_open(name = evalstr(io->name, DOTILDE), O_RDONLY, 0,

22
exec.c
View File

@ -23,7 +23,7 @@
#include "sh.h"
__RCSID("$MirOS: src/bin/mksh/exec.c,v 1.147 2015/03/20 23:37:54 tg Exp $");
__RCSID("$MirOS: src/bin/mksh/exec.c,v 1.148 2015/04/11 22:03:29 tg Exp $");
#ifndef MKSH_DEFAULT_EXECSHELL
#define MKSH_DEFAULT_EXECSHELL "/bin/sh"
@ -92,7 +92,7 @@ execute(struct op * volatile t,
t->ioact != NULL && t->ioact[0] != NULL &&
t->ioact[1] == NULL &&
/* of type "here document" (or "here string") */
(t->ioact[0]->flag & IOTYPE) == IOHERE &&
(t->ioact[0]->ioflag & IOTYPE) == IOHERE &&
/* the variable assignment begins with a valid varname */
(ccp = skip_wdvarname(t->vars[0], true)) != t->vars[0] &&
/* and has no right-hand side (i.e. "varname=") */
@ -1319,7 +1319,7 @@ iosetup(struct ioword *iop, struct tbl *tp)
{
int u = -1;
char *cp = iop->name;
int iotype = iop->flag & IOTYPE;
int iotype = iop->ioflag & IOTYPE;
bool do_open = true, do_close = false;
int flags = 0;
struct ioword iotmp;
@ -1331,7 +1331,7 @@ iosetup(struct ioword *iop, struct tbl *tp)
/* Used for tracing and error messages to print expanded cp */
iotmp = *iop;
iotmp.name = (iotype == IOHERE) ? NULL : cp;
iotmp.flag |= IONAMEXP;
iotmp.ioflag |= IONAMEXP;
if (Flag(FXTRACE)) {
change_xtrace(2, false);
@ -1354,7 +1354,7 @@ iosetup(struct ioword *iop, struct tbl *tp)
* The stat() is here to allow redirections to
* things like /dev/null without error.
*/
if (Flag(FNOCLOBBER) && !(iop->flag & IOCLOB) &&
if (Flag(FNOCLOBBER) && !(iop->ioflag & IOCLOB) &&
(stat(cp, &statb) < 0 || S_ISREG(statb.st_mode)))
flags |= O_EXCL;
break;
@ -1379,7 +1379,7 @@ iosetup(struct ioword *iop, struct tbl *tp)
u = 1009;
do_close = true;
} else if ((u = check_fd(cp,
X_OK | ((iop->flag & IORDUP) ? R_OK : W_OK),
X_OK | ((iop->ioflag & IORDUP) ? R_OK : W_OK),
&emsg)) < 0) {
char *sp;
@ -1388,7 +1388,7 @@ iosetup(struct ioword *iop, struct tbl *tp)
afree(sp, ATEMP);
return (-1);
}
if (u == iop->unit)
if (u == (int)iop->unit)
/* "dup from" == "dup to" */
return (0);
break;
@ -1416,7 +1416,7 @@ iosetup(struct ioword *iop, struct tbl *tp)
/* Do not save if it has already been redirected (i.e. "cat >x >y"). */
if (e->savefd[iop->unit] == 0) {
/* If these are the same, it means unit was previously closed */
if (u == iop->unit)
if (u == (int)iop->unit)
e->savefd[iop->unit] = -1;
else
/*
@ -1431,7 +1431,7 @@ iosetup(struct ioword *iop, struct tbl *tp)
if (do_close)
close(iop->unit);
else if (u != iop->unit) {
else if (u != (int)iop->unit) {
if (ksh_dup2(u, iop->unit, true) < 0) {
int eno;
char *sp;
@ -1453,7 +1453,7 @@ iosetup(struct ioword *iop, struct tbl *tp)
* causes the shell to close its copies
*/
else if (tp && tp->type == CSHELL && tp->val.f == c_exec) {
if (iop->flag & IORDUP)
if (iop->ioflag & IORDUP)
/* possible exec <&p */
coproc_read_close(u);
else
@ -1522,7 +1522,7 @@ herein(struct ioword *iop, char **resbuf)
}
/* lexer substitution flags */
i = (iop->flag & IOEVAL) ? (ONEWORD | HEREDOC) : 0;
i = (iop->ioflag & IOEVAL) ? (ONEWORD | HEREDOC) : 0;
/* skip all the fd setup if we just want the value */
if (resbuf != NULL)

39
lex.c
View File

@ -23,7 +23,7 @@
#include "sh.h"
__RCSID("$MirOS: src/bin/mksh/lex.c,v 1.198 2015/03/20 23:37:39 tg Exp $");
__RCSID("$MirOS: src/bin/mksh/lex.c,v 1.199 2015/04/11 22:03:30 tg Exp $");
/*
* states while lexing word
@ -921,42 +921,41 @@ yylex(int cf)
if (!ksh_isdigit(dp[c2 + 1]))
goto no_iop;
iop->unit = (iop->unit * 10) + dp[c2 + 1] - '0';
if (iop->unit >= FDBASE)
goto no_iop;
}
if (iop->unit >= FDBASE)
goto no_iop;
if (c == '&') {
if ((c2 = getsc()) != '>') {
ungetsc(c2);
goto no_iop;
}
c = c2;
iop->flag = IOBASH;
iop->ioflag = IOBASH;
} else
iop->flag = 0;
iop->ioflag = 0;
c2 = getsc();
/* <<, >>, <> are ok, >< is not */
if (c == c2 || (c == '<' && c2 == '>')) {
iop->flag |= c == c2 ?
iop->ioflag |= c == c2 ?
(c == '>' ? IOCAT : IOHERE) : IORDWR;
if (iop->flag == IOHERE) {
if (iop->ioflag == IOHERE) {
if ((c2 = getsc()) == '-') {
iop->flag |= IOSKIP;
iop->ioflag |= IOSKIP;
c2 = getsc();
} else if (c2 == '<')
iop->flag |= IOHERESTR;
iop->ioflag |= IOHERESTR;
ungetsc(c2);
if (c2 == '\n')
iop->flag |= IONDELIM;
iop->ioflag |= IONDELIM;
}
} else if (c2 == '&')
iop->flag |= IODUP | (c == '<' ? IORDUP : 0);
iop->ioflag |= IODUP | (c == '<' ? IORDUP : 0);
else {
iop->flag |= c == '>' ? IOWRITE : IOREAD;
iop->ioflag |= c == '>' ? IOWRITE : IOREAD;
if (c == '>' && c2 == '|')
iop->flag |= IOCLOB;
iop->ioflag |= IOCLOB;
else
ungetsc(c2);
}
@ -1124,7 +1123,7 @@ gethere(bool iseof)
struct ioword **p;
for (p = heres; p < herep; p++)
if (iseof && !((*p)->flag & IOHERESTR))
if (iseof && !((*p)->ioflag & IOHERESTR))
/* only here strings at EOF */
return;
else
@ -1145,7 +1144,7 @@ readhere(struct ioword *iop)
char *xp;
int xpos;
if (iop->flag & IOHERESTR) {
if (iop->ioflag & IOHERESTR) {
/* process the here string */
iop->heredoc = xp = evalstr(iop->delim, DOBLANK);
xpos = strlen(xp) - 1;
@ -1154,9 +1153,9 @@ readhere(struct ioword *iop)
return;
}
eof = iop->flag & IONDELIM ? "<<" : evalstr(iop->delim, 0);
eof = iop->ioflag & IONDELIM ? "<<" : evalstr(iop->delim, 0);
if (!(iop->flag & IOEVAL))
if (!(iop->ioflag & IOEVAL))
ignore_backslash_newline++;
Xinit(xs, xp, 256, ATEMP);
@ -1165,7 +1164,7 @@ readhere(struct ioword *iop)
/* beginning of line */
eofp = eof;
xpos = Xsavepos(xs, xp);
if (iop->flag & IOSKIP) {
if (iop->ioflag & IOSKIP) {
/* skip over leading tabs */
while ((c = getsc()) == '\t')
; /* nothing */
@ -1227,7 +1226,7 @@ readhere(struct ioword *iop)
Xput(xs, xp, '\0');
iop->heredoc = Xclose(xs, xp);
if (!(iop->flag & IOEVAL))
if (!(iop->ioflag & IOEVAL))
ignore_backslash_newline--;
}

15
main.c
View File

@ -34,7 +34,7 @@
#include <locale.h>
#endif
__RCSID("$MirOS: src/bin/mksh/main.c,v 1.290 2015/03/14 05:23:15 tg Exp $");
__RCSID("$MirOS: src/bin/mksh/main.c,v 1.291 2015/04/11 22:03:30 tg Exp $");
extern char **environ;
@ -1453,13 +1453,20 @@ closepipe(int *pv)
int
check_fd(const char *name, int mode, const char **emsgp)
{
int fd, fl;
int fd = 0, fl;
if (name[0] == 'p' && !name[1])
return (coproc_getfd(mode, emsgp));
for (fd = 0; ksh_isdigit(*name); ++name)
while (ksh_isdigit(*name)) {
fd = (fd * 10) + *name - '0';
if (*name || fd >= FDBASE) {
if (fd >= FDBASE) {
if (emsgp)
*emsgp = "file descriptor too large";
return (-1);
}
++name;
}
if (*name) {
if (emsgp)
*emsgp = "illegal file descriptor name";
return (-1);

12
sh.h
View File

@ -169,7 +169,7 @@
#endif
#ifdef EXTERN
__RCSID("$MirOS: src/bin/mksh/sh.h,v 1.721 2015/03/20 23:37:55 tg Exp $");
__RCSID("$MirOS: src/bin/mksh/sh.h,v 1.722 2015/04/11 22:03:31 tg Exp $");
#endif
#define MKSH_VERSION "R50 2015/03/20"
@ -1341,11 +1341,11 @@ struct op {
* IO redirection
*/
struct ioword {
int unit; /* unit affected */
int flag; /* action (below) */
char *name; /* file name (unused if heredoc) */
char *delim; /* delimiter for <<,<<- */
char *heredoc; /* content of heredoc */
char *name; /* filename (unused if heredoc) */
char *delim; /* delimiter for <<, <<- */
char *heredoc; /* content of heredoc */
unsigned short ioflag; /* action (below) */
short unit; /* unit (fd) affected */
};
/* ioword.flag - type of redirection */

19
syn.c
View File

@ -23,7 +23,7 @@
#include "sh.h"
__RCSID("$MirOS: src/bin/mksh/syn.c,v 1.99 2015/03/14 05:23:18 tg Exp $");
__RCSID("$MirOS: src/bin/mksh/syn.c,v 1.100 2015/04/11 22:03:32 tg Exp $");
struct nesting_state {
int start_token; /* token than began nesting (eg, FOR) */
@ -189,16 +189,16 @@ synio(int cf)
return (NULL);
ACCEPT;
iop = yylval.iop;
if (iop->flag & IONDELIM)
if (iop->ioflag & IONDELIM)
goto gotnulldelim;
ishere = (iop->flag & IOTYPE) == IOHERE;
ishere = (iop->ioflag & IOTYPE) == IOHERE;
musthave(LWORD, ishere ? HEREDELIM : 0);
if (ishere) {
iop->delim = yylval.cp;
if (*ident != 0) {
/* unquoted */
gotnulldelim:
iop->flag |= IOEVAL;
iop->ioflag |= IOEVAL;
}
if (herep > &heres[HERES - 1])
yyerror("too many %ss\n", "<<");
@ -206,7 +206,7 @@ synio(int cf)
} else
iop->name = yylval.cp;
if (iop->flag & IOBASH) {
if (iop->ioflag & IOBASH) {
char *cp;
nextiop = alloc(sizeof(*iop), ATEMP);
@ -220,9 +220,9 @@ synio(int cf)
*cp++ = '0' + (iop->unit % 10);
*cp = EOS;
iop->flag &= ~IOBASH;
iop->ioflag &= ~IOBASH;
nextiop->unit = 2;
nextiop->flag = IODUP;
nextiop->ioflag = IODUP;
nextiop->delim = NULL;
nextiop->heredoc = NULL;
}
@ -996,9 +996,10 @@ dbtestp_isa(Test_env *te, Test_meta meta)
ret = c == /*(*/ ')' ? TO_NONNULL : TO_NONOP;
else if (meta == TM_UNOP || meta == TM_BINOP) {
if (meta == TM_BINOP && c == REDIR &&
(yylval.iop->flag == IOREAD || yylval.iop->flag == IOWRITE)) {
(yylval.iop->ioflag == IOREAD ||
yylval.iop->ioflag == IOWRITE)) {
ret = TO_NONNULL;
save = wdcopy(yylval.iop->flag == IOREAD ?
save = wdcopy(yylval.iop->ioflag == IOREAD ?
db_lthan : db_gthan, ATEMP);
} else if (uqword && (ret = test_isop(meta, ident)))
save = yylval.cp;

30
tree.c
View File

@ -2,7 +2,7 @@
/*-
* Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
* 2011, 2012, 2013
* 2011, 2012, 2013, 2015
* Thorsten Glaser <tg@mirbsd.org>
*
* Provided that these terms and disclaimer and all copyright notices
@ -23,7 +23,7 @@
#include "sh.h"
__RCSID("$MirOS: src/bin/mksh/tree.c,v 1.72 2013/09/24 20:19:45 tg Exp $");
__RCSID("$MirOS: src/bin/mksh/tree.c,v 1.73 2015/04/11 22:03:32 tg Exp $");
#define INDENT 8
@ -69,7 +69,7 @@ ptree(struct op *t, int indent, struct shf *shf)
t->ioact != NULL && t->ioact[0] != NULL &&
t->ioact[1] == NULL &&
/* of type "here document" (or "here string") */
(t->ioact[0]->flag & IOTYPE) == IOHERE) {
(t->ioact[0]->ioflag & IOTYPE) == IOHERE) {
fptreef(shf, indent, "%S", t->vars[0]);
break;
}
@ -221,12 +221,12 @@ ptree(struct op *t, int indent, struct shf *shf)
struct ioword *iop = *ioact++;
/* heredoc is NULL when tracing (set -x) */
if ((iop->flag & (IOTYPE | IOHERESTR)) == IOHERE &&
if ((iop->ioflag & (IOTYPE | IOHERESTR)) == IOHERE &&
iop->heredoc) {
shf_putc('\n', shf);
shf_puts(iop->heredoc, shf);
fptreef(shf, indent, "%s",
iop->flag & IONDELIM ? "<<" :
iop->ioflag & IONDELIM ? "<<" :
evalstr(iop->delim, 0));
need_nl = true;
}
@ -246,16 +246,16 @@ ptree(struct op *t, int indent, struct shf *shf)
static void
pioact(struct shf *shf, struct ioword *iop)
{
int flag = iop->flag;
int type = flag & IOTYPE;
int expected;
unsigned short flag = iop->ioflag;
unsigned short type = flag & IOTYPE;
short expected;
expected = (type == IOREAD || type == IORDWR || type == IOHERE) ? 0 :
(type == IOCAT || type == IOWRITE) ? 1 :
(type == IODUP && (iop->unit == !(flag & IORDUP))) ? iop->unit :
iop->unit + 1;
if (iop->unit != expected)
shf_fprintf(shf, "%d", iop->unit);
shf_fprintf(shf, "%d", (int)iop->unit);
switch (type) {
case IOREAD:
@ -285,10 +285,10 @@ pioact(struct shf *shf, struct ioword *iop)
if (type == IOHERE) {
if (iop->delim)
wdvarput(shf, iop->delim, 0, WDS_TPUTS);
if (iop->flag & IOHERESTR)
if (flag & IOHERESTR)
shf_putc(' ', shf);
} else if (iop->name) {
if (iop->flag & IONAMEXP)
if (flag & IONAMEXP)
print_value_quoted(shf, iop->name);
else
wdvarput(shf, iop->name, 0, WDS_TPUTS);
@ -910,9 +910,9 @@ dumpioact(struct shf *shf, struct op *t)
shf_puts("{IOACT", shf);
while ((iop = *ioact++) != NULL) {
int type = iop->flag & IOTYPE;
unsigned short type = iop->ioflag & IOTYPE;
#define DT(x) case x: shf_puts(#x, shf); break;
#define DB(x) if (iop->flag & x) shf_puts("|" #x, shf);
#define DB(x) if (iop->ioflag & x) shf_puts("|" #x, shf);
shf_putc(';', shf);
switch (type) {
@ -933,14 +933,14 @@ dumpioact(struct shf *shf, struct op *t)
DB(IOBASH)
DB(IOHERESTR)
DB(IONDELIM)
shf_fprintf(shf, ",unit=%d", iop->unit);
shf_fprintf(shf, ",unit=%d", (int)iop->unit);
if (iop->delim) {
shf_puts(",delim<", shf);
dumpwdvar(shf, iop->delim);
shf_putc('>', shf);
}
if (iop->name) {
if (iop->flag & IONAMEXP) {
if (iop->ioflag & IONAMEXP) {
shf_puts(",name=", shf);
print_value_quoted(shf, iop->name);
} else {