Merge branch 'cloexec-audit' into 'main'

Make sure new file descriptors have close-on-exec flag set on them in a race-free way

See merge request GNOME/glib!3283
This commit is contained in:
Philip Withnall 2023-02-21 12:58:01 +00:00
commit 339aaa3719
22 changed files with 191 additions and 75 deletions

View File

@ -38,6 +38,7 @@ if get_option('gtk_doc')
'gutilsprivate.h',
'gvalgrind.h',
'dirent.h',
'glib-unixprivate.h',
'glib-visibility.h',
'gmodule-visibility.h',
]

View File

@ -53,6 +53,12 @@
#include <windows.h>
#endif
#ifdef G_OS_WIN32
#define FO_CLOEXEC ""
#else
#define FO_CLOEXEC "e"
#endif
#include "glibintl.h"
/**
@ -711,7 +717,7 @@ g_dbus_address_connect (const gchar *address_entry,
int errsv;
/* be careful to read only 16 bytes - we also check that the file is only 16 bytes long */
f = fopen (nonce_file, "rb");
f = fopen (nonce_file, "rb" FO_CLOEXEC);
errsv = errno;
if (f == NULL)
{

View File

@ -37,6 +37,10 @@
#include "gwin32sid.h"
#endif
#ifndef O_CLOEXEC
#define O_CLOEXEC 0
#endif
#include "gdbusauthmechanismsha1.h"
#include "gcredentials.h"
#include "gdbuserror.h"
@ -519,7 +523,7 @@ create_lock_exclusive (const gchar *lock_path,
int errsv;
gint ret;
ret = g_open (lock_path, O_CREAT | O_EXCL, 0600);
ret = g_open (lock_path, O_CREAT | O_EXCL | O_CLOEXEC, 0600);
errsv = errno;
if (ret < 0)
{

View File

@ -51,6 +51,10 @@
#define O_BINARY 0
#endif
#ifndef O_CLOEXEC
#define O_CLOEXEC 0
#endif
#include "gfileattribute.h"
#include "glocalfile.h"
#include "glocalfileinfo.h"
@ -1352,7 +1356,7 @@ g_local_file_read (GFile *file,
int fd, ret;
GLocalFileStat buf;
fd = g_open (local->filename, O_RDONLY|O_BINARY, 0);
fd = g_open (local->filename, O_RDONLY | O_BINARY | O_CLOEXEC, 0);
if (fd == -1)
{
int errsv = errno;
@ -2227,7 +2231,7 @@ g_local_file_trash (GFile *file,
infofile = g_build_filename (infodir, infoname, NULL);
g_free (infoname);
fd = g_open (infofile, O_CREAT | O_EXCL, 0666);
fd = g_open (infofile, O_CREAT | O_EXCL | O_CLOEXEC, 0666);
errsv = errno;
} while (fd == -1 && errsv == EEXIST);
@ -2881,9 +2885,9 @@ g_local_file_measure_size_of_file (gint parent_fd,
#ifdef AT_FDCWD
#ifdef HAVE_OPEN_O_DIRECTORY
dir_fd = openat (parent_fd, name->data, O_RDONLY|O_DIRECTORY);
dir_fd = openat (parent_fd, name->data, O_RDONLY | O_DIRECTORY | O_CLOEXEC);
#else
dir_fd = openat (parent_fd, name->data, O_RDONLY);
dir_fd = openat (parent_fd, name->data, O_RDONLY | O_CLOEXEC);
#endif
errsv = errno;
if (dir_fd < 0)

View File

@ -92,6 +92,10 @@
#endif
#endif
#ifndef O_CLOEXEC
#define O_CLOEXEC 0
#endif
#include "glocalfileinfo.h"
#include "gioerror.h"
#include "gthemedicon.h"
@ -1391,11 +1395,11 @@ get_content_type (const char *basename,
sniff_length = 4096;
#ifdef O_NOATIME
fd = g_open (path, O_RDONLY | O_NOATIME, 0);
fd = g_open (path, O_RDONLY | O_NOATIME | O_CLOEXEC, 0);
errsv = errno;
if (fd < 0 && errsv == EPERM)
#endif
fd = g_open (path, O_RDONLY, 0);
fd = g_open (path, O_RDONLY | O_CLOEXEC, 0);
if (fd != -1)
{

View File

@ -700,7 +700,7 @@ _g_local_file_output_stream_open (const char *filename,
if (g_cancellable_set_error_if_cancelled (cancellable, error))
return NULL;
open_flags = O_BINARY;
open_flags = O_BINARY | O_CLOEXEC;
if (readable)
open_flags |= O_RDWR;
else
@ -737,7 +737,7 @@ _g_local_file_output_stream_create (const char *filename,
mode = mode_from_flags_or_info (flags, reference_info);
open_flags = O_CREAT | O_EXCL | O_BINARY;
open_flags = O_CREAT | O_EXCL | O_BINARY | O_CLOEXEC;
if (readable)
open_flags |= O_RDWR;
else
@ -762,7 +762,7 @@ _g_local_file_output_stream_append (const char *filename,
else
mode = 0666;
return output_stream_open (filename, O_CREAT | O_APPEND | O_WRONLY | O_BINARY, mode,
return output_stream_open (filename, O_CREAT | O_APPEND | O_WRONLY | O_BINARY | O_CLOEXEC, mode,
cancellable, error);
}
@ -865,9 +865,9 @@ handle_overwrite_open (const char *filename,
/* We only need read access to the original file if we are creating a backup.
* We also add O_CREAT to avoid a race if the file was just removed */
if (create_backup || readable)
open_flags = O_RDWR | O_CREAT | O_BINARY;
open_flags = O_RDWR | O_CREAT | O_BINARY | O_CLOEXEC;
else
open_flags = O_WRONLY | O_CREAT | O_BINARY;
open_flags = O_WRONLY | O_CREAT | O_BINARY | O_CLOEXEC;
/* Some systems have O_NOFOLLOW, which lets us avoid some races
* when finding out if the file we opened was a symlink */
@ -1113,7 +1113,7 @@ handle_overwrite_open (const char *filename,
}
bfd = g_open (backup_filename,
O_WRONLY | O_CREAT | O_EXCL | O_BINARY,
O_WRONLY | O_CREAT | O_EXCL | O_BINARY | O_CLOEXEC,
_g_stat_mode (&original_stat) & 0777);
if (bfd == -1)
@ -1208,9 +1208,9 @@ handle_overwrite_open (const char *filename,
}
if (readable)
open_flags = O_RDWR | O_CREAT | O_BINARY;
open_flags = O_RDWR | O_CREAT | O_BINARY | O_CLOEXEC;
else
open_flags = O_WRONLY | O_CREAT | O_BINARY;
open_flags = O_WRONLY | O_CREAT | O_BINARY | O_CLOEXEC;
fd = g_open (filename, open_flags, mode);
if (fd == -1)
{

View File

@ -2858,6 +2858,9 @@ g_socket_accept (GSocket *socket,
GCancellable *cancellable,
GError **error)
{
#ifdef HAVE_ACCEPT4
gboolean try_accept4 = TRUE;
#endif
GSocket *new_socket;
gint ret;
@ -2871,7 +2874,28 @@ g_socket_accept (GSocket *socket,
while (TRUE)
{
if ((ret = accept (socket->priv->fd, NULL, 0)) < 0)
gboolean try_accept = TRUE;
#ifdef HAVE_ACCEPT4
if (try_accept4)
{
ret = accept4 (socket->priv->fd, NULL, 0, SOCK_CLOEXEC);
if (ret < 0 && errno == ENOSYS)
{
try_accept4 = FALSE;
}
else
{
try_accept = FALSE;
}
}
g_assert (try_accept4 || try_accept);
#endif
if (try_accept)
ret = accept (socket->priv->fd, NULL, 0);
if (ret < 0)
{
int errsv = get_socket_errno ();

View File

@ -50,6 +50,7 @@
#ifdef G_OS_UNIX
#include "glib-unix.h"
#include "glib-unixprivate.h"
#endif
/* -------------------------------------------------------------------------- */
@ -248,7 +249,7 @@ watcher_init (void)
gint pipe_fds[2];
/* fork a child to clean up when we are killed */
if (pipe (pipe_fds) != 0)
if (!g_unix_open_pipe_internal (pipe_fds, TRUE))
{
errsv = errno;
g_warning ("pipe() failed: %s", g_strerror (errsv));

View File

@ -3166,7 +3166,7 @@ _resolve_dev_root (void)
/* see if device with similar major:minor as /dev/root is mention
* in /etc/mtab (it usually is)
*/
f = fopen ("/etc/mtab", "r");
f = fopen ("/etc/mtab", "re");
if (f != NULL)
{
struct mntent *entp;

View File

@ -32,6 +32,10 @@
#include <fcntl.h>
#include <string.h>
#ifndef O_CLOEXEC
#define O_CLOEXEC 0
#endif
#include <glib-object.h>
#include <glib/gfileutils.h>
#include <gio/gfilemonitor.h>
@ -583,7 +587,7 @@ _kqsub_start_watching (kqueue_sub *sub)
struct stat st;
struct kevent ev;
sub->fd = open (sub->filename, O_KQFLAG);
sub->fd = open (sub->filename, O_KQFLAG | O_CLOEXEC);
if (sub->fd == -1)
return FALSE;

View File

@ -45,6 +45,7 @@
#include <time.h>
#ifdef G_OS_UNIX
#include "glib-unixprivate.h"
#include <errno.h>
#include <unistd.h>
#include <sys/wait.h>
@ -397,7 +398,8 @@ stack_trace (const char * const *args)
stack_trace_done = FALSE;
signal (SIGCHLD, stack_trace_sigchld);
if ((pipe (in_fd) == -1) || (pipe (out_fd) == -1))
if (!g_unix_open_pipe_internal (in_fd, TRUE) ||
!g_unix_open_pipe_internal (out_fd, TRUE))
{
perror ("unable to open pipe");
_exit (0);

View File

@ -446,7 +446,7 @@ read_aliases (const gchar *file,
FILE *fp;
char buf[256];
fp = fopen (file,"r");
fp = fopen (file, "re");
if (!fp)
return;
while (fgets (buf, 256, fp))

View File

@ -329,7 +329,7 @@ g_mkdir_with_parents (const gchar *pathname,
* }
*
* // DO THIS INSTEAD
* fd = g_open (filename, O_WRONLY | O_NOFOLLOW);
* fd = g_open (filename, O_WRONLY | O_NOFOLLOW | O_CLOEXEC);
* if (fd == -1)
* {
* // check error
@ -908,7 +908,7 @@ get_contents_posix (const gchar *filename,
gint fd;
/* O_BINARY useful on Cygwin */
fd = open (filename, O_RDONLY|O_BINARY);
fd = open (filename, O_RDONLY | O_BINARY | O_CLOEXEC);
if (fd < 0)
{
@ -1083,7 +1083,7 @@ rename_file (const char *old_name,
if (do_fsync)
{
gchar *dir = g_path_get_dirname (new_name);
int dir_fd = g_open (dir, O_RDONLY, 0);
int dir_fd = g_open (dir, O_RDONLY | O_CLOEXEC, 0);
if (dir_fd >= 0)
{

View File

@ -44,6 +44,10 @@
#include <fcntl.h>
#include <glib/gstdio.h>
#ifndef O_CLOEXEC
#define O_CLOEXEC 0
#endif
#include "giochannel.h"
#include "gerror.h"
@ -527,7 +531,7 @@ g_io_channel_new_file (const gchar *filename,
create_mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
fid = g_open (filename, flags, create_mode);
fid = g_open (filename, flags | O_CLOEXEC, create_mode);
if (fid == -1)
{
int err = errno;

View File

@ -53,6 +53,10 @@
#endif /* G_OS_WIN23 */
#ifndef O_CLOEXEC
#define O_CLOEXEC 0
#endif
#include "gconvert.h"
#include "gdataset.h"
#include "gerror.h"
@ -761,7 +765,7 @@ find_file_in_data_dirs (const gchar *file,
path = g_build_filename (data_dir, sub_dir,
candidate_file, NULL);
fd = g_open (path, O_RDONLY, 0);
fd = g_open (path, O_RDONLY | O_CLOEXEC, 0);
if (fd == -1)
{
@ -917,7 +921,7 @@ g_key_file_load_from_file (GKeyFile *key_file,
g_return_val_if_fail (key_file != NULL, FALSE);
g_return_val_if_fail (file != NULL, FALSE);
fd = g_open (file, O_RDONLY, 0);
fd = g_open (file, O_RDONLY | O_CLOEXEC, 0);
errsv = errno;
if (fd == -1)

View File

@ -23,12 +23,8 @@
#include "config.h"
/* To make bionic export pipe2() */
#ifndef _GNU_SOURCE
#define _GNU_SOURCE 1
#endif
#include "glib-unix.h"
#include "glib-unixprivate.h"
#include "gmain-internal.h"
#include <string.h>
@ -94,48 +90,13 @@ g_unix_open_pipe (int *fds,
int flags,
GError **error)
{
int ecode;
/* We only support FD_CLOEXEC */
g_return_val_if_fail ((flags & (FD_CLOEXEC)) == flags, FALSE);
#ifdef HAVE_PIPE2
{
int pipe2_flags = 0;
if (flags & FD_CLOEXEC)
pipe2_flags |= O_CLOEXEC;
/* Atomic */
ecode = pipe2 (fds, pipe2_flags);
if (ecode == -1 && errno != ENOSYS)
return g_unix_set_error_from_errno (error, errno);
else if (ecode == 0)
return TRUE;
/* Fall through on -ENOSYS, we must be running on an old kernel */
}
#endif
ecode = pipe (fds);
if (ecode == -1)
if (!g_unix_open_pipe_internal (fds,
(flags & FD_CLOEXEC) != 0))
return g_unix_set_error_from_errno (error, errno);
if (flags == 0)
return TRUE;
ecode = fcntl (fds[0], F_SETFD, flags);
if (ecode == -1)
{
int saved_errno = errno;
close (fds[0]);
close (fds[1]);
return g_unix_set_error_from_errno (error, saved_errno);
}
ecode = fcntl (fds[1], F_SETFD, flags);
if (ecode == -1)
{
int saved_errno = errno;
close (fds[0]);
close (fds[1]);
return g_unix_set_error_from_errno (error, saved_errno);
}
return TRUE;
}

87
glib/glib-unixprivate.h Normal file
View File

@ -0,0 +1,87 @@
/* glib-unixprivate.h - Unix specific integration private functions
*
* SPDX-License-Identifier: LGPL-2.1-or-later
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __G_UNIXPRIVATE_H__
#define __G_UNIXPRIVATE_H__
#include "config.h"
#ifndef G_OS_UNIX
#error "This header may only be used on UNIX"
#endif
/* To make bionic export pipe2() */
#ifndef _GNU_SOURCE
#define _GNU_SOURCE 1
#endif
#include "gmacros.h"
#include "gtypes.h"
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
G_BEGIN_DECLS
static inline gboolean
g_unix_open_pipe_internal (int *fds,
gboolean close_on_exec)
{
#ifdef HAVE_PIPE2
do
{
int ecode;
/* Atomic */
ecode = pipe2 (fds, close_on_exec ? O_CLOEXEC : 0);
if (ecode == -1 && errno != ENOSYS)
return FALSE;
else if (ecode == 0)
return TRUE;
/* Fall through on -ENOSYS, we must be running on an old kernel */
}
while (FALSE);
#endif
if (pipe (fds) == -1)
return FALSE;
if (!close_on_exec)
return TRUE;
if (fcntl (fds[0], F_SETFD, FD_CLOEXEC) == -1 ||
fcntl (fds[1], F_SETFD, FD_CLOEXEC) == -1)
{
int saved_errno = errno;
close (fds[0]);
close (fds[1]);
fds[0] = -1;
fds[1] = -1;
errno = saved_errno;
return FALSE;
}
return TRUE;
}
G_END_DECLS
#endif /* __G_UNIXPRIVATE_H__ */

View File

@ -50,6 +50,10 @@
#endif
#ifndef O_CLOEXEC
#define O_CLOEXEC 0
#endif
#include "gconvert.h"
#include "gerror.h"
#include "gfileutils.h"
@ -252,7 +256,7 @@ g_mapped_file_new (const gchar *filename,
g_return_val_if_fail (filename != NULL, NULL);
g_return_val_if_fail (!error || *error == NULL, NULL);
fd = g_open (filename, (writable ? O_RDWR : O_RDONLY) | _O_BINARY, 0);
fd = g_open (filename, (writable ? O_RDWR : O_RDONLY) | _O_BINARY | O_CLOEXEC, 0);
if (fd == -1)
{
int save_errno = errno;

View File

@ -3858,8 +3858,9 @@ wait_for_child (GPid pid,
* Since: 2.16
*
* Deprecated: This function is implemented only on Unix platforms,
* and is not always reliable due to problems inherent in
* fork-without-exec. Use g_test_trap_subprocess() instead.
* is not always reliable due to problems inherent in fork-without-exec
* and doesn't set close-on-exec flag on its file descriptors.
* Use g_test_trap_subprocess() instead.
*/
G_GNUC_BEGIN_IGNORE_DEPRECATIONS
gboolean

View File

@ -365,7 +365,7 @@ if host_system == 'windows'
glib_sources += files('dirent/wdirent.c')
endif
else
glib_sources += files('glib-unix.c', 'gspawn.c', 'giounix.c')
glib_sources += files('glib-unix.c', 'glib-unixprivate.h', 'gspawn.c', 'giounix.c')
platform_deps = []
endif

View File

@ -45,6 +45,10 @@
#include <io.h> /* For open() and close() prototypes. */
#endif
#ifndef O_CLOEXEC
#define O_CLOEXEC 0
#endif
#include "gmoduleconf.h"
#include "gstdio.h"
@ -388,7 +392,7 @@ parse_libtool_archive (const gchar* libtool_name)
GTokenType token;
GScanner *scanner;
int fd = g_open (libtool_name, O_RDONLY, 0);
int fd = g_open (libtool_name, O_RDONLY | O_CLOEXEC, 0);
if (fd < 0)
{
gchar *display_libtool_name = g_filename_display_name (libtool_name);

View File

@ -606,6 +606,7 @@ if host_system == 'windows'
endif
functions = [
'accept4',
'close_range',
'endmntent',
'endservent',