• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

systemd / systemd / 29790165204

20 Jul 2026 10:30PM UTC coverage: 72.837% (-0.05%) from 72.889%
29790165204

push

github

yuwata
hwdb: classify PlayStation controller audio as controller form-factor

Add Sony PlayStation controller entries to 70-sound-card.hwdb so that their
ALSA sound devices are tagged with SOUND_FORM_FACTOR=controller:
 - DualSense (054c:0ce6)
 - DualSense Edge (054c:0df2)
 - DualShock 4 CUH-ZCT1x (054c:05c4)
 - DualShock 4 CUH-ZCT2x (054c:09cc)

These controllers expose USB audio but are neither headsets nor speakers.
Pinning them in the hwdb ensures they are identified correctly before any
fallback matching occurs.

345467 of 474302 relevant lines covered (72.84%)

1388252.76 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

79.96
/src/basic/namespace-util.c
1
/* SPDX-License-Identifier: LGPL-2.1-or-later */
2

3
#include <fcntl.h>
4
#include <linux/magic.h>
5
#include <linux/nsfs.h>
6
#include <linux/sockios.h>
7
#include <sched.h>
8
#include <sys/ioctl.h>
9
#include <sys/mount.h>
10
#include <unistd.h>
11

12
#include "capability-util.h"
13
#include "dlfcn-util.h"
14
#include "errno-util.h"
15
#include "fd-util.h"
16
#include "fileio.h"
17
#include "log.h"
18
#include "mountpoint-util.h"
19
#include "namespace-util.h"
20
#include "parse-util.h"
21
#include "pidfd-util.h"
22
#include "pidref.h"
23
#include "process-util.h"
24
#include "stat-util.h"
25
#include "stdio-util.h"
26
#include "uid-range.h"
27
#include "unaligned.h"
28
#include "user-util.h"
29

30
const struct namespace_info namespace_info[_NAMESPACE_TYPE_MAX + 1] = {
31
        [NAMESPACE_CGROUP] =  { "cgroup", "ns/cgroup", CLONE_NEWCGROUP, PIDFD_GET_CGROUP_NAMESPACE, PROC_CGROUP_INIT_INO },
32
        [NAMESPACE_IPC]    =  { "ipc",    "ns/ipc",    CLONE_NEWIPC,    PIDFD_GET_IPC_NAMESPACE,    PROC_IPC_INIT_INO    },
33
        [NAMESPACE_NET]    =  { "net",    "ns/net",    CLONE_NEWNET,    PIDFD_GET_NET_NAMESPACE,    0                    },
34
        /* So, the mount namespace flag is called CLONE_NEWNS for historical
35
         * reasons. Let's expose it here under a more explanatory name: "mnt".
36
         * This is in-line with how the kernel exposes namespaces in /proc/$PID/ns. */
37
        [NAMESPACE_MOUNT]  =  { "mnt",    "ns/mnt",    CLONE_NEWNS,     PIDFD_GET_MNT_NAMESPACE,    0                    },
38
        [NAMESPACE_PID]    =  { "pid",    "ns/pid",    CLONE_NEWPID,    PIDFD_GET_PID_NAMESPACE,    PROC_PID_INIT_INO    },
39
        [NAMESPACE_USER]   =  { "user",   "ns/user",   CLONE_NEWUSER,   PIDFD_GET_USER_NAMESPACE,   PROC_USER_INIT_INO   },
40
        [NAMESPACE_UTS]    =  { "uts",    "ns/uts",    CLONE_NEWUTS,    PIDFD_GET_UTS_NAMESPACE,    PROC_UTS_INIT_INO    },
41
        [NAMESPACE_TIME]   =  { "time",   "ns/time",   CLONE_NEWTIME,   PIDFD_GET_TIME_NAMESPACE,   PROC_TIME_INIT_INO   },
42
        {}, /* Allow callers to iterate over the array without using _NAMESPACE_TYPE_MAX. */
43
};
44

45
#define pid_namespace_path(pid, type) procfs_file_alloca(pid, namespace_info[type].proc_path)
46

47
NamespaceType clone_flag_to_namespace_type(unsigned long clone_flag) {
1,777✔
48
        for (NamespaceType t = 0; t < _NAMESPACE_TYPE_MAX; t++)
9,792✔
49
                if (((namespace_info[t].clone_flag ^ clone_flag) & (CLONE_NEWCGROUP|CLONE_NEWIPC|CLONE_NEWNET|CLONE_NEWNS|CLONE_NEWPID|CLONE_NEWUSER|CLONE_NEWUTS|CLONE_NEWTIME)) == 0)
9,792✔
50
                        return t;
51

52
        return _NAMESPACE_TYPE_INVALID;
53
}
54

55
bool namespace_type_supported(NamespaceType type) {
1,962✔
56
        assert(type >= 0 && type < _NAMESPACE_TYPE_MAX);
1,962✔
57

58
        const char *p = pid_namespace_path(0, type);
9,810✔
59
        return access(p, F_OK) >= 0;
1,962✔
60
}
61

