Willy, Thomas
This is v3 to allow run with minimal kernel config, see v2 [1].
Applied further suggestions from Thomas, It is based on our previous v5 sysret helper series [2] and Thomas' chmod_net removal patchset [3].
Now, a test report on arm/vexpress-a9 without procfs, shmem, tmpfs, net and memfd_create looks like:
LOG: testing report for arm/vexpress-a9:
14 chmod_self [SKIPPED] 16 chown_self [SKIPPED] 40 link_cross [SKIPPED] 0 -fstackprotector not supported [SKIPPED]
139 test(s) passed, 4 skipped, 0 failed. See all results in /labs/linux-lab/logging/nolibc/arm-vexpress-a9-nolibc-test.log
LOG: testing summary:
arch/board | result ------------|------------ arm/vexpress-a9 | 139 test(s) passed, 4 skipped, 0 failed. See all results in /labs/linux-lab/logging/nolibc/arm-vexpress-a9-nolibc-test.log
Changes from v2 --> v3:
* Added Reviewed-by from Thomas for the whole series, Many Thanks
* selftests/nolibc: stat_fault: silence NULL argument warning with glibc selftests/nolibc: gettid: restore for glibc and musl selftests/nolibc: add _LARGEFILE64_SOURCE for musl selftests/nolibc: fix up int_fast16/32_t test cases for musl selftests/nolibc: fix up kernel parameters support selftests/nolibc: stat_timestamps: remove procfs dependency selftests/nolibc: link_cross: use /proc/self/cmdline tools/nolibc: add rmdir() support selftests/nolibc: add a new rmdir() test case selftests/nolibc: fix up failures when CONFIG_PROC_FS=n selftests/nolibc: vfprintf: remove MEMFD_CREATE dependency
No code changes except some commit message cleanups.
* selftests/nolibc: prepare /tmp for tmpfs or ramfs
As suggested by Thomas, simply calling mkdir() and mount() to prepare /tmp can save a stat() call.
* selftests/nolibc: chroot_exe: remove procfs dependency
As suggested by Thomas, remove the 'weird' get_tmpfile() and use the '/init' for !procfs as we did for stat_timestamps.
For the worst-case scene, when '/init' is not there, add ENOENT to the error check list.
Now, it is a oneline code change.
* selftests/nolibc: add chmod_tmpdir test
Without get_tmpfile(), let's direct mkdir() a temp directory for chmod_tmpdir test, it function as a substitute for the removed chmod_net.
Now, it is a oneline code change.
Best regards, Zhangjin --- [1]: https://lore.kernel.org/lkml/cover.1688078604.git.falcon@tinylab.org/
Zhangjin Wu (14): selftests/nolibc: stat_fault: silence NULL argument warning with glibc selftests/nolibc: gettid: restore for glibc and musl selftests/nolibc: add _LARGEFILE64_SOURCE for musl selftests/nolibc: fix up int_fast16/32_t test cases for musl selftests/nolibc: fix up kernel parameters support selftests/nolibc: stat_timestamps: remove procfs dependency selftests/nolibc: chroot_exe: remove procfs dependency selftests/nolibc: link_cross: use /proc/self/cmdline tools/nolibc: add rmdir() support selftests/nolibc: add a new rmdir() test case selftests/nolibc: fix up failures when CONFIG_PROC_FS=n selftests/nolibc: prepare /tmp for tmpfs or ramfs selftests/nolibc: add chmod_tmpdir test selftests/nolibc: vfprintf: remove MEMFD_CREATE dependency
tools/include/nolibc/sys.h | 22 ++++++ tools/testing/selftests/nolibc/nolibc-test.c | 83 +++++++++++++++----- 2 files changed, 87 insertions(+), 18 deletions(-)
Use another invalid address (void *)1 instead of NULL to silence this compile warning with glibc:
$ make libc-test CC libc-test nolibc-test.c: In function ‘run_syscall’: nolibc-test.c:622:49: warning: null argument where non-null required (argument 1) [-Wnonnull] 622 | CASE_TEST(stat_fault); EXPECT_SYSER(1, stat(NULL, &stat_buf), -1, EFAULT); break; | ^~~~ nolibc-test.c:304:79: note: in definition of macro ‘EXPECT_SYSER2’ 304 | do { if (!cond) pad_spc(llen, 64, "[SKIPPED]\n"); else ret += expect_syserr2(expr, expret, experr1, experr2, llen); } while (0) | ^~~~ nolibc-test.c:622:33: note: in expansion of macro ‘EXPECT_SYSER’ 622 | CASE_TEST(stat_fault); EXPECT_SYSER(1, stat(NULL, &stat_buf), -1, EFAULT); break;
Reviewed-by: Thomas Weißschuh linux@weissschuh.net Signed-off-by: Zhangjin Wu falcon@tinylab.org --- tools/testing/selftests/nolibc/nolibc-test.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/tools/testing/selftests/nolibc/nolibc-test.c b/tools/testing/selftests/nolibc/nolibc-test.c index f85f55921410..ffef0ff921bf 100644 --- a/tools/testing/selftests/nolibc/nolibc-test.c +++ b/tools/testing/selftests/nolibc/nolibc-test.c @@ -733,7 +733,7 @@ int run_syscall(int min, int max) CASE_TEST(select_stdout); EXPECT_SYSNE(1, ({ fd_set fds; FD_ZERO(&fds); FD_SET(1, &fds); select(2, NULL, &fds, NULL, NULL); }), -1); break; CASE_TEST(select_fault); EXPECT_SYSER(1, select(1, (void *)1, NULL, NULL, 0), -1, EFAULT); break; CASE_TEST(stat_blah); EXPECT_SYSER(1, stat("/proc/self/blah", &stat_buf), -1, ENOENT); break; - CASE_TEST(stat_fault); EXPECT_SYSER(1, stat(NULL, &stat_buf), -1, EFAULT); break; + CASE_TEST(stat_fault); EXPECT_SYSER(1, stat((void *)1, &stat_buf), -1, EFAULT); break; CASE_TEST(stat_timestamps); EXPECT_SYSZR(1, test_stat_timestamps()); break; CASE_TEST(symlink_root); EXPECT_SYSER(1, symlink("/", "/"), -1, EEXIST); break; CASE_TEST(unlink_root); EXPECT_SYSER(1, unlink("/"), -1, EISDIR); break;
As the gettid manpage [1] shows, glibc 2.30 has gettid support, so, let's enable the test for glibc >= 2.30.
gettid works on musl too.
[1]: https://man7.org/linux/man-pages/man2/gettid.2.html
Reviewed-by: Thomas Weißschuh linux@weissschuh.net Signed-off-by: Zhangjin Wu falcon@tinylab.org --- tools/testing/selftests/nolibc/nolibc-test.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-)
diff --git a/tools/testing/selftests/nolibc/nolibc-test.c b/tools/testing/selftests/nolibc/nolibc-test.c index ffef0ff921bf..be7614c897b4 100644 --- a/tools/testing/selftests/nolibc/nolibc-test.c +++ b/tools/testing/selftests/nolibc/nolibc-test.c @@ -659,6 +659,7 @@ int run_syscall(int min, int max) int tmp; int ret = 0; void *p1, *p2; + int has_gettid = 1;
/* <proc> indicates whether or not /proc is mounted */ proc = stat("/proc", &stat_buf) == 0; @@ -666,6 +667,11 @@ int run_syscall(int min, int max) /* this will be used to skip certain tests that can't be run unprivileged */ euid0 = geteuid() == 0;
+ /* from 2.30, glibc provides gettid() */ +#if defined(__GLIBC_MINOR__) && defined(__GLIBC__) + has_gettid = __GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 30); +#endif + for (test = min; test >= 0 && test <= max; test++) { int llen = 0; /* line length */
@@ -675,9 +681,7 @@ int run_syscall(int min, int max) switch (test + __LINE__ + 1) { CASE_TEST(getpid); EXPECT_SYSNE(1, getpid(), -1); break; CASE_TEST(getppid); EXPECT_SYSNE(1, getppid(), -1); break; -#ifdef NOLIBC - CASE_TEST(gettid); EXPECT_SYSNE(1, gettid(), -1); break; -#endif + CASE_TEST(gettid); EXPECT_SYSNE(has_gettid, gettid(), -1); break; CASE_TEST(getpgid_self); EXPECT_SYSNE(1, getpgid(0), -1); break; CASE_TEST(getpgid_bad); EXPECT_SYSER(1, getpgid(-1), -1, ESRCH); break; CASE_TEST(kill_0); EXPECT_SYSZR(1, kill(getpid(), 0)); break;
_GNU_SOURCE Implies _LARGEFILE64_SOURCE in glibc, but in musl, the default configuration doesn't enable _LARGEFILE64_SOURCE.
From include/dirent.h of musl, getdents64 is provided as getdents when _LARGEFILE64_SOURCE is defined.
#if defined(_LARGEFILE64_SOURCE) ... #define getdents64 getdents #endif
Let's define _LARGEFILE64_SOURCE to fix up this compile error:
tools/testing/selftests/nolibc/nolibc-test.c: In function ‘test_getdents64’: tools/testing/selftests/nolibc/nolibc-test.c:453:8: warning: implicit declaration of function ‘getdents64’; did you mean ‘getdents’? [-Wimplicit-function-declaration] 453 | ret = getdents64(fd, (void *)buffer, sizeof(buffer)); | ^~~~~~~~~~ | getdents /usr/bin/ld: /tmp/ccKILm5u.o: in function `test_getdents64': nolibc-test.c:(.text+0xe3e): undefined reference to `getdents64' collect2: error: ld returned 1 exit status
Reviewed-by: Thomas Weißschuh linux@weissschuh.net Signed-off-by: Zhangjin Wu falcon@tinylab.org --- tools/testing/selftests/nolibc/nolibc-test.c | 1 + 1 file changed, 1 insertion(+)
diff --git a/tools/testing/selftests/nolibc/nolibc-test.c b/tools/testing/selftests/nolibc/nolibc-test.c index be7614c897b4..d9644244fc95 100644 --- a/tools/testing/selftests/nolibc/nolibc-test.c +++ b/tools/testing/selftests/nolibc/nolibc-test.c @@ -1,6 +1,7 @@ /* SPDX-License-Identifier: GPL-2.0 */
#define _GNU_SOURCE +#define _LARGEFILE64_SOURCE
/* libc-specific include files * The program may be built in 3 ways:
On Fri, Jun 30, 2023, at 16:44, Zhangjin Wu wrote:
_GNU_SOURCE Implies _LARGEFILE64_SOURCE in glibc, but in musl, the default configuration doesn't enable _LARGEFILE64_SOURCE.
From include/dirent.h of musl, getdents64 is provided as getdents when _LARGEFILE64_SOURCE is defined.
#if defined(_LARGEFILE64_SOURCE) ... #define getdents64 getdents #endif
Let's define _LARGEFILE64_SOURCE to fix up this compile error:
I think a better solution would be to use the normal getdents() instead of glibc getdents64(), but then define _FILE_OFFSET_BITS=64 to tell glibc to use the modern version of all filesystem syscalls.
Arnd
Hi, Arnd
On Fri, Jun 30, 2023, at 16:44, Zhangjin Wu wrote:
_GNU_SOURCE Implies _LARGEFILE64_SOURCE in glibc, but in musl, the default configuration doesn't enable _LARGEFILE64_SOURCE.
From include/dirent.h of musl, getdents64 is provided as getdents when _LARGEFILE64_SOURCE is defined.
#if defined(_LARGEFILE64_SOURCE) ... #define getdents64 getdents #endif
Let's define _LARGEFILE64_SOURCE to fix up this compile error:
I think a better solution would be to use the normal getdents() instead of glibc getdents64(), but then define _FILE_OFFSET_BITS=64 to tell glibc to use the modern version of all filesystem syscalls.
Just checked the getdents manpage[1] and the nolibc code, both of glibc and nolibc don't provide the getdents() library routine but both of them provide the getdents64(), only musl provide getdents() by default.
And as the manpage shows, it is not easy to call getdents() with glibc, we need manually call syscall() and define the 'dirent' struct ourselves:
glibc does not provide a wrapper for getdents(); call getdents() using syscall(2). In that case you will need to define the linux_dirent or linux_dirent64 structure yourself.
And for nolibc, a getdents64() with linux_dirent64 struct (with int64_t offset) is provided, there is either no getdents() currently.
This patch aims to let nolibc-test at least compile for musl and therefore we can easily check the new tests for musl, glibc and nolibc together.
For the 64bit offset related stuff, we'd better delay it in another patchset (part of full rv32 support), which will convert the off_t to 64bit by default.
Thanks, Zhangjin
[1]: https://man7.org/linux/man-pages/man2/getdents.2.html
Arnd
On Fri, Jun 30, 2023, at 20:01, Zhangjin Wu wrote:
Hi, Arnd
On Fri, Jun 30, 2023, at 16:44, Zhangjin Wu wrote:
_GNU_SOURCE Implies _LARGEFILE64_SOURCE in glibc, but in musl, the default configuration doesn't enable _LARGEFILE64_SOURCE.
From include/dirent.h of musl, getdents64 is provided as getdents when _LARGEFILE64_SOURCE is defined.
#if defined(_LARGEFILE64_SOURCE) ... #define getdents64 getdents #endif
Let's define _LARGEFILE64_SOURCE to fix up this compile error:
I think a better solution would be to use the normal getdents() instead of glibc getdents64(), but then define _FILE_OFFSET_BITS=64 to tell glibc to use the modern version of all filesystem syscalls.
Just checked the getdents manpage[1] and the nolibc code, both of glibc and nolibc don't provide the getdents() library routine but both of them provide the getdents64(), only musl provide getdents() by default.
And as the manpage shows, it is not easy to call getdents() with glibc, we need manually call syscall() and define the 'dirent' struct ourselves:
glibc does not provide a wrapper for getdents(); call getdents() using syscall(2). In that case you will need to define the linux_dirent or linux_dirent64 structure yourself.
And for nolibc, a getdents64() with linux_dirent64 struct (with int64_t offset) is provided, there is either no getdents() currently.
This patch aims to let nolibc-test at least compile for musl and therefore we can easily check the new tests for musl, glibc and nolibc together.
Ok, I see. Your current approach should be fine then.
For the 64bit offset related stuff, we'd better delay it in another patchset (part of full rv32 support), which will convert the off_t to 64bit by default.
Right, makes sense.
ARnd
musl limits the fast signed int in 32bit, but glibc and nolibc don't, to let such test cases work on musl, let's provide the type based SINT_MAX_OF_TYPE(type) and SINT_MIN_OF_TYPE(type).
Suggested-by: Thomas Weißschuh linux@weissschuh.net Link: https://lore.kernel.org/lkml/bc635c4f-67fe-4e86-bfdf-bcb4879b928d@t-8ch.de/ Reviewed-by: Thomas Weißschuh linux@weissschuh.net Signed-off-by: Zhangjin Wu falcon@tinylab.org --- tools/testing/selftests/nolibc/nolibc-test.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-)
diff --git a/tools/testing/selftests/nolibc/nolibc-test.c b/tools/testing/selftests/nolibc/nolibc-test.c index d9644244fc95..290b43c7dc72 100644 --- a/tools/testing/selftests/nolibc/nolibc-test.c +++ b/tools/testing/selftests/nolibc/nolibc-test.c @@ -41,6 +41,10 @@ #endif #endif
+/* for the type of int_fast16_t and int_fast32_t, musl differs from glibc and nolibc */ +#define SINT_MAX_OF_TYPE(type) (((type)1 << (sizeof(type) * 8 - 2)) - (type)1 + ((type)1 << (sizeof(type) * 8 - 2))) +#define SINT_MIN_OF_TYPE(type) (-SINT_MAX_OF_TYPE(type) - 1) + /* will be used by nolibc by getenv() */ char **environ;
@@ -818,11 +822,11 @@ int run_stdlib(int min, int max) CASE_TEST(limit_int_fast8_max); EXPECT_EQ(1, INT_FAST8_MAX, (int_fast8_t) 0x7f); break; CASE_TEST(limit_int_fast8_min); EXPECT_EQ(1, INT_FAST8_MIN, (int_fast8_t) 0x80); break; CASE_TEST(limit_uint_fast8_max); EXPECT_EQ(1, UINT_FAST8_MAX, (uint_fast8_t) 0xff); break; - CASE_TEST(limit_int_fast16_min); EXPECT_EQ(1, INT_FAST16_MIN, (int_fast16_t) INTPTR_MIN); break; - CASE_TEST(limit_int_fast16_max); EXPECT_EQ(1, INT_FAST16_MAX, (int_fast16_t) INTPTR_MAX); break; + CASE_TEST(limit_int_fast16_min); EXPECT_EQ(1, INT_FAST16_MIN, (int_fast16_t) SINT_MIN_OF_TYPE(int_fast16_t)); break; + CASE_TEST(limit_int_fast16_max); EXPECT_EQ(1, INT_FAST16_MAX, (int_fast16_t) SINT_MAX_OF_TYPE(int_fast16_t)); break; CASE_TEST(limit_uint_fast16_max); EXPECT_EQ(1, UINT_FAST16_MAX, (uint_fast16_t) UINTPTR_MAX); break; - CASE_TEST(limit_int_fast32_min); EXPECT_EQ(1, INT_FAST32_MIN, (int_fast32_t) INTPTR_MIN); break; - CASE_TEST(limit_int_fast32_max); EXPECT_EQ(1, INT_FAST32_MAX, (int_fast32_t) INTPTR_MAX); break; + CASE_TEST(limit_int_fast32_min); EXPECT_EQ(1, INT_FAST32_MIN, (int_fast32_t) SINT_MIN_OF_TYPE(int_fast32_t)); break; + CASE_TEST(limit_int_fast32_max); EXPECT_EQ(1, INT_FAST32_MAX, (int_fast32_t) SINT_MAX_OF_TYPE(int_fast32_t)); break; CASE_TEST(limit_uint_fast32_max); EXPECT_EQ(1, UINT_FAST32_MAX, (uint_fast32_t) UINTPTR_MAX); break; CASE_TEST(limit_int_fast64_min); EXPECT_EQ(1, INT_FAST64_MIN, (int_fast64_t) INT64_MIN); break; CASE_TEST(limit_int_fast64_max); EXPECT_EQ(1, INT_FAST64_MAX, (int_fast64_t) INT64_MAX); break;
kernel parameters allow pass two types of strings, one type is like 'noapic', another type is like 'panic=5', the first type is passed as arguments of the init program, the second type is passed as environment variables of the init program.
when users pass kernel parameters like this:
noapic NOLIBC_TEST=syscall
our nolibc-test program will use the test setting from argv[1] and ignore the one from NOLIBC_TEST environment variable, and at last, it will print the following line and ignore the whole test setting.
Ignoring unknown test name 'noapic'
reversing the parsing order does solve the above issue:
test = getenv("NOLIBC_TEST"); if (test) test = argv[1];
but it still doesn't work with such kernel parameters (without NOLIBC_TEST environment variable):
noapic FOO=bar
To support all of the potential kernel parameters, let's verify the test setting from both of argv[1] and NOLIBC_TEST environment variable.
Reviewed-by: Thomas Weißschuh linux@weissschuh.net Signed-off-by: Zhangjin Wu falcon@tinylab.org --- tools/testing/selftests/nolibc/nolibc-test.c | 33 ++++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-)
diff --git a/tools/testing/selftests/nolibc/nolibc-test.c b/tools/testing/selftests/nolibc/nolibc-test.c index 290b43c7dc72..94801961740a 100644 --- a/tools/testing/selftests/nolibc/nolibc-test.c +++ b/tools/testing/selftests/nolibc/nolibc-test.c @@ -1062,6 +1062,35 @@ static const struct test test_names[] = { { 0 } };
+int is_setting_valid(char *test) +{ + int idx, len, test_len, valid = 0; + char delimiter; + + if (!test) + return valid; + + test_len = strlen(test); + + for (idx = 0; test_names[idx].name; idx++) { + len = strlen(test_names[idx].name); + if (test_len < len) + continue; + + if (strncmp(test, test_names[idx].name, len) != 0) + continue; + + delimiter = test[len]; + if (delimiter != ':' && delimiter != ',' && delimiter != '\0') + continue; + + valid = 1; + break; + } + + return valid; +} + int main(int argc, char **argv, char **envp) { int min = 0; @@ -1087,10 +1116,10 @@ int main(int argc, char **argv, char **envp) * syscall:5-15[:.*],stdlib:8-10 */ test = argv[1]; - if (!test) + if (!is_setting_valid(test)) test = getenv("NOLIBC_TEST");
- if (test) { + if (is_setting_valid(test)) { char *comma, *colon, *dash, *value;
do {
'/proc/self/' is a good path which doesn't have stale time info but it is only available for CONFIG_PROC_FS=y.
When CONFIG_PROC_FS=n, use '/init' for initramfs and '/' for the others.
Reviewed-by: Thomas Weißschuh linux@weissschuh.net Signed-off-by: Zhangjin Wu falcon@tinylab.org --- tools/testing/selftests/nolibc/nolibc-test.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/tools/testing/selftests/nolibc/nolibc-test.c b/tools/testing/selftests/nolibc/nolibc-test.c index 94801961740a..3e2e808e06c2 100644 --- a/tools/testing/selftests/nolibc/nolibc-test.c +++ b/tools/testing/selftests/nolibc/nolibc-test.c @@ -582,7 +582,7 @@ static int test_stat_timestamps(void) if (sizeof(st.st_atim.tv_sec) != sizeof(st.st_atime)) return 1;
- if (stat("/proc/self/", &st)) + if (stat("/proc/self/", &st) && stat("/init", &st) && stat("/", &st)) return 1;
if (st.st_atim.tv_sec != st.st_atime || st.st_atim.tv_nsec > 1000000000)
Use '/init' instead of '/proc/self/exe' for CONFIG_PROC_FS=n.
If '/init' is not there, add ENOENT into the errno check list.
Signed-off-by: Zhangjin Wu falcon@tinylab.org --- tools/testing/selftests/nolibc/nolibc-test.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/tools/testing/selftests/nolibc/nolibc-test.c b/tools/testing/selftests/nolibc/nolibc-test.c index 3e2e808e06c2..a7a4310dec3e 100644 --- a/tools/testing/selftests/nolibc/nolibc-test.c +++ b/tools/testing/selftests/nolibc/nolibc-test.c @@ -702,7 +702,7 @@ int run_syscall(int min, int max) CASE_TEST(chown_self); EXPECT_SYSER(proc, chown("/proc/self", 0, 0), -1, EPERM); break; CASE_TEST(chroot_root); EXPECT_SYSZR(euid0, chroot("/")); break; CASE_TEST(chroot_blah); EXPECT_SYSER(1, chroot("/proc/self/blah"), -1, ENOENT); break; - CASE_TEST(chroot_exe); EXPECT_SYSER(proc, chroot("/proc/self/exe"), -1, ENOTDIR); break; + CASE_TEST(chroot_exe); EXPECT_SYSER2(1, chroot(proc ? "/proc/self/exe" : "/init"), -1, ENOTDIR, ENOENT); break; CASE_TEST(close_m1); EXPECT_SYSER(1, close(-1), -1, EBADF); break; CASE_TEST(close_dup); EXPECT_SYSZR(1, close(dup(0))); break; CASE_TEST(dup_0); tmp = dup(0); EXPECT_SYSNE(1, tmp, -1); close(tmp); break;
For CONFIG_NET=n, there would be no /proc/self/net, so, use /proc/self/cmdline instead.
Reviewed-by: Thomas Weißschuh linux@weissschuh.net Signed-off-by: Zhangjin Wu falcon@tinylab.org --- tools/testing/selftests/nolibc/nolibc-test.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/tools/testing/selftests/nolibc/nolibc-test.c b/tools/testing/selftests/nolibc/nolibc-test.c index a7a4310dec3e..8269f900db76 100644 --- a/tools/testing/selftests/nolibc/nolibc-test.c +++ b/tools/testing/selftests/nolibc/nolibc-test.c @@ -723,7 +723,7 @@ int run_syscall(int min, int max) CASE_TEST(link_root1); EXPECT_SYSER(1, link("/", "/"), -1, EEXIST); break; CASE_TEST(link_blah); EXPECT_SYSER(1, link("/proc/self/blah", "/blah"), -1, ENOENT); break; CASE_TEST(link_dir); EXPECT_SYSER(euid0, link("/", "/blah"), -1, EPERM); break; - CASE_TEST(link_cross); EXPECT_SYSER(proc, link("/proc/self/net", "/blah"), -1, EXDEV); break; + CASE_TEST(link_cross); EXPECT_SYSER(proc, link("/proc/self/cmdline", "/blah"), -1, EXDEV); break; CASE_TEST(lseek_m1); EXPECT_SYSER(1, lseek(-1, 0, SEEK_SET), -1, EBADF); break; CASE_TEST(lseek_0); EXPECT_SYSER(1, lseek(0, 0, SEEK_SET), -1, ESPIPE); break; CASE_TEST(mkdir_root); EXPECT_SYSER(1, mkdir("/", 0755), -1, EEXIST); break;
a reverse operation of mkdir() is meaningful, add rmdir() here.
required by nolibc-test to remove /proc while CONFIG_PROC_FS is not enabled.
Reviewed-by: Thomas Weißschuh linux@weissschuh.net Signed-off-by: Zhangjin Wu falcon@tinylab.org --- tools/include/nolibc/sys.h | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+)
diff --git a/tools/include/nolibc/sys.h b/tools/include/nolibc/sys.h index b6c33c40c037..7b052958e2ae 100644 --- a/tools/include/nolibc/sys.h +++ b/tools/include/nolibc/sys.h @@ -610,6 +610,28 @@ int mkdir(const char *path, mode_t mode) return __sysret(sys_mkdir(path, mode)); }
+/* + * int rmdir(const char *path); + */ + +static __attribute__((unused)) +int sys_rmdir(const char *path) +{ +#ifdef __NR_rmdir + return my_syscall1(__NR_rmdir, path); +#elif defined(__NR_unlinkat) + return my_syscall3(__NR_unlinkat, AT_FDCWD, path, AT_REMOVEDIR); +#else + return -ENOSYS; +#endif +} + +static __attribute__((unused)) +int rmdir(const char *path) +{ + return __sysret(sys_rmdir(path)); +} +
/* * int mknod(const char *path, mode_t mode, dev_t dev);
A new rmdir_blah test case is added to remove a non-existing /blah, which expects failure with ENOENT errno.
Reviewed-by: Thomas Weißschuh linux@weissschuh.net Signed-off-by: Zhangjin Wu falcon@tinylab.org --- tools/testing/selftests/nolibc/nolibc-test.c | 1 + 1 file changed, 1 insertion(+)
diff --git a/tools/testing/selftests/nolibc/nolibc-test.c b/tools/testing/selftests/nolibc/nolibc-test.c index 8269f900db76..34a47c512b97 100644 --- a/tools/testing/selftests/nolibc/nolibc-test.c +++ b/tools/testing/selftests/nolibc/nolibc-test.c @@ -737,6 +737,7 @@ int run_syscall(int min, int max) CASE_TEST(poll_fault); EXPECT_SYSER(1, poll((void *)1, 1, 0), -1, EFAULT); break; CASE_TEST(prctl); EXPECT_SYSER(1, prctl(PR_SET_NAME, (unsigned long)NULL, 0, 0, 0), -1, EFAULT); break; CASE_TEST(read_badf); EXPECT_SYSER(1, read(-1, &tmp, 1), -1, EBADF); break; + CASE_TEST(rmdir_blah); EXPECT_SYSER(1, rmdir("/blah"), -1, ENOENT); break; CASE_TEST(sched_yield); EXPECT_SYSZR(1, sched_yield()); break; CASE_TEST(select_null); EXPECT_SYSZR(1, ({ struct timeval tv = { 0 }; select(0, NULL, NULL, NULL, &tv); })); break; CASE_TEST(select_stdout); EXPECT_SYSNE(1, ({ fd_set fds; FD_ZERO(&fds); FD_SET(1, &fds); select(2, NULL, &fds, NULL, NULL); }), -1); break;
For CONFIG_PROC_FS=n, the /proc is not mountable, but the /proc directory has been created in the prepare() stage whenever /proc is there or not.
so, the checking of /proc in the run_syscall() stage will be always true and at last it will fail all of the procfs dependent test cases, which deviates from the 'cond' check design of the EXPECT_xx macros, without procfs, these test cases should be skipped instead of failed.
To solve this issue, one method is checking /proc/self instead of /proc, another method is removing the /proc directory completely for CONFIG_PROC_FS=n, we apply the second method to avoid misleading the users.
Reviewed-by: Thomas Weißschuh linux@weissschuh.net Signed-off-by: Zhangjin Wu falcon@tinylab.org --- tools/testing/selftests/nolibc/nolibc-test.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-)
diff --git a/tools/testing/selftests/nolibc/nolibc-test.c b/tools/testing/selftests/nolibc/nolibc-test.c index 34a47c512b97..b7ea95dad0fb 100644 --- a/tools/testing/selftests/nolibc/nolibc-test.c +++ b/tools/testing/selftests/nolibc/nolibc-test.c @@ -1046,8 +1046,11 @@ int prepare(void)
/* try to mount /proc if not mounted. Silently fail otherwise */ if (stat("/proc/.", &stat_buf) == 0 || mkdir("/proc", 0755) == 0) { - if (stat("/proc/self", &stat_buf) != 0) - mount("/proc", "/proc", "proc", 0, 0); + if (stat("/proc/self", &stat_buf) != 0) { + /* If not mountable, remove /proc completely to avoid misuse */ + if (mount("none", "/proc", "proc", 0, 0) != 0) + rmdir("/proc"); + } }
return 0;
create a /tmp directory and mount tmpfs there, if tmpfs is not mountable, use ramfs as tmpfs.
tmpfs will be used instead of procfs for some tests.
Reviewed-by: Thomas Weißschuh linux@weissschuh.net Signed-off-by: Zhangjin Wu falcon@tinylab.org --- tools/testing/selftests/nolibc/nolibc-test.c | 4 ++++ 1 file changed, 4 insertions(+)
diff --git a/tools/testing/selftests/nolibc/nolibc-test.c b/tools/testing/selftests/nolibc/nolibc-test.c index b7ea95dad0fb..906b70ddec79 100644 --- a/tools/testing/selftests/nolibc/nolibc-test.c +++ b/tools/testing/selftests/nolibc/nolibc-test.c @@ -1053,6 +1053,10 @@ int prepare(void) } }
+ /* try to mount /tmp if not mounted, if not mountable, use ramfs as tmpfs */ + mkdir("/tmp", 0755); + mount("none", "/tmp", "tmpfs", 0, 0); + return 0; }
create a temp directory to test chmod with tmpfs.
Reviewed-by: Thomas Weißschuh linux@weissschuh.net Signed-off-by: Zhangjin Wu falcon@tinylab.org --- tools/testing/selftests/nolibc/nolibc-test.c | 1 + 1 file changed, 1 insertion(+)
diff --git a/tools/testing/selftests/nolibc/nolibc-test.c b/tools/testing/selftests/nolibc/nolibc-test.c index 906b70ddec79..be2a18cd5bd5 100644 --- a/tools/testing/selftests/nolibc/nolibc-test.c +++ b/tools/testing/selftests/nolibc/nolibc-test.c @@ -699,6 +699,7 @@ int run_syscall(int min, int max) CASE_TEST(chdir_dot); EXPECT_SYSZR(1, chdir(".")); break; CASE_TEST(chdir_blah); EXPECT_SYSER(1, chdir("/blah"), -1, ENOENT); break; CASE_TEST(chmod_self); EXPECT_SYSER(proc, chmod("/proc/self", 0555), -1, EPERM); break; + CASE_TEST(chmod_tmpdir); mkdir("/tmp/blah", 0755); EXPECT_SYSZR(1, chmod("/tmp/blah", 0555)); rmdir("/tmp/blah"); break; CASE_TEST(chown_self); EXPECT_SYSER(proc, chown("/proc/self", 0, 0), -1, EPERM); break; CASE_TEST(chroot_root); EXPECT_SYSZR(euid0, chroot("/")); break; CASE_TEST(chroot_blah); EXPECT_SYSER(1, chroot("/proc/self/blah"), -1, ENOENT); break;
The vfprintf test case require to open a temporary file to write, the old memfd_create() method is perfect but has strong dependency on MEMFD_CREATE and also TMPFS or HUGETLBFS (see fs/Kconfig):
config MEMFD_CREATE def_bool TMPFS || HUGETLBFS
And from v6.2, MFD_NOEXEC_SEAL must be passed for the non-executable memfd, otherwise, The kernel warning will be output to the test result like this:
Running test 'vfprintf' 0 emptymemfd_create() without MFD_EXEC nor MFD_NOEXEC_SEAL, pid=1 'init' "" = "" [OK]
To avoid such warning and also to remove the MEMFD_CREATE dependency, let's open a file from tmpfs directly.
The /tmp directory is used to detect the existing of tmpfs, if not there, skip instead of fail.
And further, for pid == 1, the initramfs is loaded as ramfs, which can be used as tmpfs, so, it is able to further remove TMPFS dependency too.
Suggested-by: Thomas Weißschuh linux@weissschuh.net Link: https://lore.kernel.org/lkml/9ad51430-b7c0-47dc-80af-20c86539498d@t-8ch.de Reviewed-by: Thomas Weißschuh linux@weissschuh.net Signed-off-by: Zhangjin Wu falcon@tinylab.org --- tools/testing/selftests/nolibc/nolibc-test.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/tools/testing/selftests/nolibc/nolibc-test.c b/tools/testing/selftests/nolibc/nolibc-test.c index be2a18cd5bd5..714252518595 100644 --- a/tools/testing/selftests/nolibc/nolibc-test.c +++ b/tools/testing/selftests/nolibc/nolibc-test.c @@ -868,10 +868,10 @@ static int expect_vfprintf(int llen, size_t c, const char *expected, const char FILE *memfile; va_list args;
- fd = memfd_create("vfprintf", 0); + fd = open("/tmp", O_TMPFILE | O_EXCL | O_RDWR, 0600); if (fd == -1) { - pad_spc(llen, 64, "[FAIL]\n"); - return 1; + pad_spc(llen, 64, "[SKIPPED]\n"); + return 0; }
memfile = fdopen(fd, "w+");
linux-kselftest-mirror@lists.linaro.org