From 4059e105a283fa280d150154fa1b0e5c6daa7dfd Mon Sep 17 00:00:00 2001 From: tg Date: Thu, 31 Dec 2015 21:03:47 +0000 Subject: [PATCH] fstat(2) after open(2) for set -C case when initial stat(2) was !S_ISREG to fix race condition as suggested by jilles --- check.t | 6 +++--- exec.c | 42 ++++++++++++++++++++++++++++++++---------- sh.h | 4 ++-- 3 files changed, 37 insertions(+), 15 deletions(-) diff --git a/check.t b/check.t index d5745f5..0189cf6 100644 --- a/check.t +++ b/check.t @@ -1,4 +1,4 @@ -# $MirOS: src/bin/mksh/check.t,v 1.716 2015/12/12 23:31:15 tg Exp $ +# $MirOS: src/bin/mksh/check.t,v 1.717 2015/12/31 21:03:44 tg Exp $ # -*- mode: sh -*- #- # Copyright © 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, @@ -30,7 +30,7 @@ # (2013/12/02 20:39:44) http://openbsd.cs.toronto.edu/cgi-bin/cvsweb/src/regress/bin/ksh/?sortby=date expected-stdout: - @(#)MIRBSD KSH R52 2015/12/12 + @(#)MIRBSD KSH R52 2015/12/31 description: Check version of shell. stdin: @@ -39,7 +39,7 @@ name: KSH_VERSION category: shell:legacy-no --- expected-stdout: - @(#)LEGACY KSH R52 2015/12/12 + @(#)LEGACY KSH R52 2015/12/31 description: Check version of legacy shell. stdin: diff --git a/exec.c b/exec.c index 78d50f9..9361c19 100644 --- a/exec.c +++ b/exec.c @@ -23,7 +23,7 @@ #include "sh.h" -__RCSID("$MirOS: src/bin/mksh/exec.c,v 1.169 2015/12/31 12:58:43 tg Exp $"); +__RCSID("$MirOS: src/bin/mksh/exec.c,v 1.170 2015/12/31 21:03:47 tg Exp $"); #ifndef MKSH_DEFAULT_EXECSHELL #define MKSH_DEFAULT_EXECSHELL MKSH_UNIXROOT "/bin/sh" @@ -1363,7 +1363,7 @@ iosetup(struct ioword *iop, struct tbl *tp) int u = -1; char *cp = iop->ioname; int iotype = iop->ioflag & IOTYPE; - bool do_open = true, do_close = false; + bool do_open = true, do_close = false, do_fstat = false; int flags = 0; struct ioword iotmp; struct stat statb; @@ -1392,14 +1392,27 @@ iosetup(struct ioword *iop, struct tbl *tp) break; case IOWRITE: - flags = O_WRONLY | O_CREAT | O_TRUNC; - /* - * The stat() is here to allow redirections to - * things like /dev/null without error. - */ - if (Flag(FNOCLOBBER) && !(iop->ioflag & IOCLOB) && - (stat(cp, &statb) < 0 || S_ISREG(statb.st_mode))) - flags |= O_EXCL; + if (Flag(FNOCLOBBER) && !(iop->ioflag & IOCLOB)) { + /* >file under set -C */ + if (stat(cp, &statb)) { + /* nonexistent file */ + flags = O_WRONLY | O_CREAT | O_EXCL; + } else if (S_ISREG(statb.st_mode)) { + /* regular file, refuse clobbering */ + goto clobber_refused; + } else { + /* + * allow redirections to things + * like /dev/null without error + */ + flags = O_WRONLY; + /* but check again after opening */ + do_fstat = true; + } + } else { + /* >|file or set +C */ + flags = O_WRONLY | O_CREAT | O_TRUNC; + } break; case IORDWR: @@ -1444,6 +1457,15 @@ iosetup(struct ioword *iop, struct tbl *tp) return (-1); } u = binopen3(cp, flags, 0666); + if (do_fstat && u >= 0) { + /* prevent race conditions */ + if (fstat(u, &statb) || S_ISREG(statb.st_mode)) { + close(u); + clobber_refused: + u = -1; + errno = EEXIST; + } + } } if (u < 0) { /* herein() may already have printed message */ diff --git a/sh.h b/sh.h index 6cdd6c0..39530aa 100644 --- a/sh.h +++ b/sh.h @@ -175,9 +175,9 @@ #endif #ifdef EXTERN -__RCSID("$MirOS: src/bin/mksh/sh.h,v 1.751 2015/12/12 22:25:15 tg Exp $"); +__RCSID("$MirOS: src/bin/mksh/sh.h,v 1.752 2015/12/31 21:03:47 tg Exp $"); #endif -#define MKSH_VERSION "R52 2015/12/12" +#define MKSH_VERSION "R52 2015/12/31" /* arithmetic types: C implementation */ #if !HAVE_CAN_INTTYPES