62
static int pidref_namespace_open_by_type_internal(const PidRef *pidref, NamespaceType type, bool *need_verify) {
8,493✔
63
        int r;
8,493✔
64

65
        assert(pidref_is_set(pidref));
8,493✔
66
        assert(type >= 0 && type < _NAMESPACE_TYPE_MAX);
8,493✔
67

68
        if (pidref_is_remote(pidref))
8,493✔
69
                return -EREMOTE;
8,493✔
70

71
        if (pidref->fd >= 0) {
8,493✔
72
                r = pidfd_get_namespace(pidref->fd, namespace_info[type].pidfd_get_ns_ioctl_cmd);
8,488✔
73
                if (r == -ENOPKG)
8,488✔
74
                        return log_debug_errno(
×
75
                                        r,
76
                                        "Cannot open %s namespace for PID "PID_FMT" as the namespace type is not supported by the kernel",
77
                                        namespace_info[type].proc_name, pidref->pid);
78
                if (!ERRNO_IS_NEG_NOT_SUPPORTED(r))
8,488✔
79
                        return r;
80
        }
81

82
        if (need_verify) /* The caller shall call pidref_verify() later */
5✔
83
                *need_verify = true;
×
84

85
        _cleanup_close_ int nsfd = -EBADF;
8,493✔
86
        const char *p;
5✔
87

88
        p = pid_namespace_path(pidref->pid, type);
5✔
89
        nsfd = RET_NERRNO(open(p, O_RDONLY|O_NOCTTY|O_CLOEXEC));
5✔
90
        if (nsfd == -ENOENT) {
×
91
                r = proc_mounted();
×
92
                if (r == 0)
×
93
                        /* /proc/ is not available or not set up properly, we're most likely in some chroot environment. */
94
                        return log_debug_errno(
×
95
                                        SYNTHETIC_ERRNO(ENOSYS),
96
                                        "Cannot open %s namespace for PID "PID_FMT" as /proc is not mounted",
97
                                        namespace_info[type].proc_name, pidref->pid);
98
                if (r > 0)
×
99
                        /* If /proc/ is definitely around then this means the namespace type is not supported */
100
                        return log_debug_errno(
×
101
                                        SYNTHETIC_ERRNO(ENOPKG),
102
                                        "Cannot open %s namespace for PID "PID_FMT" via /proc as the namespace type is not supported by the kernel",
103
                                        namespace_info[type].proc_name, pidref->pid);
104

105
                /* can't determine? then propagate original error */
106
        }
107
        if (nsfd < 0)
5✔
108
                return nsfd;
109

110
        if (!need_verify) { /* Otherwise we verify on our own */
5✔
111
                r = pidref_verify(pidref);
5✔
112
                if (r < 0)
5✔
113
                        return r;
×
114
        }
115

116
        return TAKE_FD(nsfd);
117
}
118

119
int pidref_namespace_open_by_type(const PidRef *pidref, NamespaceType type) {
8,162✔
120
        return pidref_namespace_open_by_type_internal(pidref, type, NULL);
8,162✔
121
}
122

123
int namespace_open_by_type(NamespaceType type) {
4,531✔
124
        _cleanup_(pidref_done) PidRef self = PIDREF_NULL;
4,531✔
125
        int r;
4,531✔
126

127
        assert(type >= 0 && type < _NAMESPACE_TYPE_MAX);
4,531✔
128

129
        r = pidref_set_self(&self);
4,531✔
130
        if (r < 0)
4,531✔
131
                return r;
132

133
        return pidref_namespace_open_by_type(&self, type);
4,531✔
134
}
135

136
int pidref_namespace_open(
164✔
137
                const PidRef *pidref,
138
                int *ret_pidns_fd,
139
                int *ret_mntns_fd,
140
                int *ret_netns_fd,
141
                int *ret_userns_fd,
142
                int *ret_root_fd) {
143

144
        _cleanup_close_ int pidns_fd = -EBADF, mntns_fd = -EBADF, netns_fd = -EBADF,
328✔
145
                userns_fd = -EBADF, root_fd = -EBADF;
328✔
146
        bool need_verify = false;
164✔
147
        int r;
164✔
148

149
        assert(pidref_is_set(pidref));
164✔
150

151
        if (pidref_is_remote(pidref))
328✔
152
                return -EREMOTE;
153

154
        if (ret_pidns_fd) {
164✔
155
                pidns_fd = pidref_namespace_open_by_type_internal(pidref, NAMESPACE_PID, &need_verify);
124✔
156
                if (pidns_fd < 0)
124✔
157
                        return pidns_fd;
158
        }
159

160
        if (ret_mntns_fd) {
164✔
161
                mntns_fd = pidref_namespace_open_by_type_internal(pidref, NAMESPACE_MOUNT, &need_verify);
126✔
162
                if (mntns_fd < 0)
126✔
163
                        return mntns_fd;
164
        }
165

166
        if (ret_netns_fd) {
164✔
167
                netns_fd = pidref_namespace_open_by_type_internal(pidref, NAMESPACE_NET, &need_verify);
41✔
168
                if (netns_fd < 0)
41✔
169
                        return netns_fd;
170
        }
171

172
        if (ret_userns_fd) {
164✔
173
                userns_fd = pidref_namespace_open_by_type_internal(pidref, NAMESPACE_USER, &need_verify);
40✔
174
                if (userns_fd < 0 && userns_fd != -ENOPKG)
40✔
175
                        return userns_fd;
176
        }
177

178
        if (ret_root_fd) {
164✔
179
                const char *root;
126✔
180

181
                root = procfs_file_alloca(pidref->pid, "root");
126✔
182
                root_fd = RET_NERRNO(open(root, O_CLOEXEC|O_DIRECTORY));
126✔
183
                if (root_fd == -ENOENT && proc_mounted() == 0)
×
184
                        return -ENOSYS;
185
                if (root_fd < 0)
126✔
186
                        return root_fd;
187

188
                need_verify = true;
126✔
189
        }
190

191
        if (need_verify) {
164✔
192
                r = pidref_verify(pidref);
126✔
193
                if (r < 0)
126✔
194
                        return r;
195
        }
196

197
        if (ret_pidns_fd)
164✔
198
                *ret_pidns_fd = TAKE_FD(pidns_fd);
124✔
199

200
        if (ret_mntns_fd)
164✔
201
                *ret_mntns_fd = TAKE_FD(mntns_fd);
126✔
202

203
        if (ret_netns_fd)
164✔
204
                *ret_netns_fd = TAKE_FD(netns_fd);
41✔
205

206
        if (ret_userns_fd)
164✔
207
                *ret_userns_fd = TAKE_FD(userns_fd);
40✔
208

209
        if (ret_root_fd)
164✔
210
                *ret_root_fd = TAKE_FD(root_fd);
126✔
211

212
        return 0;
213
}
214

215
int namespace_open(
36✔
216
                pid_t pid,
217
                int *ret_pidns_fd,
218
                int *ret_mntns_fd,
219
                int *ret_netns_fd,
220
                int *ret_userns_fd,
221
                int *ret_root_fd) {
222

223
        _cleanup_(pidref_done) PidRef pidref = PIDREF_NULL;
36✔
224
        int r;
36✔
225

226
        r = pidref_set_pid(&pidref, pid);
36✔
227
        if (r < 0)
36✔
228
                return r;
229

230
        return pidref_namespace_open(&pidref, ret_pidns_fd, ret_mntns_fd, ret_netns_fd, ret_userns_fd, ret_root_fd);
36✔
231
}
232

