aboutsummaryrefslogtreecommitdiff
path: root/support
diff options
context:
space:
mode:
Diffstat (limited to 'support')
-rw-r--r--support/Makefile10
-rw-r--r--support/capture_subprocess.h11
-rw-r--r--support/fuse.h1
-rw-r--r--support/support_capture_subprocess.c181
-rw-r--r--support/support_subprocess.c3
-rw-r--r--support/test-container.c13
-rw-r--r--support/tst-support-openpty-c.c2
-rw-r--r--support/tst-support-openpty.c49
8 files changed, 178 insertions, 92 deletions
diff --git a/support/Makefile b/support/Makefile
index 0c67055..d41278e 100644
--- a/support/Makefile
+++ b/support/Makefile
@@ -281,8 +281,11 @@ CFLAGS-temp_file.c += -D_FILE_OFFSET_BITS=64 -D_TIME_BITS=64
ifeq (,$(CXX))
LINKS_DSO_PROGRAM = links-dso-program-c
+LDLIBS-links-dso-program-c = -lgcc
+ifeq ($(have-libgcc_s),yes)
CFLAGS-links-dso-program-c.c += -fexceptions
-LDLIBS-links-dso-program-c = -lgcc -lgcc_s $(libunwind)
+LDLIBS-links-dso-program-c += -lgcc_s $(libunwind)
+endif
else
LINKS_DSO_PROGRAM = links-dso-program
LDLIBS-links-dso-program = -lstdc++ -lgcc -lgcc_s $(libunwind)
@@ -327,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 \
@@ -348,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/fuse.h b/support/fuse.h
index 7b246de..f379cc7 100644
--- a/support/fuse.h
+++ b/support/fuse.h
@@ -96,6 +96,7 @@ void *support_fuse_cast_name_internal (struct fuse_in_header *, uint32_t,
#define support_fuse_payload_type_READ struct fuse_read_in
#define support_fuse_payload_type_SETATTR struct fuse_setattr_in
#define support_fuse_payload_type_WRITE struct fuse_write_in
+#define support_fuse_payload_type_COPY_FILE_RANGE struct fuse_copy_file_range_in
#define support_fuse_cast(typ, inh) \
((support_fuse_payload_type_##typ *) \
support_fuse_cast_internal ((inh), FUSE_##typ))
diff --git a/support/support_capture_subprocess.c b/support/support_capture_subprocess.c
index c3ef478..c89e65b 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,109 @@ 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;
+ support_copy_file ("/proc/self/exe", execname);
+
+ if (chown (execname, getuid (), gid) != 0)
+ FAIL_UNSUPPORTED ("cannot change group of \"%s\" to %jd: %m",
+ execname, (intmax_t) gid);
+
+ if (chmod (execname, 02750) != 0)
+ FAIL_UNSUPPORTED ("cannot make \"%s\" SGID: %m ", execname);
+
+ /* Now we can drop the privilege of that group. */
+ const int count = 64;
+ gid_t groups[count];
+ int ngroups = getgroups(count, groups);
+
+ if (ngroups < 0)
+ FAIL_UNSUPPORTED ("Could not get group list again for user %jd\n",
+ (intmax_t) getuid ());
- char buf[4096];
- for (;;)
+ int n = 0;
+ for (int i = 0; i < ngroups; i++)
{
- 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)
+ if (groups[i] != gid)
{
- 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;
+ if (n != i)
+ groups[n] = groups[i];
+ n++;
}
}
-
- 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;
- }
-
- 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;
+ setgroups (n, groups);
/* 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 +228,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 +238,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/support_subprocess.c b/support/support_subprocess.c
index be00dde..8bf9a33 100644
--- a/support/support_subprocess.c
+++ b/support/support_subprocess.c
@@ -25,6 +25,7 @@
#include <support/check.h>
#include <support/xunistd.h>
#include <support/subprocess.h>
+#include <support/temp_file-internal.h>
static struct support_subprocess
support_subprocess_init (void)
@@ -60,6 +61,8 @@ support_subprocess (void (*callback) (void *), void *closure)
xclose (result.stdout_pipe[1]);
xclose (result.stderr_pipe[1]);
callback (closure);
+ /* Make sure that temporary files are deleted. */
+ support_delete_temp_files ();
_exit (0);
}
xclose (result.stdout_pipe[1]);
diff --git a/support/test-container.c b/support/test-container.c
index 79d3189..1c40ab3 100644
--- a/support/test-container.c
+++ b/support/test-container.c
@@ -740,7 +740,7 @@ main (int argc, char **argv)
char *command_basename;
char *so_base;
int do_postclean = 0;
- bool do_ldconfig = false;
+ bool do_ldconfig = true;
char *change_cwd = NULL;
int pipes[2];
@@ -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>