Re: [PATCH 1/2] fts: fix cloexec races

2017-08-14 Thread Bruno Haible
Paul Eggert wrote:
> Despite that disadvantage, it would be a win for users or Gnulib, who 
> could stop worrying about the possibility that O_CLOEXEC == 0. I 
> installed the attached patch to try to implement this. I don't use 
> MS-Windows, though, and may well have missed something.

Looks quite nice. I've verified the value of O_CLOEXEC fits:

1) All systems appear to "book" the O_* bit values, starting with the
   small ones.

Linux   max is 0x40
Hurdmax is 0x40
Mac OS X 10.7   max is 0x20
FreeBSD 6.4 max is 0x1
NetBSD 5.0  max is 0x8
OpenBSD 3.8 max is 0x8000
MirBSD  max is 0x8000
Minix 3.1.8 max is 0x1000
AIX 7.1 collision with _FKERNEL - probably harmless
HP-UX 11.31 max is 0x100
IRIX 6.5max is 0x4
OSF/1 5.1   max is 0x4
Solaris 11 2011-11  max is 0x200
Cygwin  max is 0x10
mingw   max is 0x4
MSVC 14 max is 0x4
Interix 3.5 max is 0x2000
BeOSmax is 0x8000

2) The collision with _FKERNEL on AIX is probably harmless, since this flag
probably only exists in kernel code.

Here's an update of the documentation:


2017-08-14  Bruno Haible  

open, openat: Update doc about O_CLOEXEC.
* doc/posix-functions/open.texi: More concrete list of platforms.
* doc/posix-functions/openat.texi: Likewise.
 
diff --git a/doc/posix-functions/open.texi b/doc/posix-functions/open.texi
index f1565c3..7e9df26 100644
--- a/doc/posix-functions/open.texi
+++ b/doc/posix-functions/open.texi
@@ -10,7 +10,7 @@ Portability problems fixed by the Gnulib module open:
 @itemize
 @item
 Some platforms do not support @code{O_CLOEXEC}:
-Solaris 10, probably many others.
+Mac OS X 10.5, FreeBSD 6.0, NetBSD 5.0, OpenBSD 3.8, Minix 3.1.8, AIX 5.1, 
HP-UX 11, IRIX 6.5, OSF/1 5.1, Solaris 10, Cygwin, mingw, MSVC 14, Interix 3.5.
 @item
 On platforms where @code{off_t} is a 32-bit type, @code{open} may not work
 correctly with files larger than 2 GB.  (Cf. @code{AC_SYS_LARGEFILE}.)
diff --git a/doc/posix-functions/openat.texi b/doc/posix-functions/openat.texi
index 9f7632b..4a20636 100644
--- a/doc/posix-functions/openat.texi
+++ b/doc/posix-functions/openat.texi
@@ -15,7 +15,7 @@ AIX 5.1, HP-UX 11, IRIX 6.5, OSF/1 5.1, Cygwin 1.5.x, mingw, 
MSVC 14, Interix 3.
 But the replacement function is not safe to be used in libraries and is not 
multithread-safe.
 @item
 Some platforms do not support @code{O_CLOEXEC}:
-Solaris 10.
+AIX 7.1, Solaris 10.
 @item
 On platforms where @code{off_t} is a 32-bit type, @code{open} may not work
 correctly with files larger than 2 GB.  (Cf. @code{AC_SYS_LARGEFILE}.)




Re: [PATCH 1/2] fts: fix cloexec races

2017-08-14 Thread Paul Eggert
Thanks for pointing out the problem; I guess I was subconsciously hoping 
that platforms lacking O_CLOEXEC were no longer a practical issue. But 
that was silly.


On 08/14/2017 10:18 AM, Bruno Haible wrote:

Alternatively, define O_CLOEXEC to a non-zero value on those platforms
that don't support it, and extend gnulib's open() and openat() wrappers
to support O_CLOEXEC. But this is more hairy because the platforms could,
at any time, introduce other O_* flags that happen to collide wit the value
we happen to pick for O_CLOEXEC.


Despite that disadvantage, it would be a win for users or Gnulib, who 
could stop worrying about the possibility that O_CLOEXEC == 0. I 
installed the attached patch to try to implement this. I don't use 
MS-Windows, though, and may well have missed something.