233
int namespace_enter(int pidns_fd, int mntns_fd, int netns_fd, int userns_fd, int root_fd) {
253✔
234
        int r;
253✔
235

236
        /* Block dlopen() now, to avoid us inadvertently loading shared library from another namespace */
237
        block_dlopen();
253✔
238

239
         /* Join namespaces, but only if we're not part of them already. This is important if we don't
240
          * necessarily own the namespace in question, as kernel would unconditionally return EPERM otherwise. */
241

242
        if (pidns_fd >= 0) {
253✔
243
                r = is_our_namespace(pidns_fd, NAMESPACE_PID);
69✔
244
                if (r < 0)
69✔
245
                        return r;
246
                if (r > 0)
69✔
247
                        pidns_fd = -EBADF;
36✔
248
        }
249

250
        if (mntns_fd >= 0) {
253✔
251
                r = is_our_namespace(mntns_fd, NAMESPACE_MOUNT);
193✔
252
                if (r < 0)
193✔
253
                        return r;
254
                if (r > 0)
193✔
255
                        mntns_fd = -EBADF;
8✔
256
        }
257

258
        if (netns_fd >= 0) {
253✔
259
                r = is_our_namespace(netns_fd, NAMESPACE_NET);
44✔
260
                if (r < 0)
44✔
261
                        return r;
262
                if (r > 0)
44✔
263
                        netns_fd = -EBADF;
1✔
264
        }
265

266
        if (userns_fd >= 0) {
253✔
267
                /* Can't setns to your own userns, since then you could escalate from non-root to root in
268
                 * your own namespace, so check if namespaces are equal before attempting to enter. */
269

270
                r = is_our_namespace(userns_fd, NAMESPACE_USER);
20✔
271
                if (r < 0)
20✔
272
                        return r;
273
                if (r > 0)
20✔
274
                        userns_fd = -EBADF;
1✔
275
        }
276

277
        r = have_effective_cap(CAP_SYS_ADMIN);
253✔
278
        if (r < 0)
253✔
279
                return r;
280

281
        bool have_cap_sys_admin = r > 0;
253✔
282

283
        if (!have_cap_sys_admin) {
253✔
284
                /* If we don't have CAP_SYS_ADMIN in our own user namespace, our best bet is to enter the
285
                 * user namespace first (if we got one) to get CAP_SYS_ADMIN within the child user namespace,
286
                 * and then hope the other namespaces are owned by the child user namespace. If they aren't,
287
                 * we'll just get an EPERM later on when trying to setns() to them. */
288

289
                if (userns_fd < 0)
18✔
290
                        return log_debug_errno(
×
291
                                        SYNTHETIC_ERRNO(EPERM),
292
                                        "Need CAP_SYS_ADMIN or a child user namespace to enter namespaces.");
293

294
                if (setns(userns_fd, CLONE_NEWUSER) < 0)
18✔
295
                        return -errno;
×
296
        }
297

298
        if (pidns_fd >= 0)
253✔
299
                if (setns(pidns_fd, CLONE_NEWPID) < 0)
33✔
300
                        return -errno;
×
301

302
        if (mntns_fd >= 0)
253✔
303
                if (setns(mntns_fd, CLONE_NEWNS) < 0)
185✔
304
                        return -errno;
×
305

306
        if (netns_fd >= 0)
253✔
307
                if (setns(netns_fd, CLONE_NEWNET) < 0)
43✔
308
                        return -errno;
×
309

310
        if (userns_fd >= 0 && have_cap_sys_admin)
253✔
311
                if (setns(userns_fd, CLONE_NEWUSER) < 0)
1✔
312
                        return -errno;
×
313

314
        if (root_fd >= 0) {
253✔
315
                if (fchdir(root_fd) < 0)
72✔
316
                        return -errno;
×
317

318
                if (chroot(".") < 0)
72✔
319
                        return -errno;
×
320
        }
321

322
        if (userns_fd >= 0) {
253✔
323
                /* Try to become root in the user namespace but don't error out if we can't, since it's not
324
                 * uncommon to have user namespaces without a root user in them. */
325
                r = reset_uid_gid();
19✔
326
                if (r < 0)
19✔
327
                        log_debug_errno(r, "Unable to drop auxiliary groups or reset UID/GID, ignoring: %m");
2✔
328
        }
329

330
        return 0;
331
}
332

333
int fd_is_namespace(int fd, NamespaceType type) {
1,775✔
334
        int r;
1,775✔
335

336
        /* Checks whether the specified file descriptor refers to a namespace (of type if type != _NAMESPACE_INVALID). */
337

338
        assert(fd >= 0);
1,775✔
339
        assert(type < _NAMESPACE_TYPE_MAX);
1,775✔
340

341
        r = fd_is_fs_type(fd, NSFS_MAGIC);
1,775✔
342
        if (r <= 0)
1,775✔
343
                return r;
344

345
        if (type < 0)
1,772✔
346
                return true;
347

348
        int clone_flag = ioctl(fd, NS_GET_NSTYPE);
1,772✔
349
        if (clone_flag < 0)
1,772✔
350
                return -errno;
×
351

352
        NamespaceType found_type = clone_flag_to_namespace_type(clone_flag);
1,772✔
353
        if (found_type < 0)
1,772✔
354
                return -EBADF; /* Uh? Unknown namespace type? */
355

356
        return found_type == type;
1,772✔
357
}
358

359
int is_our_namespace(int fd, NamespaceType type) {
1,355✔
360
        int r;
1,355✔
361

362
        assert(fd >= 0);
1,355✔
363
        assert(type >= 0 && type < _NAMESPACE_TYPE_MAX);
1,355✔
364

365
        r = fd_is_namespace(fd, type);
1,355✔
366
        if (r < 0)
1,355✔
367
                return r;
1,355✔
368
        if (r == 0) /* Not a namespace or not of the right type? */
1,355✔
369
                return -EUCLEAN;
370

371
        _cleanup_close_ int our_ns = namespace_open_by_type(type);
2,710✔
372
        if (our_ns < 0)
1,355✔
373
                return our_ns;
374

375
        return fd_inode_same(fd, our_ns);
1,355✔
376
}
377

