glib-unix: Ensure g_unix_open_pipe never leaves standard io fd range exposed

It's now possible that g_unix_open_pipe will reject an fd because it's
STDIN, STDOUT, or STDERR.

Having those fds left open to be used by the next thing is causing buggy
apps to fail, since they relied on g_unix_open_pipe using the fds.

This commit puts a /dev/null placeholder on an standard fds that
g_unix_open_pipe rejects.

Closes: https://gitlab.gnome.org/GNOME/glib/-/issues/2795
This commit is contained in:
Ray Strode 2022-10-28 10:16:56 -04:00
parent 356676bc5e
commit ee9c928e06

View File

@ -30,6 +30,7 @@
#include "glib-unix.h"
#include "gmain-internal.h"
#include "gstdio.h"
#include <string.h>
#include <sys/types.h>
@ -70,6 +71,59 @@ g_unix_set_error_from_errno (GError **error,
return FALSE;
}
static gboolean
move_pipe_fds_away_from_standard_io_range (int *fds,
GError **error)
{
int i;
gboolean result = TRUE;
/* For backward compatibilty with buggy apps, still make sure
* the standard fd slots get used up with something harmless
*/
for (i = 0; i < 2; i++)
{
int open_flags = 0;
int ret;
g_autofd int dev_null = -1;
switch (fds[i])
{
case STDIN_FILENO:
open_flags = O_RDONLY;
break;
case STDOUT_FILENO:
case STDERR_FILENO:
open_flags = O_WRONLY;
break;
default:
continue;
}
dev_null = g_open ("/dev/null", open_flags, 0);
if (dev_null < 0)
{
g_unix_set_error_from_errno (error, errno);
result = FALSE;
break;
}
do
ret = dup2 (g_steal_fd (&dev_null), fds[i]);
while (ret < 0 && (errno == EINTR || errno == EBUSY));
if (ret < 0)
{
g_unix_set_error_from_errno (error, errno);
result = FALSE;
break;
}
}
return result;
}
/**
* g_unix_open_pipe:
* @fds: (array fixed-size=2): Array of two integers
@ -111,10 +165,14 @@ g_unix_open_pipe (int *fds,
/* Don't reassign pipes to stdin, stdout, stderr if closed meanwhile */
else if (fds[0] < 3 || fds[1] < 3)
{
int old_fds[2] = { fds[0], fds[1] };
gboolean result = g_unix_open_pipe (fds, flags, error);
close (old_fds[0]);
close (old_fds[1]);
gboolean result;
result = move_pipe_fds_away_from_standard_io_range (fds, error);
if (!result)
return FALSE;
result = g_unix_open_pipe (fds, flags, error);
return result;
}
@ -129,10 +187,14 @@ g_unix_open_pipe (int *fds,
/* Don't reassign pipes to stdin, stdout, stderr if closed meanwhile */
else if (fds[0] < 3 || fds[1] < 3)
{
int old_fds[2] = { fds[0], fds[1] };
gboolean result = g_unix_open_pipe (fds, flags, error);
close (old_fds[0]);
close (old_fds[1]);
gboolean result;
result = move_pipe_fds_away_from_standard_io_range (fds, error);
if (!result)
return FALSE;
result = g_unix_open_pipe (fds, flags, error);
return result;
}