GCC Code Coverage Report


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