378
int are_our_namespaces(int pidns_fd, int mntns_fd, int netns_fd, int userns_fd, int root_fd) {
35✔
379
        int r;
35✔
380

381
        if (pidns_fd >= 0) {
35✔
382
                r = is_our_namespace(pidns_fd, NAMESPACE_PID);
35✔
383
                if (r <= 0)
35✔
384
                        return r;
385
        }
386

387
        if (mntns_fd >= 0) {
1✔
388
                r = is_our_namespace(mntns_fd, NAMESPACE_MOUNT);
1✔
389
                if (r <= 0)
1✔
390
                        return r;
391
        }
392

393
        if (netns_fd >= 0) {
1✔
394
                r = is_our_namespace(netns_fd, NAMESPACE_NET);
×
395
                if (r <= 0)
×
396
                        return r;
397
        }
398

399
        if (userns_fd >= 0) {
1✔
400
                r = is_our_namespace(userns_fd, NAMESPACE_USER);
1✔
401
                if (r <= 0)
1✔
402
                        return r;
403
        }
404

405
        if (root_fd >= 0) {
1✔
406
                r = dir_fd_is_root(root_fd);
1✔
407
                if (r <= 0)
1✔
408
                        return r;
×
409
        }
410

411
        return true;
412
}
413

414
int network_namespace_is_init(int socket_fd) {
2,594✔
415
        struct stat a, b;
2,594✔
416
        int r;
2,594✔
417

418
        /* This works only when privileged. */
419

420
        r = RET_NERRNO(stat(pid_namespace_path(1, NAMESPACE_NET), &a));
2,594✔
421
        if (r == -ENOENT) {
707✔
422
                /* If the /proc/ns/<type> API is not around in /proc/ then ns is off in the kernel and we are in the init ns */
423
                r = proc_mounted();
408✔
424
                if (r < 0)
408✔
425
                        return -ENOENT; /* If we can't determine if /proc/ is mounted propagate original error */
2,594✔
426

427
                return r ? true : -ENOSYS;
408✔
428
        }
429
        if (r < 0)
2,186✔
430
                return r;
431

432
        if (socket_fd >= 0) {
1,887✔
433
                _cleanup_close_ int netns = ioctl(socket_fd, SIOCGSKNS);
1,884✔
434
                if (netns < 0)
1,884✔
435
                        return -errno;
452✔
436

437
                if (fstat(netns, &b) < 0)
1,432✔
438
                        return -errno;
×
439
        } else {
440
                if (stat(pid_namespace_path(0, NAMESPACE_NET), &b) < 0)
15✔
441
                        return -errno;
×
442
        }
443

444
        return stat_inode_same(&a, &b);
1,435✔
445
}
446

447
int namespace_is_init(NamespaceType type) {
8,763✔
448
        int r;
8,763✔
449

450
        assert(type >= 0);
8,763✔
451
        assert(type < _NAMESPACE_TYPE_MAX);
8,763✔
452

453
        if (type == NAMESPACE_NET)
8,763✔
454
                return network_namespace_is_init(/* socket_fd= */ -EBADF);
3✔
455

456
        if (namespace_info[type].root_inode == 0)
8,760✔
457
                return -EBADR; /* Cannot answer this question */
458

459
        const char *p = pid_namespace_path(0, type);
43,795✔
460

461
        struct stat st;
8,759✔
462
        r = RET_NERRNO(stat(p, &st));
8,759✔
463
        if (r == -ENOENT) {
25✔
464
                /* If the /proc/ns/<type> API is not around in /proc/ then ns is off in the kernel and we are in the init ns */
465
                r = proc_mounted();
25✔
466
                if (r < 0)
25✔
467
                        return -ENOENT; /* If we can't determine if /proc/ is mounted propagate original error */
468

469
                return r ? true : -ENOSYS;
25✔
470
        }
471
        if (r < 0)
8,734✔
472
                return r;
473

474
        return st.st_ino == namespace_info[type].root_inode;
8,734✔
475
}
476

477
int pidref_in_same_namespace(PidRef *pid1, PidRef *pid2, NamespaceType type) {
104✔
478
        _cleanup_close_ int ns1 = -EBADF, ns2 = -EBADF;
104✔
479

480
        /* Accepts NULL to indicate our own process */
481

482
        assert(!pid1 || pidref_is_set(pid1));
104✔
483
        assert(!pid2 || pidref_is_set(pid2));
104✔
484
        assert(type >= 0 && type < _NAMESPACE_TYPE_MAX);
104✔
485

486
        if (pidref_equal(pid1, pid2))
104✔
487
                return true;
488

489
        if (!pid1)
104✔
490
                ns1 = namespace_open_by_type(type);
87✔
491
        else
492
                ns1 = pidref_namespace_open_by_type(pid1, type);
17✔
493
        if (ns1 < 0)
104✔
494
                return ns1;
495

496
        if (!pid2)
104✔
497
                ns2 = namespace_open_by_type(type);
4✔
498
        else
499
                ns2 = pidref_namespace_open_by_type(pid2, type);
100✔
500
        if (ns2 < 0)
104✔
501
                return ns2;
502

503
        return fd_inode_same(ns1, ns2);
104✔
504
}
505

506
int in_same_namespace(pid_t pid1, pid_t pid2, NamespaceType type) {
×
507
        assert(pid1 >= 0);
×
508
        assert(pid2 >= 0);
×
509
        return pidref_in_same_namespace(pid1 == 0 ? NULL : &PIDREF_MAKE_FROM_PID(pid1),
×
510
                                        pid2 == 0 ? NULL : &PIDREF_MAKE_FROM_PID(pid2),
×
511
                                        type);
512
}
513