>From 9830b8cbec084211d3169bbb074317053cb1088e Mon Sep 17 00:00:00 2001
From: Paul Eggert 
Date: Mon, 14 Aug 2017 13:04:46 -0700
Subject: [PATCH] open: support O_CLOEXEC

* NEWS, doc/posix-functions/open.texi:
* doc/posix-functions/openat.texi: Document this.
* lib/fcntl.in.h (O_CLOEXEC): Default to a nonzero value.
(GNULIB_defined_O_CLOEXEC): New symbol.
* lib/open.c: Include cloexec.h.
(open): Support O_CLOEXEC.
* lib/openat.c: Include cloexec.h.
(rpl_openat): Support O_CLOEXEC.
* lib/popen-safer.c: Do not include cloexec.h.
(open_noinherit): Remove.
(popen_safer): Use O_CLOEXEC instead of set_cloexec_flag.
* lib/save-cwd.c: Do not include cloexec.h.
(save_cwd): Use O_CLOEXEC instead of set_cloexec_flag.
* m4/open-cloexec.m4: New file.
* m4/open.m4 (gl_FUNC_OPEN): Require gl_PREPROC_O_CLOEXEC.
Replace 'open' if O_CLOEXEC is not present.
* m4/openat.m4 (gl_FUNC_OPENAT): Require gl_PREPROC_O_CLOEXEC.
Replace 'openat' if O_CLOEXEC is not present.
* modules/freopen (Depends-on): Depend on 'open' if replacing freopen.
* modules/open (Files): Add m4/open-cloexec.m4.
(Depends-on): Depend on cloexec if replacing 'open'.
* modules/openat (Files): Add m4/open-cloexec.m4.
(Depends-on): Depend on cloexec if replacing openat.
* modules/popen-safer (Depends-on): Remove cloexec.
* modules/save-cwd (Depends-on): Remove cloexec, and add
fd-safer-flag and 'open'.
---
 ChangeLog   | 31 +++
 NEWS|  4 
 doc/posix-functions/open.texi   |  6 ++
 doc/posix-functions/openat.texi |  6 ++
 lib/fcntl.in.h  |  5 -
 lib/open.c  | 29 -
 lib/openat.c| 32 ++--
 lib/popen-safer.c   | 35 +--
 lib/save-cwd.c  |  6 ++
 m4/open-cloexec.m4  | 21 +
 m4/open.m4  |  6 +-
 m4/openat.m4|  8 +---
 modules/freopen |  1 +
 modules/open|  2 ++
 modules/openat  |  2 ++
 modules/popen-safer |  1 -
 modules/save-cwd|  5 +++--
 17 files changed, 151 insertions(+), 49 deletions(-)
 create mode 100644 m4/open-cloexec.m4

diff --git a/ChangeLog b/ChangeLog
index a07e17251..7f1a5c2e9 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,34 @@
+2017-08-14  Paul Eggert  
+
+
+	open: support O_CLOEXEC
+	* NEWS, doc/posix-functions/open.texi:
+	* doc/posix-functions/openat.texi: Document this.
+	* lib/fcntl.in.h (O_CLOEXEC): Default to a nonzero value.
+	(GNULIB_defined_O_CLOEXEC): New symbol.
+	* lib/open.c: Include cloexec.h.
+	(open): Support O_CLOEXEC.
+	* lib/openat.c: Include cloexec.h.
+	(rpl_openat): Support O_CLOEXEC.
+	* lib/popen-safer.c: Do not include cloexec.h.
+	(open_noinherit): Remove.
+	(popen_safer): Use O_CLOEXEC instead of set_cloexec_flag.
+	* lib/save-cwd.c: Do not include cloexec.h.
+	(save_cwd): Use O_CLOEXEC instead of set_cloexec_flag.
+	* m4/open-cloexec.m4: New file.
+	* m4/open.m4 (gl_FUNC_OPEN): Require gl_PREPROC_O_CLOEXEC.
+	Replace 'open' if O_CLOEXEC is not present.
+	* m4/openat.m4 (gl_FUNC_OPENAT): Require gl_PREPROC_O_CLOEXEC.
+	Replace 'openat' if O_CLOEXEC is not present.
+	* modules/freopen (Depends-on): Depend on 'open' if replacing freopen.
+	* modules/open (Files): Add m4/open-cloexec.m4.
+	(Depends-on): Depend on cloexec if replacing 'open'.
+	* modules/openat (Files): Add m4/open-cloexec.m4.
+	(Depends-on): Depend on cloexec if replacing openat.
+	* modules/popen-safer (Depends-on): Remove cloexec.
+	* modules/save-cwd (Depends-on): Remove cloexec, and add
+	fd-safer-flag and 'open'.
+
 2017-08-13  Paul Eggert  
 
 	reallocarray: minor fixes
