Add regression tests

This commit is contained in:
Chen Chengjun 2026-01-16 02:19:05 +00:00 committed by Tate, Hongliang Tian
parent 38d455496b
commit 8e6406ae35
4 changed files with 167 additions and 0 deletions

View File

@ -15,6 +15,7 @@ TEST_BUILD_DIR ?= $(INITRAMFS)/test
TEST_APPS := \
alarm \
capability \
chroot \
clone3 \
cpu_affinity \
devfs \

View File

@ -0,0 +1,5 @@
# SPDX-License-Identifier: MPL-2.0
include ../test_common.mk
EXTRA_C_FLAGS :=

View File

@ -0,0 +1,160 @@
// SPDX-License-Identifier: MPL-2.0
#define _GNU_SOURCE
#include <dirent.h>
#include <fcntl.h>
#include <limits.h>
#include <sched.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mount.h>
#include <sys/stat.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include "../test.h"
FN_SETUP(create_chroot_env)
{
// Create chroot target structure
CHECK_WITH(mkdir("/foo", 0755), _ret >= 0 || errno == EEXIST);
CHECK_WITH(mkdir("/foo/proc", 0755), _ret >= 0 || errno == EEXIST);
CHECK_WITH(mkdir("/foo/nix", 0755), _ret >= 0 || errno == EEXIST);
// Perform bind mounts
CHECK(mount("proc", "/foo/proc", "proc", 0, ""));
CHECK(mount("/nix", "/foo/nix", NULL, MS_BIND, NULL));
}
END_SETUP()
// Helper function to check if a directory does NOT contain a specific entry
static int dir_not_contains(const char *path, const char *entry_name)
{
DIR *dir = CHECK(opendir(path));
struct dirent *entry;
while ((entry = readdir(dir)) != NULL) {
if (strcmp(entry->d_name, entry_name) == 0) {
CHECK(closedir(dir));
return -1; // Found the entry, which means failure
}
}
CHECK(closedir(dir));
return 0; // Entry not found, success
}
// Helper function to read a file and check for a substring
static int file_contains(const char *filepath, const char *substring)
{
int fd = CHECK(open(filepath, O_RDONLY));
char buf[4096] = { 0 };
CHECK(read(fd, buf, sizeof(buf) - 1));
CHECK(close(fd));
return strstr(buf, substring) != NULL ? 0 : -1;
}
// Macro to wait for a child process and check its exit status
#define WAIT_AND_CHECK_CHILD(pid) \
do { \
int status; \
TEST_RES(waitpid(pid, &status, 0), \
_ret == pid && WIFEXITED(status) && \
WEXITSTATUS(status) == 0); \
} while (0)
FN_TEST(chroot_getcwd)
{
pid_t pid = TEST_SUCC(fork());
if (pid == 0) {
CHECK(chroot("/foo"));
CHECK(chdir("/"));
char cwd[PATH_MAX];
CHECK_WITH(getcwd(cwd, sizeof(cwd)), strcmp(cwd, "/") == 0);
exit(EXIT_SUCCESS);
} else {
WAIT_AND_CHECK_CHILD(pid);
}
}
END_TEST()
FN_TEST(chroot_chdir)
{
pid_t pid = TEST_SUCC(fork());
if (pid == 0) {
CHECK(chroot("/foo"));
CHECK(chdir("/"));
CHECK(chdir(".."));
// Verify we can't see 'foo' directory
CHECK(dir_not_contains(".", "foo"));
// Verify we are still at root
char cwd[PATH_MAX];
CHECK_WITH(getcwd(cwd, sizeof(cwd)), strcmp(cwd, "/") == 0);
exit(EXIT_SUCCESS);
} else {
WAIT_AND_CHECK_CHILD(pid);
}
}
END_TEST()
FN_TEST(chroot_mountinfo)
{
pid_t pid = TEST_SUCC(fork());
if (pid == 0) {
CHECK(chroot("/foo"));
CHECK(chdir("/"));
CHECK(file_contains("/proc/self/mountinfo", "/nix /nix"));
exit(EXIT_SUCCESS);
} else {
WAIT_AND_CHECK_CHILD(pid);
}
}
END_TEST()
FN_TEST(chroot_fd_escape)
{
pid_t pid = TEST_SUCC(fork());
if (pid == 0) {
int pre_chroot_fd =
CHECK(open("/test", O_RDONLY | O_DIRECTORY));
CHECK(chroot("/foo"));
CHECK(chdir("/"));
// Use fchdir to go to the pre-chroot /test directory
CHECK(syscall(SYS_fchdir, pre_chroot_fd));
// Now getcwd should add "(unreachable)" prefix because we're outside the chroot jail
char cwd[PATH_MAX];
CHECK_WITH(syscall(SYS_getcwd, cwd, sizeof(cwd)),
strcmp(cwd, "(unreachable)/test") == 0);
// But we should be able to see 'foo' directory by listing parent
CHECK(chdir(".."));
CHECK_WITH(dir_not_contains(".", "foo"), _ret == -1);
CHECK(close(pre_chroot_fd));
exit(EXIT_SUCCESS);
} else {
WAIT_AND_CHECK_CHILD(pid);
}
}
END_TEST()
FN_SETUP(cleanup_chroot_env)
{
CHECK(umount("/foo/proc"));
CHECK(umount("/foo/nix"));
CHECK(rmdir("/foo/proc"));
CHECK(rmdir("/foo/nix"));
CHECK(rmdir("/foo"));
}
END_SETUP()

View File

@ -10,6 +10,7 @@ cd ${SCRIPT_DIR}/..
echo "Start process test......"
# These test programs are sorted by name.
tests="
chroot/chroot_jail
clone3/clone_exit_signal
clone3/clone_files
clone3/clone_no_exit_signal