514
int namespace_get_leader(PidRef *pidref, NamespaceType type, PidRef *ret) {
3✔
515
        int r;
3✔
516

517
        /* Note: we don't bother with pidref_is_set()/pidref_is_remote() here, as the first call we do,
518
         * pidref_get_ppid_as_pidref() calls those anyway */
519

520
        assert(type >= 0 && type < _NAMESPACE_TYPE_MAX);
3✔
521
        assert(ret);
3✔
522

523
        _cleanup_(pidref_done) PidRef current = PIDREF_NULL;
3✔
524
        PidRef *c = pidref;
3✔
525

526
        for (;;) {
5✔
527
                _cleanup_(pidref_done) PidRef parent = PIDREF_NULL;
3✔
528

529
                r = pidref_get_ppid_as_pidref(c, &parent);
8✔
530
                if (r < 0)
8✔
531
                        return r;
532

533
                r = pidref_in_same_namespace(c, &parent, type);
8✔
534
                if (r < 0)
8✔
535
                        return r;
536
                if (r == 0) {
8✔
537
                        /* If the parent and the child are not in the same namespace, then the child is
538
                         * the leader we are looking for. */
539

540
                        if (pidref_is_set(&current))
3✔
541
                                *ret = TAKE_PIDREF(current);
3✔
542
                        else {
543
                                r = pidref_copy(c, ret);
×
544
                                if (r < 0)
×
545
                                        return r;
546
                        }
547

548
                        return 0;
3✔
549
                }
550

551
                pidref_done(&current);
5✔
552
                current = TAKE_PIDREF(parent);
5✔
553
                c = &current;
5✔
554
        }
555
}
556

557
int detach_mount_namespace(void) {
313✔
558
        /* Detaches the mount namespace, disabling propagation from our namespace to the host. Sets
559
         * propagation first to MS_SLAVE for all mounts (disabling propagation), and then back to MS_SHARED
560
         * (so that we create a new peer group).  */
561

562
        if (unshare(CLONE_NEWNS) < 0)
313✔
563
                return log_debug_errno(errno, "Failed to acquire mount namespace: %m");
×
564

565
        if (mount(NULL, "/", NULL, MS_SLAVE | MS_REC, NULL) < 0)
313✔
566
                return log_debug_errno(errno, "Failed to set mount propagation to MS_SLAVE for all mounts: %m");
×
567

568
        if (mount(NULL, "/", NULL, MS_SHARED | MS_REC, NULL) < 0)
313✔
569
                return log_debug_errno(errno, "Failed to set mount propagation back to MS_SHARED for all mounts: %m");
×
570

571
        return 0;
572
}
573

574
int detach_mount_namespace_harder(uid_t target_uid, gid_t target_gid) {
94✔
575
        uid_t from_uid;
94✔
576
        gid_t from_gid;
94✔
577
        int r;
94✔
578

579
        /* Tried detach_mount_namespace() first. If that doesn't work due to permissions, opens up an
580
         * unprivileged user namespace with a mapping of the originating UID/GID to the specified target
581
         * UID/GID. Then, tries detach_mount_namespace() again.
582
         *
583
         * Or in other words: tries much harder to get a mount namespace, making use of unprivileged user
584
         * namespaces if need be.
585
         *
586
         * Note that after this function completed:
587
         *
588
         *    → if we had privs, afterwards uids/gids on files and processes are as before
589
         *
590
         *    → if we had no privs, our own id and all our files will show up owned by target_uid/target_gid,
591
         *    and everything else owned by nobody.
592
         *
593
         * Yes, that's quite a difference. */
594

595
        if (!uid_is_valid(target_uid))
94✔
596
                return -EINVAL;
597
        if (!gid_is_valid(target_gid))
94✔
598
                return -EINVAL;
599

600
        r = detach_mount_namespace();
94✔
601
        if (r != -EPERM)
94✔
602
                return r;
603

604
        from_uid = getuid();
×
605
        from_gid = getgid();
×
606

607
        if (unshare(CLONE_NEWUSER) < 0)
×
608
                return log_debug_errno(errno, "Failed to acquire user namespace: %m");
×
609

610
        r = write_string_filef("/proc/self/uid_map", 0,
×
611
                               UID_FMT " " UID_FMT " 1\n", target_uid, from_uid);
612
        if (r < 0)
×
613
                return log_debug_errno(r, "Failed to write uid map: %m");
×
614

615
        r = write_string_file("/proc/self/setgroups", "deny", 0);
×
616
        if (r < 0)
×
617
                return log_debug_errno(r, "Failed to write setgroups file: %m");
×
618

619
        r = write_string_filef("/proc/self/gid_map", 0,
×
620
                               GID_FMT " " GID_FMT " 1\n", target_gid, from_gid);
621
        if (r < 0)
×
622
                return log_debug_errno(r, "Failed to write gid map: %m");
×
623

624
        return detach_mount_namespace();
×
625
}
626

627
int detach_mount_namespace_userns(int userns_fd) {
69✔
628
        int r;
69✔
629

630
        assert(userns_fd >= 0);
69✔
631

632
        if (setns(userns_fd, CLONE_NEWUSER) < 0)
69✔
633
                return log_debug_errno(errno, "Failed to join user namespace: %m");
×
634

635
        r = reset_uid_gid();
69✔
636
        if (r < 0)
69✔
637
                return log_debug_errno(r, "Failed to become root in user namespace: %m");
×
638

639
        return detach_mount_namespace();
69✔
640
}
641

642
int parse_userns_uid_range(const char *s, uid_t *ret_uid_shift, uid_t *ret_uid_range) {
2✔
643
        _cleanup_free_ char *buffer = NULL;
2✔
644
        const char *range, *shift;
2✔
645
        int r;
2✔
646
        uid_t uid_shift, uid_range = 65536;
2✔
647

648
        assert(s);
2✔
649

650
        range = strchr(s, ':');
2✔
651
        if (range) {
2✔
652
                buffer = strndup(s, range - s);
×
653
                if (!buffer)
×
654
                        return log_oom();
×
655
                shift = buffer;
×
656

657
                range++;
×
658
                r = safe_atou32(range, &uid_range);
×
659
                if (r < 0)
×
660
                        return log_error_errno(r, "Failed to parse UID range \"%s\": %m", range);
×
661
        } else
662
                shift = s;
663

664
        r = parse_uid(shift, &uid_shift);
2✔
665
        if (r < 0)
2✔
666
                return log_error_errno(r, "Failed to parse UID \"%s\": %m", s);
2✔
667

668
        if (!userns_shift_range_valid(uid_shift, uid_range))
×
669
                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "UID range cannot be empty or go beyond " UID_FMT ".", UID_INVALID);
×
670

671
        if (ret_uid_shift)
×
672
                *ret_uid_shift = uid_shift;