diff --git a/NEWS b/NEWS
index 8fb2c54dd..d3a300a30 100644
--- a/NEWS
+++ b/NEWS
@@ -42,6 +42,10 @@ User visible incompatible changes
 
 DateModules Changes
 
+2017-08-14  fcntl-h This module now defaults O_CLOEXEC to a nonzero
+value instead of to 0, as the 'open' and
+'openat' modules now emulate O_C

Re: [PATCH 1/2] fts: fix cloexec races

2017-08-14 Thread Bruno Haible
Hi Paul,

> (opendirat, diropen): Use O_CLOEXEC instead of set_cloexec_flag.

There are platforms which support O_CLOEXEC; on these platforms this commit
is an improvement (eliminate uncontrolled inheritance of fd if another thread
calls fork+exec()).

There are also platforms which don't support O_CLOEXEC; on these platforms
gnulib's fcntl.h substitute defines O_CLOEXEC to 0. So, adding it to
the open() or openat() arguments has no effect, and eliminating the
set_cloexec_flag is a regression: it introduces unwanted inheritance of fd
even in single-threaded programs.

Or did I miss something?

lib/popen-safer.c contains a helper function open_noinherit that
  (a) is race-free on platformas which support O_CLOEXEC,
  (b) prevents unwanted inheritance of fd in single-threaded programs.
How about
  - moving this helper function to a separate module,
  - adding a similar helper function for openat(),
  - using these instead of unconditional uses of O_CLOEXEC ?

Alternatively, define O_CLOEXEC to a non-zero value on those platforms
that don't support it, and extend gnulib's open() and openat() wrappers
to support O_CLOEXEC. But this is more hairy because the platforms could,
at any time, introduce other O_* flags that happen to collide wit the value
we happen to pick for O_CLOEXEC.

Bruno




[PATCH 1/2] fts: fix cloexec races

2017-08-12 Thread Paul Eggert
* lib/fts.c [!_LIBC]: Do not include dirent--.h, unistd--.h, cloexec.h.
(opendirat, diropen): Use O_CLOEXEC instead of set_cloexec_flag.
(fts_build): Use F_DUPD_CLOEXEC rinstad of set_cloexec_flag.
(fd_ring_check): Set cloexec flag on new file descriptors.
(fts_build, fd_ring_check): While we’re at it, make sure the
resulting file descriptor is not 0, 1, or 2, since that is easy.
* modules/fts (Depends-on): Remove cloexec, dirent-safer, dup,
fcntl-safer, unistd-safer.  Add fcntl.
---
 ChangeLog   | 10 ++
 lib/fts.c   | 27 ---
 modules/fts |  6 +-
 3 files changed, 19 insertions(+), 24 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index 4b475437d..c725f5d91 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,13 @@
+2017-08-12  Paul Eggert  
+
+   fts: fix cloexec races
+   * lib/fts.c [!_LIBC]: Do not include dirent--.h, unistd--.h, cloexec.h.
+   (opendirat, diropen): Use O_CLOEXEC instead of set_cloexec_flag.
+   (fts_build): Use F_DUPD_CLOEXEC rinstad of set_cloexec_flag.
+   (fd_ring_check): Set cloexec flag on new file descriptors.
+   (fts_build, fd_ring_check): While we’re at it, make sure the
+   resulting file descriptor is not 0, 1, or 2, since that is easy.
+
 2017-08-11  Bruno Haible  
 
fts tests: Fix link error.
diff --git a/lib/fts.c b/lib/fts.c
index bf8edf821..ffa41130a 100644
--- a/lib/fts.c
+++ b/lib/fts.c
@@ -71,11 +71,6 @@ static char sccsid[] = "@(#)fts.c   8.6 (Berkeley) 
8/14/94";
 
 #if ! _LIBC
 # include "fcntl--.h"
