aboutsummaryrefslogtreecommitdiff
path: root/support
diff options
context:
space:
mode:
Diffstat (limited to 'support')
-rw-r--r--support/Makefile7
-rw-r--r--support/capture_subprocess.h11
-rw-r--r--support/support_capture_subprocess.c166
-rw-r--r--support/test-container.c11
-rw-r--r--support/tst-support-openpty-c.c2
-rw-r--r--support/tst-support-openpty.c49
6 files changed, 152 insertions, 94 deletions
diff --git a/support/Makefile b/support/Makefile
index ea7b4cd..d41278e 100644
--- a/support/Makefile
+++ b/support/Makefile
@@ -281,9 +281,9 @@ CFLAGS-temp_file.c += -D_FILE_OFFSET_BITS=64 -D_TIME_BITS=64
ifeq (,$(CXX))
LINKS_DSO_PROGRAM = links-dso-program-c
-CFLAGS-links-dso-program-c.c += -fexceptions
LDLIBS-links-dso-program-c = -lgcc
ifeq ($(have-libgcc_s),yes)
+CFLAGS-links-dso-program-c.c += -fexceptions
LDLIBS-links-dso-program-c += -lgcc_s $(libunwind)
endif
else
@@ -330,6 +330,7 @@ tests = \
README-testing \
tst-support-namespace \
tst-support-open-dev-null-range \
+ tst-support-openpty \
tst-support-process_state \
tst-support_blob_repeat \
tst-support_capture_subprocess \
@@ -351,6 +352,10 @@ tests = \
tst-xsigstack \
# tests
+tests-container = \
+ tst-support-openpty-c \
+ # tests-container
+
ifeq ($(run-built-tests),yes)
tests-special = \
$(objpfx)tst-support_record_failure-2.out
diff --git a/support/capture_subprocess.h b/support/capture_subprocess.h
index 91d75e5..b37462d 100644
--- a/support/capture_subprocess.h
+++ b/support/capture_subprocess.h
@@ -42,11 +42,12 @@ struct support_capture_subprocess support_capture_subprocess
struct support_capture_subprocess support_capture_subprogram
(const char *file, char *const argv[], char *const envp[]);
-/* Copy the running program into a setgid binary and run it with CHILD_ID
- argument. If execution is successful, return the exit status of the child
- program, otherwise return a non-zero failure exit code. */
-int support_capture_subprogram_self_sgid
- (char *child_id);
+/* Copy the running program into a setgid binary and run it with
+ CHILD_ID argument. If the program exits with a non-zero status,
+ exit with that exit status (or status 1 if the program did not exit
+ normally). If the test cannot be performed, exit with
+ EXIT_UNSUPPORTED. */
+void support_capture_subprogram_self_sgid (const char *child_id);
/* Deallocate the subprocess data captured by
support_capture_subprocess. */
diff --git a/support/support_capture_subprocess.c b/support/support_capture_subprocess.c
index c3ef478..b4e4bf9 100644
--- a/support/support_capture_subprocess.c
+++ b/support/support_capture_subprocess.c
@@ -21,12 +21,17 @@
#include <errno.h>
#include <fcntl.h>
+#include <grp.h>
+#include <scratch_buffer.h>
+#include <stdio_ext.h>
#include <stdlib.h>
+#include <string.h>
#include <support/check.h>
#include <support/xunistd.h>
#include <support/xsocket.h>
#include <support/xspawn.h>
#include <support/support.h>
+#include <support/temp_file.h>
#include <support/test-driver.h>
static void
@@ -109,111 +114,88 @@ support_capture_subprogram (const char *file, char *const argv[],
/* Copies the executable into a restricted directory, so that we can
safely make it SGID with the TARGET group ID. Then runs the
executable. */
-static int
-copy_and_spawn_sgid (char *child_id, gid_t gid)
+static void
+copy_and_spawn_sgid (const char *child_id, gid_t gid)
{
- char *dirname = xasprintf ("%s/tst-tunables-setuid.%jd",
- test_dir, (intmax_t) getpid ());
+ char *dirname = support_create_temp_directory ("tst-glibc-sgid-");
char *execname = xasprintf ("%s/bin", dirname);
- int infd = -1;
- int outfd = -1;
- int ret = 1, status = 1;
-
- TEST_VERIFY (mkdir (dirname, 0700) == 0);
- if (support_record_failure_is_failed ())
- goto err;
+ add_temp_file (execname);
- infd = open ("/proc/self/exe", O_RDONLY);
- if (infd < 0)
+ if (access ("/proc/self/exe", R_OK) != 0)
FAIL_UNSUPPORTED ("unsupported: Cannot read binary from procfs\n");
- outfd = open (execname, O_WRONLY | O_CREAT | O_EXCL, 0700);
- TEST_VERIFY (outfd >= 0);
- if (support_record_failure_is_failed ())
- goto err;
-
- char buf[4096];
- for (;;)
- {
- ssize_t rdcount = read (infd, buf, sizeof (buf));
- TEST_VERIFY (rdcount >= 0);
- if (support_record_failure_is_failed ())
- goto err;
- if (rdcount == 0)
- break;
- char *p = buf;
- char *end = buf + rdcount;
- while (p != end)
- {
- ssize_t wrcount = write (outfd, buf, end - p);
- if (wrcount == 0)
- errno = ENOSPC;
- TEST_VERIFY (wrcount > 0);
- if (support_record_failure_is_failed ())
- goto err;
- p += wrcount;
- }
- }
+ support_copy_file ("/proc/self/exe", execname);
- bool chowned = false;
- TEST_VERIFY ((chowned = fchown (outfd, getuid (), gid) == 0)
- || errno == EPERM);
- if (support_record_failure_is_failed ())
- goto err;
- else if (!chowned)
- {
- ret = 77;
- goto err;
- }
+ if (chown (execname, getuid (), gid) != 0)
+ FAIL_UNSUPPORTED ("cannot change group of \"%s\" to %jd: %m",
+ execname, (intmax_t) gid);
- TEST_VERIFY (fchmod (outfd, 02750) == 0);
- if (support_record_failure_is_failed ())
- goto err;
- TEST_VERIFY (close (outfd) == 0);
- if (support_record_failure_is_failed ())
- goto err;
- TEST_VERIFY (close (infd) == 0);
- if (support_record_failure_is_failed ())
- goto err;
+ if (chmod (execname, 02750) != 0)
+ FAIL_UNSUPPORTED ("cannot make \"%s\" SGID: %m ", execname);
/* We have the binary, now spawn the subprocess. Avoid using
support_subprogram because we only want the program exit status, not the
contents. */
- ret = 0;
- infd = outfd = -1;
- char * const args[] = {execname, child_id, NULL};
+ char * const args[] = {execname, (char *) child_id, NULL};
+ int status = support_subprogram_wait (args[0], args);
- status = support_subprogram_wait (args[0], args);
+ free (execname);
+ free (dirname);
-err:
- if (outfd >= 0)
- close (outfd);
- if (infd >= 0)
- close (infd);
- if (execname != NULL)
+ if (WIFEXITED (status))
{
- unlink (execname);
- free (execname);
+ if (WEXITSTATUS (status) == 0)
+ return;
+ else
+ exit (WEXITSTATUS (status));
}
- if (dirname != NULL)
+ else
+ FAIL_EXIT1 ("subprogram failed with status %d", status);
+}
+
+/* Returns true if a group with NAME has been found, and writes its
+ GID to *TARGET. */
+static bool
+find_sgid_group (gid_t *target, const char *name)
+{
+ /* Do not use getgrname_r because it does not work in statically
+ linked binaries if the system libc is different. */
+ FILE *fp = fopen ("/etc/group", "rce");
+ if (fp == NULL)
+ return false;
+ __fsetlocking (fp, FSETLOCKING_BYCALLER);
+
+ bool ok = false;
+ struct scratch_buffer buf;
+ scratch_buffer_init (&buf);
+ while (true)
{
- rmdir (dirname);
- free (dirname);
+ struct group grp;
+ struct group *result = NULL;
+ int status = fgetgrent_r (fp, &grp, buf.data, buf.length, &result);
+ if (status == 0 && result != NULL)
+ {
+ if (strcmp (result->gr_name, name) == 0)
+ {
+ *target = result->gr_gid;
+ ok = true;
+ break;
+ }
+ }
+ else if (errno != ERANGE)
+ break;
+ else if (!scratch_buffer_grow (&buf))
+ break;
}
-
- if (ret == 77)
- FAIL_UNSUPPORTED ("Failed to make sgid executable for test\n");
- if (ret != 0)
- FAIL_EXIT1 ("Failed to make sgid executable for test\n");
-
- return status;
+ scratch_buffer_free (&buf);
+ fclose (fp);
+ return ok;
}
-int
-support_capture_subprogram_self_sgid (char *child_id)
+void
+support_capture_subprogram_self_sgid (const char *child_id)
{
- gid_t target = 0;
const int count = 64;
gid_t groups[count];
@@ -225,6 +207,7 @@ support_capture_subprogram_self_sgid (char *child_id)
(intmax_t) getuid ());
gid_t current = getgid ();
+ gid_t target = current;
for (int i = 0; i < ret; ++i)
{
if (groups[i] != current)
@@ -234,11 +217,18 @@ support_capture_subprogram_self_sgid (char *child_id)
}
}
- if (target == 0)
- FAIL_UNSUPPORTED("Could not find a suitable GID for user %jd\n",
- (intmax_t) getuid ());
+ if (target == current)
+ {
+ /* If running as root, try to find a harmless group for SGID. */
+ if (getuid () != 0
+ || (!find_sgid_group (&target, "nogroup")
+ && !find_sgid_group (&target, "bin")
+ && !find_sgid_group (&target, "daemon")))
+ FAIL_UNSUPPORTED("Could not find a suitable GID for user %jd\n",
+ (intmax_t) getuid ());
+ }
- return copy_and_spawn_sgid (child_id, target);
+ copy_and_spawn_sgid (child_id, target);
}
void
diff --git a/support/test-container.c b/support/test-container.c
index 79d3189..a641250 100644
--- a/support/test-container.c
+++ b/support/test-container.c
@@ -1151,6 +1151,9 @@ main (int argc, char **argv)
devmount (new_root_path, "null");
devmount (new_root_path, "zero");
devmount (new_root_path, "urandom");
+#ifdef __linux__
+ devmount (new_root_path, "ptmx");
+#endif
/* We're done with the "old" root, switch to the new one. */
if (chroot (new_root_path) < 0)
@@ -1217,6 +1220,14 @@ main (int argc, char **argv)
maybe_xmkdir ("/tmp", 0755);
+#ifdef __linux__
+ maybe_xmkdir ("/dev/pts", 0777);
+ if (mount ("/dev/pts", "/dev/pts", "devpts", 0, "newinstance,ptmxmode=0666,mode=0666") < 0)
+ FAIL_EXIT1 ("can't mount /dev/pts: %m\n");
+ if (mount ("/dev/pts/ptmx", "/dev/ptmx", "", MS_BIND | MS_REC, NULL) < 0)
+ FAIL_EXIT1 ("can't mount /dev/ptmx\n");
+#endif
+
if (require_pidns)
{
/* Now that we're pid 1 (effectively "root") we can mount /proc */
diff --git a/support/tst-support-openpty-c.c b/support/tst-support-openpty-c.c
new file mode 100644
index 0000000..0a6a428
--- /dev/null
+++ b/support/tst-support-openpty-c.c
@@ -0,0 +1,2 @@
+/* Same test, but in a test-container. */
+#include "tst-support-openpty.c"
diff --git a/support/tst-support-openpty.c b/support/tst-support-openpty.c
new file mode 100644
index 0000000..1222d70
--- /dev/null
+++ b/support/tst-support-openpty.c
@@ -0,0 +1,49 @@
+/* Basic test for support_openpty support in test-container.
+ Copyright (C) 2025 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C 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.
+
+ The GNU C 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 the GNU C Library; if not, see
+ <https://www.gnu.org/licenses/>. */
+
+#include <termios.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+
+#include <support/tty.h>
+#include <support/check.h>
+#include <support/support.h>
+
+/* Note: the purpose of this test isn't to test if ptys function
+ correctly, but only to verify that test-container's support for
+ them is correct. The many checks in support_openpty.c are
+ sufficient for this. */
+
+int
+do_test (void)
+{
+ int outer, inner;
+ char *name;
+ struct termios term;
+ struct winsize win;
+
+ cfmakeraw (&term);
+ win.ws_row = 24;
+ win.ws_col = 80;
+
+ support_openpty (&outer, &inner, &name, &term, &win);
+
+ return 0;
+}
+
+#include <support/test-driver.c>