×
673

674
        if (ret_uid_range)
×
675
                *ret_uid_range = uid_range;
×
676

677
        return 0;
678
}
679

680
int userns_acquire_empty(void) {
121✔
681
        _cleanup_(pidref_done_sigkill_wait) PidRef pid = PIDREF_NULL;
121✔
682
        int r;
121✔
683

684
        r = pidref_safe_fork("(sd-mkuserns)", FORK_CLOSE_ALL_FDS|FORK_DEATHSIG_SIGKILL|FORK_NEW_USERNS|FORK_FREEZE, &pid);
121✔
685
        if (r < 0)
121✔
686
                return r;
687
        assert(r > 0);
121✔
688

689
        return pidref_namespace_open_by_type(&pid, NAMESPACE_USER);
121✔
690
}
691

692
int userns_acquire(const char *uid_map, const char *gid_map, bool setgroups_deny) {
3,365✔
693
        char path[STRLEN("/proc//setgroups") + DECIMAL_STR_MAX(pid_t) + 1];
3,365✔
694
        _cleanup_(pidref_done_sigkill_wait) PidRef pid = PIDREF_NULL;
3,365✔
695
        int r;
3,365✔
696

697
        assert(uid_map);
3,365✔
698
        assert(gid_map);
3,365✔
699

700
        /* Forks off a process in a new userns, configures the specified uidmap/gidmap, acquires an fd to it,
701
         * and then kills the process again. This way we have a userns fd that is not bound to any
702
         * process. We can use that for file system mounts and similar. */
703

704
        r = pidref_safe_fork("(sd-mkuserns)", FORK_CLOSE_ALL_FDS|FORK_DEATHSIG_SIGKILL|FORK_NEW_USERNS|FORK_FREEZE, &pid);
3,365✔
705
        if (r < 0)
3,365✔
706
                return r;
707
        assert(r > 0);
3,365✔
708

709
        xsprintf(path, "/proc/" PID_FMT "/uid_map", pid.pid);
3,365✔
710
        r = write_string_file(path, uid_map, WRITE_STRING_FILE_DISABLE_BUFFER);
3,365✔
711
        if (r < 0)
3,365✔
712
                return log_debug_errno(r, "Failed to write UID map: %m");
×
713

714
        if (setgroups_deny) {
3,365✔
715
                xsprintf(path, "/proc/" PID_FMT "/setgroups", pid.pid);
3,365✔
716
                r = write_string_file(path, "deny", WRITE_STRING_FILE_DISABLE_BUFFER);
3,365✔
717
                if (r < 0)
3,365✔
718
                        return log_debug_errno(r, "Failed to write setgroups file: %m");
×
719
        }
720

721
        xsprintf(path, "/proc/" PID_FMT "/gid_map", pid.pid);
3,365✔
722
        r = write_string_file(path, gid_map, WRITE_STRING_FILE_DISABLE_BUFFER);
3,365✔
723
        if (r < 0)
3,365✔
724
                return log_debug_errno(r, "Failed to write GID map: %m");
×
725

726
        return pidref_namespace_open_by_type(&pid, NAMESPACE_USER);
3,365✔
727
}
728

729
int userns_acquire_self_root(void) {
3,170✔
730

731
        /* Returns a user namespace with only our own uid/gid mapped to root, and everything else unmapped.
732
         *
733
         * Note: this can be acquired unprivileged! */
734

735
        _cleanup_free_ char *uid_map = NULL, *gid_map = NULL;
3,170✔
736
        if (asprintf(&uid_map, "0 " UID_FMT " 1", getuid()) < 0)
3,170✔
737
                return -ENOMEM;
738
        if (asprintf(&gid_map, "0 " GID_FMT " 1", getgid()) < 0)
3,170✔
739
                return -ENOMEM;
740

741
        return userns_acquire(uid_map, gid_map, /* setgroups_deny= */ true);
3,170✔
742
}
743

744
int userns_enter_and_pin(int userns_fd, PidRef *ret) {
296✔
745
        _cleanup_close_pair_ int pfd[2] = EBADF_PAIR;
296✔
746
        _cleanup_(pidref_done_sigkill_wait) PidRef pidref = PIDREF_NULL;
296✔
747
        ssize_t n;
296✔
748
        char x;
296✔
749
        int r;
296✔
750

751
        assert(userns_fd >= 0);
296✔
752
        assert(ret);
296✔
753

754
        if (pipe2(pfd, O_CLOEXEC) < 0)
296✔
755
                return -errno;
×
756

757
        r = pidref_safe_fork_full(
592✔
758
                        "(sd-pinuserns)",
759
                        /* stdio_fds= */ NULL,
760
                        (int[]) { pfd[1], userns_fd }, 2,
296✔
761
                        FORK_CLOSE_ALL_FDS|FORK_REOPEN_LOG|FORK_DEATHSIG_SIGKILL,
762
                        &pidref);
763
        if (r < 0)
296✔
764
                return r;
765
        if (r == 0) {
296✔
766
                /* Child. */
767

768
                if (setns(userns_fd, CLONE_NEWUSER) < 0) {
×
769
                        log_debug_errno(errno, "Failed to join userns: %m");
×
770
                        _exit(EXIT_FAILURE);
×
771
                }
772

773
                userns_fd = safe_close(userns_fd);
×
774

775
                n = write(pfd[1], &(const char) { 'x' }, 1);
×
776
                if (n < 0) {
×
777
                        log_debug_errno(errno, "Failed to write to pipe: %m");
×
778
                        _exit(EXIT_FAILURE);
×
779
                }
780
                assert(n == 1);
×
781

782
                freeze();
×
783
        }
784

785
        pfd[1] = safe_close(pfd[1]);
296✔
786

787
        n = read(pfd[0], &x, 1);
296✔
788
        if (n < 0)
296✔
789
                return -errno;
×
790
        if (n == 0)
296✔
791
                return -EPROTO;
792
        assert(n == 1);
296✔
793
        assert(x == 'x');
296✔
794

795
        *ret = TAKE_PIDREF(pidref);
296✔
796
        return 0;
296✔
797
}
798

799
bool userns_supported(void) {
305✔
800
        return access("/proc/self/uid_map", F_OK) >= 0;
305✔
801
}
802