-# include "dirent--.h"
-# include "unistd--.h"
-/* FIXME - use fcntl(F_DUPFD_CLOEXEC)/openat(O_CLOEXEC) once they are
-   supported.  */
-# include "cloexec.h"
 # include "flexmember.h"
 # include "openat.h"
 # include "same-inode.h"
@@ -305,14 +300,13 @@ static DIR *
 internal_function
 opendirat (int fd, char const *dir, int extra_flags, int *pdir_fd)
 {
-  int new_fd = openat (fd, dir,
-   (O_RDONLY | O_DIRECTORY | O_NOCTTY | O_NONBLOCK
-| extra_flags));
+  int open_flags = (O_RDONLY | O_CLOEXEC | O_DIRECTORY | O_NOCTTY
+| O_NONBLOCK | extra_flags);
+  int new_fd = openat (fd, dir, open_flags);
   DIR *dirp;
 
   if (new_fd < 0)
 return NULL;
-  set_cloexec_flag (new_fd, true);
   dirp = fdopendir (new_fd);
   if (dirp)
 *pdir_fd = new_fd;
@@ -375,15 +369,13 @@ static int
 internal_function
 diropen (FTS const *sp, char const *dir)
 {
-  int open_flags = (O_SEARCH | O_DIRECTORY | O_NOCTTY | O_NONBLOCK
+  int open_flags = (O_SEARCH | O_CLOEXEC | O_DIRECTORY | O_NOCTTY | O_NONBLOCK
 | (ISSET (FTS_PHYSICAL) ? O_NOFOLLOW : 0)
 | (ISSET (FTS_NOATIME) ? O_NOATIME : 0));
 
   int fd = (ISSET (FTS_CWDFD)
 ? openat (sp->fts_cwd_fd, dir, open_flags)
 : open (dir, open_flags));
-  if (0 <= fd)
-set_cloexec_flag (fd, true);
   return fd;
 }
 
@@ -1435,11 +1427,7 @@ fts_build (register FTS *sp, int type)
 if (descend || type == BREAD)
   {
 if (ISSET(FTS_CWDFD))
-  {
-dir_fd = dup (dir_fd);
-if (0 <= dir_fd)
-  set_cloexec_flag (dir_fd, true);
-  }
+  dir_fd = fcntl (dir_fd, F_DUPFD_CLOEXEC, STDERR_FILENO + 1);
 if (dir_fd < 0 || fts_safe_changedir(sp, cur, dir_fd, NULL)) {
 if (descend && type == BREAD)
 cur->fts_errno = errno;
@@ -1781,7 +1769,7 @@ fd_ring_check (FTS const *sp)
   I_ring fd_w = sp->fts_fd_ring;
 
   int cwd_fd = sp->fts_cwd_fd;
-  cwd_fd = dup (cwd_fd);
+  cwd_fd = fcntl (cwd_fd, F_DUPFD_CLOEXEC, STDERR_FILENO + 1);
   char *dot = getcwdat (cwd_fd, NULL, 0);
   error (0, 0, "= check = cwd: %s", dot);
   free (dot);
@@ -1790,7 +1778,8 @@ fd_ring_check (FTS const *sp)
   int fd = i_ring_pop (&fd_w);
   if (0 <= fd)
 {
-  int parent_fd = openat (cwd_fd, "..", O_SEARCH | O_NOATIME);
+  int open_flags = O_SEARCH | O_CLOEXEC | O_NOATIME;
+  int parent_fd = openat (cwd_fd, "..", open_flags);
   if (parent_fd < 0)
 {
   // Warn?
diff --git a/modules/fts b/modules/fts
index 55c09e7c9..ecbca70b5 100644
--- a/modules/fts
+++ b/modules/fts
@@ -8,16 +8,13 @@ lib/fts-cycle.c
 m4/fts.m4
 
 Depends-on:
-cloexec
 closedir
 cycle-check
 d-ino
 d-type
-dirent-safer
-dup
 fchdir
+fcntl
 fcntl-h
-fcntl-safer
 fdopendir
 flexmember
 fstat
@@ -32,7 +29,6 @@ readdir
 stdalign
 stdbool
 stddef
-unistd-safer
 
 configure.ac:
 gl_FUNC_FTS
-- 
2.13.4