Permalink
Browse files

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.
  • Loading branch information...
1 parent 3c5c5d0 commit e1cda74d044c0a2607a3686ce5f9924e6a6c0811 @mirabilos mirabilos committed Apr 11, 2015
Showing with 74 additions and 67 deletions.
  1. +2 −2 eval.c
  2. +11 −11 exec.c
  3. +19 −20 lex.c
  4. +11 −4 main.c
  5. +6 −6 sh.h
  6. +10 −9 syn.c
  7. +15 −15 tree.c
View
@@ -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,
View
@@ -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)
View
@@ -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--;
}
View
@@ -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);
View
@@ -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 */
View
@@ -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,24 +189,24 @@ 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", "<<");
*herep++ = iop;
} 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;
Oops, something went wrong.

0 comments on commit e1cda74

Please sign in to comment.