803
int userns_get_base_uid(int userns_fd, uid_t *ret_uid, gid_t *ret_gid) {
37✔
804
        _cleanup_(pidref_done_sigkill_wait) PidRef pidref = PIDREF_NULL;
37✔
805
        int r;
37✔
806

807
        assert(userns_fd >= 0);
37✔
808

809
        r = userns_enter_and_pin(userns_fd, &pidref);
37✔
810
        if (r < 0)
37✔
811
                return r;
812

813
        uid_t uid;
37✔
814
        r = uid_map_search_root(pidref.pid, UID_RANGE_USERNS_OUTSIDE, &uid);
37✔
815
        if (r < 0)
37✔
816
                return r;
817

818
        gid_t gid;
36✔
819
        r = uid_map_search_root(pidref.pid, GID_RANGE_USERNS_OUTSIDE, &gid);
36✔
820
        if (r < 0)
36✔
821
                return r;
822

823
        if (!ret_gid && uid != gid)
36✔
824
                return -EUCLEAN;
825

826
        if (ret_uid)
35✔
827
                *ret_uid = uid;
35✔
828
        if (ret_gid)
35✔
829
                *ret_gid = gid;
1✔
830

831
        return 0;
832
}
833

834
int process_is_owned_by_uid(const PidRef *pidref, uid_t uid) {
17✔
835
        int r;
17✔
836

837
        /* Checks if the specified process either is owned directly by the specified user, or if it is inside
838
         * a user namespace owned by it. */
839

840
        assert(uid_is_valid(uid));
17✔
841

842
        uid_t process_uid;
17✔
843
        r = pidref_get_uid(pidref, &process_uid);
17✔
844
        if (r < 0)
17✔
845
                return log_debug_errno(r, "Failed to get UID of process " PID_FMT ": %m", pidref ? pidref->pid : 0);
×
846
        if (process_uid == uid)
17✔
847
                return true;
848

849
        log_debug("Process " PID_FMT " has UID " UID_FMT ", which doesn't match expected UID " UID_FMT ", checking user namespace ownership.",
11✔
850
                  pidref->pid, process_uid, uid);
851

852
        _cleanup_close_ int userns_fd = -EBADF;
17✔
853
        userns_fd = pidref_namespace_open_by_type(pidref, NAMESPACE_USER);
11✔
854
        if (userns_fd == -ENOPKG) /* If userns is not supported, then they don't matter for ownership */
11✔
855
                return false;
856
        if (userns_fd < 0)
11✔
857
                return log_debug_errno(userns_fd, "Failed to open user namespace of process " PID_FMT ": %m", pidref->pid);
×
858

859
        for (unsigned iteration = 0;; iteration++) {
×
860
                uid_t ns_uid;
11✔
861

862
                /* This process is in our own userns? Then we are done, in our own userns only the UIDs
863
                 * themselves matter. */
864
                r = is_our_namespace(userns_fd, NAMESPACE_USER);
11✔
865
                if (r < 0)
11✔
866
                        return log_debug_errno(r, "Failed to check if user namespace of process " PID_FMT " is our own (iteration %u): %m", pidref->pid, iteration);
×
867
                if (r > 0) {
11✔
868
                        log_debug("User namespace of process " PID_FMT " is our own namespace (iteration %u), not owned by expected UID.", pidref->pid, iteration);
2✔
869
                        return false;
870
                }
871

872
                if (ioctl(userns_fd, NS_GET_OWNER_UID, &ns_uid) < 0)
9✔
873
                        return log_debug_errno(errno, "Failed to get owner UID of user namespace of process " PID_FMT " (iteration %u): %m", pidref->pid, iteration);
×
874
                if (ns_uid == uid) {
9✔
875
                        log_debug("User namespace of process " PID_FMT " is owned by UID " UID_FMT " (iteration %u), ownership check passed.", pidref->pid, uid, iteration);
9✔
876
                        return true;
877
                }
878

879
                log_debug("User namespace of process " PID_FMT " is owned by UID " UID_FMT ", expected UID " UID_FMT " (iteration %u), going up the tree.",
×
880
                          pidref->pid, ns_uid, uid, iteration);
881

882
                /* Paranoia check */
883
                if (iteration > 16)
×
884
                        return log_debug_errno(SYNTHETIC_ERRNO(ELOOP), "Giving up while tracing parents of user namespaces after %u steps.", iteration);
×
885

886
                /* Go up the tree */
887
                _cleanup_close_ int parent_fd = ioctl(userns_fd, NS_GET_USERNS);
11✔
888
                if (parent_fd < 0) {
×
889
                        if (errno == EPERM) { /* EPERM means we left our own userns */
×
890
                                log_debug("NS_GET_USERNS ioctl returned EPERM for process " PID_FMT " (iteration %u), left our own userns.", pidref->pid, iteration);
×
891
                                return false;
892
                        }
893

894
                        return log_debug_errno(errno, "NS_GET_USERNS ioctl failed for process " PID_FMT " (iteration %u): %m", pidref->pid, iteration);
×
895
                }
896

897
                close_and_replace(userns_fd, parent_fd);
×
898
        }
899
}
900

