1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3 * Author: Aleksa Sarai <cyphar@cyphar.com>
4 * Copyright (C) 2025 SUSE LLC.
5 */
6
7 #include <assert.h>
8 #include <errno.h>
9 #include <fcntl.h>
10 #include <sched.h>
11 #include <unistd.h>
12 #include <sys/mount.h>
13
14 #include "kselftest_harness.h"
15
16 #define ASSERT_ERRNO(expected, _t, seen) \
17 __EXPECT(expected, #expected, \
18 ({__typeof__(seen) _tmp_seen = (seen); \
19 _tmp_seen >= 0 ? _tmp_seen : -errno; }), #seen, _t, 1)
20
21 #define ASSERT_ERRNO_EQ(expected, seen) \
22 ASSERT_ERRNO(expected, ==, seen)
23
24 #define ASSERT_SUCCESS(seen) \
25 ASSERT_ERRNO(0, <=, seen)
26
FIXTURE(ns)27 FIXTURE(ns)
28 {
29 int host_mntns;
30 };
31
FIXTURE_SETUP(ns)32 FIXTURE_SETUP(ns)
33 {
34 /* Stash the old mntns. */
35 self->host_mntns = open("/proc/self/ns/mnt", O_RDONLY|O_CLOEXEC);
36 ASSERT_SUCCESS(self->host_mntns);
37
38 /* Create a new mount namespace and make it private. */
39 ASSERT_SUCCESS(unshare(CLONE_NEWNS));
40 ASSERT_SUCCESS(mount(NULL, "/", NULL, MS_PRIVATE|MS_REC, NULL));
41 }
42
FIXTURE_TEARDOWN(ns)43 FIXTURE_TEARDOWN(ns)
44 {
45 ASSERT_SUCCESS(setns(self->host_mntns, CLONE_NEWNS));
46 ASSERT_SUCCESS(close(self->host_mntns));
47 }
48
TEST_F(ns,fscontext_log_enodata)49 TEST_F(ns, fscontext_log_enodata)
50 {
51 int fsfd = fsopen("tmpfs", FSOPEN_CLOEXEC);
52 ASSERT_SUCCESS(fsfd);
53
54 /* A brand new fscontext has no log entries. */
55 char buf[128] = {};
56 for (int i = 0; i < 16; i++)
57 ASSERT_ERRNO_EQ(-ENODATA, read(fsfd, buf, sizeof(buf)));
58
59 ASSERT_SUCCESS(close(fsfd));
60 }
61
TEST_F(ns,fscontext_log_errorfc)62 TEST_F(ns, fscontext_log_errorfc)
63 {
64 int fsfd = fsopen("tmpfs", FSOPEN_CLOEXEC);
65 ASSERT_SUCCESS(fsfd);
66
67 ASSERT_ERRNO_EQ(-EINVAL, fsconfig(fsfd, FSCONFIG_SET_STRING, "invalid-arg", "123", 0));
68
69 char buf[128] = {};
70 ASSERT_SUCCESS(read(fsfd, buf, sizeof(buf)));
71 EXPECT_STREQ("e tmpfs: Unknown parameter 'invalid-arg'\n", buf);
72
73 /* The message has been consumed. */
74 ASSERT_ERRNO_EQ(-ENODATA, read(fsfd, buf, sizeof(buf)));
75 ASSERT_SUCCESS(close(fsfd));
76 }
77
TEST_F(ns,fscontext_log_errorfc_after_fsmount)78 TEST_F(ns, fscontext_log_errorfc_after_fsmount)
79 {
80 int fsfd = fsopen("tmpfs", FSOPEN_CLOEXEC);
81 ASSERT_SUCCESS(fsfd);
82
83 ASSERT_ERRNO_EQ(-EINVAL, fsconfig(fsfd, FSCONFIG_SET_STRING, "invalid-arg", "123", 0));
84
85 ASSERT_SUCCESS(fsconfig(fsfd, FSCONFIG_CMD_CREATE, NULL, NULL, 0));
86 int mfd = fsmount(fsfd, FSMOUNT_CLOEXEC, MOUNT_ATTR_NOEXEC | MOUNT_ATTR_NOSUID);
87 ASSERT_SUCCESS(mfd);
88 ASSERT_SUCCESS(move_mount(mfd, "", AT_FDCWD, "/tmp", MOVE_MOUNT_F_EMPTY_PATH));
89
90 /*
91 * The fscontext log should still contain data even after
92 * FSCONFIG_CMD_CREATE and fsmount().
93 */
94 char buf[128] = {};
95 ASSERT_SUCCESS(read(fsfd, buf, sizeof(buf)));
96 EXPECT_STREQ("e tmpfs: Unknown parameter 'invalid-arg'\n", buf);
97
98 /* The message has been consumed. */
99 ASSERT_ERRNO_EQ(-ENODATA, read(fsfd, buf, sizeof(buf)));
100 ASSERT_SUCCESS(close(fsfd));
101 }
102
TEST_F(ns,fscontext_log_emsgsize)103 TEST_F(ns, fscontext_log_emsgsize)
104 {
105 int fsfd = fsopen("tmpfs", FSOPEN_CLOEXEC);
106 ASSERT_SUCCESS(fsfd);
107
108 ASSERT_ERRNO_EQ(-EINVAL, fsconfig(fsfd, FSCONFIG_SET_STRING, "invalid-arg", "123", 0));
109
110 char buf[128] = {};
111 /*
112 * Attempting to read a message with too small a buffer should not
113 * result in the message getting consumed.
114 */
115 ASSERT_ERRNO_EQ(-EMSGSIZE, read(fsfd, buf, 0));
116 ASSERT_ERRNO_EQ(-EMSGSIZE, read(fsfd, buf, 1));
117 for (int i = 0; i < 16; i++)
118 ASSERT_ERRNO_EQ(-EMSGSIZE, read(fsfd, buf, 16));
119
120 ASSERT_SUCCESS(read(fsfd, buf, sizeof(buf)));
121 EXPECT_STREQ("e tmpfs: Unknown parameter 'invalid-arg'\n", buf);
122
123 /* The message has been consumed. */
124 ASSERT_ERRNO_EQ(-ENODATA, read(fsfd, buf, sizeof(buf)));
125 ASSERT_SUCCESS(close(fsfd));
126 }
127
128 TEST_HARNESS_MAIN
129