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