901
int namespace_open_by_id(uint64_t ns_id) {
103✔
902
        int r;
103✔
903

904
        /* Looks up a namespace by its unique boot-stable identifier and returns an O_PATH fd to it.
905
         * Requires kernel ≥ 6.13.
906
         *
907
         * Returns -ESTALE if the namespace no longer exists, or if the kernel refuses the lookup
908
         * for permission reasons. The latter happens outside the initial user namespace: the
909
         * kernel only permits open_by_handle_at() on nsfs when the caller is in the initial user
910
         * and pid namespaces with CAP_SYS_ADMIN, with a narrow exception for lookups of the
911
         * caller's own user namespace and its ancestors. To avoid conflating "namespace is dead"
912
         * with "kernel refused us", we refuse early with -EPERM when we aren't in the initial
913
         * user/pid namespace or missing CAP_SYS_ADMIN and let the caller skip the check. */
914

915
        if (ns_id == 0)
103✔
916
                return -EINVAL;
103✔
917

918
        r = namespace_is_init(NAMESPACE_USER);
103✔
919
        if (r < 0)
103✔
920
                return r;
921
        if (r == 0)
103✔
922
                return -EPERM;
923

924
        r = namespace_is_init(NAMESPACE_PID);
103✔
925
        if (r < 0)
103✔
926
                return r;
927
        if (r == 0)
103✔
928
                return -EPERM;
929

930
        r = have_effective_cap(CAP_SYS_ADMIN);
103✔
931
        if (r < 0)
103✔
932
                return r;
933
        if (r == 0)
103✔
934
                return -EPERM;
935

936
        /* The natural way to write this would be a compound designated initializer:
937
         *
938
         *         union { ... } fh = {
939
         *                 .file_handle.handle_bytes = sizeof(struct nsfs_file_handle),
940
         *                 .file_handle.handle_type = FILEID_NSFS,
941
         *         };
942
         *
943
         * but that only zero-initializes the named struct members of struct file_handle.
944
         * struct file_handle ends with a flexible array (`unsigned char f_handle[]`), whose
945
         * storage comes from the overlapping `space[]` member of the union. Bytes in that storage
946
         * are not covered by the partial struct initializer and end up as stack garbage. Zero the
947
         * entire union first, then fill in the fields explicitly. */
948

949
        union {
103✔
950
                struct file_handle file_handle;
951
                uint8_t space[offsetof(struct file_handle, f_handle) + sizeof(struct nsfs_file_handle)];
952
        } fh = {};
103✔
953
        fh.file_handle.handle_bytes = sizeof(struct nsfs_file_handle);
103✔
954
        fh.file_handle.handle_type = FILEID_NSFS;
103✔
955

956
        /* The first 8 bytes of struct nsfs_file_handle (see <linux/nsfs.h>, uapi since kernel v6.18)
957
         * are __u64 ns_id; the remaining ns_type/ns_inum fields stay zero so the kernel looks up by
958
         * id alone. The kernel made lookup-by-id-only an explicit ABI guarantee in v6.19 via commit
959
         * 04173501a69e ("nstree: allow lookup solely based on inode"). */
960
        unaligned_write_ne64(fh.file_handle.f_handle, ns_id);
103✔
961

962
        return RET_NERRNO(open_by_handle_at(FD_NSFS_ROOT, &fh.file_handle, O_PATH|O_CLOEXEC));
103✔
963
}
964

965
int is_idmapping_supported(const char *path) {
3,169✔
966
        _cleanup_close_ int mount_fd = -EBADF, userns_fd = -EBADF, dir_fd = -EBADF;
6,338✔
967
        int r;
3,169✔
968

969
        assert(path);
3,169✔
970

971
        if (!mount_new_api_supported())
3,169✔
972
                return false;
973

974
        userns_fd = r = userns_acquire_self_root();
3,169✔
975
        if (ERRNO_IS_NEG_NOT_SUPPORTED(r) || ERRNO_IS_NEG_PRIVILEGE(r) || r == -EINVAL)
3,169✔
976
                return false;
977
        if (r == -ENOSPC) {
3,169✔
978
                log_debug_errno(r, "Failed to acquire new user namespace, user.max_user_namespaces seems to be exhausted or maybe even zero, assuming ID-mapping is not supported: %m");
×
979
                return false;
980
        }
981
        if (r < 0)
3,169✔
982
                return log_debug_errno(r, "Failed to acquire new user namespace for checking if '%s' supports ID-mapping: %m", path);
×
983

984
        dir_fd = r = RET_NERRNO(open(path, O_RDONLY | O_CLOEXEC | O_NOFOLLOW));
3,169✔
985
        if (ERRNO_IS_NEG_NOT_SUPPORTED(r))
×
986
                return false;
987
        if (r < 0)
3,169✔
988
                return log_debug_errno(r, "Failed to open '%s', cannot determine if ID-mapping is supported: %m", path);
×
989

990
        mount_fd = r = RET_NERRNO(open_tree(dir_fd, "", AT_EMPTY_PATH | OPEN_TREE_CLONE | OPEN_TREE_CLOEXEC));
3,169✔
991
        if (ERRNO_IS_NEG_NOT_SUPPORTED(r) || ERRNO_IS_NEG_PRIVILEGE(r) || r == -EINVAL)
3,169✔
992
                return false;
993
        if (r < 0)
3,169✔
994
                return log_debug_errno(r, "Failed to open mount tree '%s', cannot determine if ID-mapping is supported: %m", path);
×
995

996
        r = RET_NERRNO(mount_setattr(mount_fd, "", AT_EMPTY_PATH,
3,169✔
997
                       &(struct mount_attr) {
3,169✔
998
                                .attr_set = MOUNT_ATTR_IDMAP | MOUNT_ATTR_NOSUID | MOUNT_ATTR_NOEXEC | MOUNT_ATTR_RDONLY | MOUNT_ATTR_NODEV,
999
                                .userns_fd = userns_fd,
1000
                        }, sizeof(struct mount_attr)));
1001
        if (ERRNO_IS_NEG_NOT_SUPPORTED(r) || ERRNO_IS_NEG_PRIVILEGE(r) || r == -EINVAL)
6,338✔
1002
                return false;
1003
        if (r < 0)
3,169✔
1004
                return log_debug_errno(r, "Failed to set mount attribute to '%s', cannot determine if ID-mapping is supported: %m", path);
×
1005

1006
        return true;
1007
}
1008

1009
int netns_acquire(void) {
7✔
1010
        _cleanup_(pidref_done_sigkill_wait) PidRef pid = PIDREF_NULL;
7✔
1011
        int r;
7✔
1012

1013
        /* Forks off a process in a new network namespace, acquires a network namespace fd, and then kills
1014
         * the process again. This way we have a netns fd that is not bound to any process. */
1015

1016
        r = pidref_safe_fork("(sd-mknetns)", FORK_CLOSE_ALL_FDS|FORK_DEATHSIG_SIGKILL|FORK_NEW_NETNS|FORK_FREEZE, &pid);
7✔
1017
        if (r < 0)
7✔
1018
                return log_debug_errno(r, "Failed to fork process into new netns: %m");
×
1019
        assert(r > 0);
7✔
1020

1021
        return pidref_namespace_open_by_type(&pid, NAMESPACE_NET);
7✔
1022
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc