GCC Code Coverage Report


Directory: cvmfs/
File: cvmfs/util/namespace.cc
Date: 2025-06-22 02:36:02
Exec Total Coverage
Lines: 7 94 7.4%
Branches: 5 103 4.9%

Line Branch Exec Source
1 /**
2 * This file is part of the CernVM File System.
3 */
4
5
6 #include "namespace.h"
7
8 #include <fcntl.h>
9 #include <signal.h>
10 #ifndef __APPLE__
11 #include <sched.h>
12 #include <sys/mount.h>
13 #endif
14 #include <sys/wait.h>
15
16 #include <cstring>
17
18 #include "util/posix.h"
19 #include "util/string.h"
20
21 // Might otherwise not compile on older Linux kernels or glibc versions
22 #ifndef CLONE_NEWUSER
23 #define CLONE_NEWUSER 0x10000000
24 #endif
25 #ifndef CLONE_NEWPID
26 #define CLONE_NEWPID 0x20000000
27 #endif
28 #ifndef MS_REC
29 #define MS_REC 0x4000
30 #endif
31
32 #ifndef __APPLE__
33 #define CVMFS_HAS_UNSHARE 1
34 #ifdef __GLIBC_MINOR__
35 #if __GLIBC_MINOR__ < 4
36 #undef CVMFS_HAS_UNSHARE
37 #endif
38 #endif
39 #endif
40
41
42 60 int CheckNamespaceFeatures() {
43 #ifdef __APPLE__
44 return 0;
45 #else
46 60 int result = kNsFeatureMount; // available since kernel 2.4
47
3/6
✓ Branch 2 taken 60 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 60 times.
✗ Branch 6 not taken.
✓ Branch 9 taken 60 times.
✗ Branch 10 not taken.
60 if (SymlinkExists("/proc/self/ns/pid"))
48 60 result |= kNsFeaturePid;
49
1/2
✓ Branch 1 taken 60 times.
✗ Branch 2 not taken.
60 const int fd = open("/proc/sys/kernel/unprivileged_userns_clone", O_RDONLY);
50
1/2
✓ Branch 0 taken 60 times.
✗ Branch 1 not taken.
60 if (fd < 0)
51 60 return result;
52 result |= kNsFeatureUserAvailable;
53 char enabled = 0;
54 SafeRead(fd, &enabled, 1);
55 close(fd);
56 return (enabled != '1') ? result : (result | kNsFeatureUserEnabled);
57 #endif
58 }
59
60
61 NamespaceFailures CreateUserNamespace(uid_t map_uid_to, gid_t map_gid_to) {
62 #ifdef CVMFS_HAS_UNSHARE
63 const std::string uid_str = StringifyInt(geteuid());
64 const std::string gid_str = StringifyInt(getegid());
65
66 const int rvi = unshare(CLONE_NEWUSER);
67 if (rvi != 0)
68 return kFailNsUnshare;
69
70 std::string uid_map = StringifyInt(map_uid_to) + " " + uid_str + " 1";
71 std::string gid_map = StringifyInt(map_gid_to) + " " + gid_str + " 1";
72
73 int fd;
74 ssize_t nbytes;
75 fd = open("/proc/self/setgroups", O_WRONLY);
76 if (fd < 0)
77 return kFailNsSetgroupsOpen;
78 nbytes = write(fd, "deny", 4);
79 close(fd);
80 if (nbytes != 4)
81 return kFailNsSetgroupsWrite;
82
83 fd = open("/proc/self/uid_map", O_WRONLY);
84 if (fd < 0)
85 return kFailNsMapUidOpen;
86 nbytes = write(fd, uid_map.data(), uid_map.length());
87 close(fd);
88 if (nbytes != static_cast<ssize_t>(uid_map.length()))
89 return kFailNsMapUidWrite;
90
91 fd = open("/proc/self/gid_map", O_WRONLY);
92 if (fd < 0)
93 return kFailNsMapGidOpen;
94 nbytes = write(fd, gid_map.data(), gid_map.length());
95 close(fd);
96 if (nbytes != static_cast<ssize_t>(gid_map.length()))
97 return kFailNsMapGidWrite;
98
99 return kFailNsOk;
100 #else
101 return kFailNsUnsuppored;
102 #endif
103 }
104
105
106 bool BindMount(const std::string &from, const std::string &to) {
107 #ifdef __APPLE__
108 return false;
109 #else
110 const int rvi = mount(from.c_str(), to.c_str(), "", MS_BIND | MS_REC, NULL);
111 return rvi == 0;
112 #endif
113 }
114
115
116 bool ProcMount(const std::string &to) {
117 #ifdef __APPLE__
118 return false;
119 #else
120 const int rvi = mount("proc", to.c_str(), "proc", 0, NULL);
121 return rvi == 0;
122 #endif
123 }
124
125
126 bool CreateMountNamespace() {
127 #ifdef CVMFS_HAS_UNSHARE
128 const std::string cwd = GetCurrentWorkingDirectory();
129
130 int rvi = unshare(CLONE_NEWNS);
131 if (rvi != 0)
132 return false;
133
134 rvi = chdir(cwd.c_str());
135 return rvi == 0;
136 #else
137 return false;
138 #endif
139 }
140
141
142 #ifdef CVMFS_HAS_UNSHARE
143 namespace {
144
145 static void Reaper(int /*sig*/, siginfo_t * /*siginfo*/, void * /*context*/) {
146 while (true) {
147 const pid_t retval = waitpid(-1, NULL, WNOHANG);
148 if (retval <= 0)
149 return;
150 }
151 }
152
153 } // anonymous namespace
154 #endif
155
156
157 /**
158 * The fd_parent file descriptor, if passed, is the read end of a pipe whose
159 * write end is connected to the parent process. This gives the namespace's
160 * init process a means to know its pid in the context of the parent namespace.
161 */
162 bool CreatePidNamespace(int *fd_parent) {
163 #ifdef CVMFS_HAS_UNSHARE
164 int rvi = unshare(CLONE_NEWPID);
165 if (rvi != 0)
166 return false;
167
168 int pipe_parent[2];
169 MakePipe(pipe_parent);
170
171 int max_fd;
172 int status;
173 pid_t pid = fork();
174 switch (pid) {
175 case -1:
176 abort();
177 case 0:
178 // New init process
179 break;
180 default:
181 // Parent, wait for the namespace to exit
182
183 // Close all file descriptors
184 max_fd = static_cast<int>(sysconf(_SC_OPEN_MAX));
185 for (int fd = 0; fd < max_fd; fd++) {
186 if (fd != pipe_parent[1])
187 close(fd);
188 }
189
190 pid_t parent_pid = getpid();
191 SafeWrite(pipe_parent[1], &parent_pid, sizeof(parent_pid));
192 SafeWrite(pipe_parent[1], &pid, sizeof(pid));
193
194 rvi = waitpid(pid, &status, 0);
195 if (rvi >= 0) {
196 if (WIFEXITED(status))
197 exit(WEXITSTATUS(status));
198 }
199 exit(127);
200 }
201 close(pipe_parent[1]);
202 if (fd_parent != NULL)
203 *fd_parent = pipe_parent[0];
204
205 // Note: only signals for which signal handlers are established can be sent
206 // by other processes of this pid namespace to the init process
207 struct sigaction sa;
208 memset(&sa, 0, sizeof(sa));
209 sa.sa_sigaction = Reaper;
210 sa.sa_flags = SA_SIGINFO;
211 sigfillset(&sa.sa_mask);
212 rvi = sigaction(SIGCHLD, &sa, NULL);
213 assert(rvi == 0);
214
215 rvi = mount("", "/proc", "proc", 0, NULL);
216 return rvi == 0;
217 #else
218 return false;
219 #endif
220 }
221