GCC Code Coverage Report


Directory: cvmfs/
File: cvmfs/util/namespace.cc
Date: 2024-04-28 02:33:07
Exec Total Coverage
Lines: 6 86 7.0%
Branches: 5 103 4.9%

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