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

systemd / systemd / 14950366571

10 May 2025 12:04PM UTC coverage: 72.229% (-0.02%) from 72.251%
14950366571

push

github

keszybz
test: fix root check for test-bpf-foreign-programs

This test requires root, but the check was mistakenly dropped, causing it
to fail with an assert when running without root:

src/test/test-bpf-foreign-programs.c:308: Assertion failed: expected "test_bpf_cgroup_programs(m, "single_prog.service", single_prog, ELEMENTSOF(single_prog))" to succeed, but got error: Operation not permitted

Restore the uid check

Follow-up for 22e2f0642

0 of 3 new or added lines in 1 file covered. (0.0%)

176 existing lines in 40 files now uncovered.

297455 of 411822 relevant lines covered (72.23%)

704153.97 hits per line

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

87.5
/src/basic/pidref.c
1
/* SPDX-License-Identifier: LGPL-2.1-or-later */
2

3
#include "errno-util.h"
4
#include "fd-util.h"
5
#include "log.h"
6
#include "missing_syscall.h"
7
#include "missing_wait.h"
8
#include "parse-util.h"
9
#include "pidfd-util.h"
10
#include "pidref.h"
11
#include "process-util.h"
12
#include "signal-util.h"
13

14
int pidref_acquire_pidfd_id(PidRef *pidref) {
9,940✔
15
        int r;
9,940✔
16

17
        assert(pidref);
9,940✔
18

19
        if (!pidref_is_set(pidref))
9,940✔
20
                return -ESRCH;
21

22
        if (pidref_is_remote(pidref))
9,940✔
23
                return -EREMOTE;
24

25
        if (pidref->fd < 0)
9,940✔
26
                return -ENOMEDIUM;
27

28
        if (pidref->fd_id > 0)
9,274✔
29
                return 0;
30

31
        r = pidfd_get_inode_id(pidref->fd, &pidref->fd_id);
5,187✔
32
        if (r < 0) {
5,187✔
33
                if (!ERRNO_IS_NEG_NOT_SUPPORTED(r))
×
34
                        log_debug_errno(r, "Failed to get inode number of pidfd for pid " PID_FMT ": %m",
×
35
                                        pidref->pid);
36
                return r;
×
37
        }
38

39
        return 0;
40
}
41

42
bool pidref_equal(PidRef *a, PidRef *b) {
20,219✔
43

44
        /* If this is the very same structure, it definitely refers to the same process */
45
        if (a == b)
20,219✔
46
                return true;
47

48
        if (!pidref_is_set(a))
20,216✔
49
                return !pidref_is_set(b);
5,948✔
50

51
        if (!pidref_is_set(b))
17,242✔
52
                return false;
53

54
        if (a->pid != b->pid)
17,236✔
55
                return false;
56

57
        if (pidref_is_remote(a)) {
3,700✔
58
                /* If one is remote and the other isn't, they are not the same */
59
                if (!pidref_is_remote(b))
×
60
                        return false;
61

62
                /* If both are remote, compare fd IDs if we have both, otherwise don't bother, and cut things short */
63
                if (a->fd_id == 0 || b->fd_id == 0)
×
64
                        return true;
65
        } else {
66
                /* If the other side is remote, then this is not the same */
67
                if (pidref_is_remote(b))
3,700✔
68
                        return false;
69

70
                /* PID1 cannot exit, hence it cannot change pidfs ids, hence no point in comparing them, we
71
                 * can shortcut things */
72
                if (a->pid == 1)
3,699✔
73
                        return true;
74

75
                /* Try to compare pidfds using their inode numbers. This way we can ensure that we
76
                 * don't spuriously consider two PidRefs equal if the pid has been reused once. Note
77
                 * that we ignore all errors here, not only EOPNOTSUPP, as fstat() might fail due to
78
                 * many reasons. */
79
                if (pidref_acquire_pidfd_id(a) < 0 || pidref_acquire_pidfd_id(b) < 0)
3,581✔
80
                        return true;
2✔
81
        }
82

83
        return a->fd_id == b->fd_id;
3,579✔
84
}
85

86
int pidref_set_pid(PidRef *pidref, pid_t pid) {
60,501✔
87
        uint64_t pidfdid = 0;
60,501✔
88
        int fd;
60,501✔
89

90
        assert(pidref);
60,501✔
91

92
        if (pid < 0)
60,501✔
93
                return -ESRCH;
60,501✔
94
        if (pid == 0) {
60,501✔
95
                pid = getpid_cached();
37,468✔
96
                (void) pidfd_get_inode_id_self_cached(&pidfdid);
37,468✔
97
        }
98

99
        fd = pidfd_open(pid, 0);
60,501✔
100
        if (fd < 0) {
60,501✔
101
                /* Graceful fallback in case the kernel is out of fds */
UNCOV
102
                if (!ERRNO_IS_RESOURCE(errno))
×
UNCOV
103
                        return log_debug_errno(errno, "Failed to open pidfd for pid " PID_FMT ": %m", pid);
×
104

105
                fd = -EBADF;
106
        }
107

108
        *pidref = (PidRef) {
60,501✔
109
                .fd = fd,
110
                .pid = pid,
111
                .fd_id = pidfdid,
112
        };
113

114
        return 0;
60,501✔
115
}
116

117
int pidref_set_pidstr(PidRef *pidref, const char *pid) {
4✔
118
        pid_t nr;
4✔
119
        int r;
4✔
120

121
        assert(pidref);
4✔
122

123
        r = parse_pid(pid, &nr);
4✔
124
        if (r < 0)
4✔
125
                return r;
4✔
126

127
        return pidref_set_pid(pidref, nr);
4✔
128
}
129

130
int pidref_set_pidfd(PidRef *pidref, int fd) {
585✔
131
        int r;
585✔
132

133
        assert(pidref);
585✔
134

135
        if (fd < 0)
585✔
136
                return -EBADF;
137

138
        int fd_copy = fcntl(fd, F_DUPFD_CLOEXEC, 3);
585✔
139
        if (fd_copy < 0) {
585✔
140
                pid_t pid;
×
141

142
                if (!ERRNO_IS_RESOURCE(errno))
×
143
                        return -errno;
×
144

145
                /* Graceful fallback if we are out of fds */
146
                r = pidfd_get_pid(fd, &pid);
×
147
                if (r < 0)
×
148
                        return r;
149

150
                *pidref = PIDREF_MAKE_FROM_PID(pid);
×
151
                return 0;
×
152
        }
153

154
        return pidref_set_pidfd_consume(pidref, fd_copy);
585✔
155
}
156

157
int pidref_set_pidfd_take(PidRef *pidref, int fd) {
2,848✔
158
        pid_t pid;
2,848✔
159
        int r;
2,848✔
160

161
        assert(pidref);
2,848✔
162

163
        if (fd < 0)
2,848✔
164
                return -EBADF;
2,848✔
165

166
        r = pidfd_get_pid(fd, &pid);
2,848✔
167
        if (r < 0)
2,848✔
168
                return r;
169

170
        *pidref = (PidRef) {
2,848✔
171
                .fd = fd,
172
                .pid = pid,
173
        };
174

175
        return 0;
2,848✔
176
}
177

178
int pidref_set_pidfd_consume(PidRef *pidref, int fd) {
2,819✔
179
        int r;
2,819✔
180

181
        r = pidref_set_pidfd_take(pidref, fd);
2,819✔
182
        if (r < 0)
2,819✔
183
                safe_close(fd);
×
184

185
        return r;
2,819✔
186
}
187

188
int pidref_set_parent(PidRef *ret) {
1,111✔
189
        _cleanup_(pidref_done) PidRef parent = PIDREF_NULL;
1,111✔
190
        pid_t ppid;
1,111✔
191
        int r;
1,111✔
192

193
        assert(ret);
1,111✔
194

195
        /* Acquires a pidref to our parent process. Deals with the fact that parent processes might exit, and
196
         * we get reparented to other processes, with our old parent's PID already being recycled. */
197

198
        ppid = getppid();
1,111✔
199
        for (;;) {
1,111✔
200
                r = pidref_set_pid(&parent, ppid);
1,111✔
201
                if (r < 0)
1,111✔
202
                        return r;
203

204
                if (parent.fd < 0) /* If pidfds are not available, then we are done */
1,111✔
205
                        break;
206

207
                pid_t now_ppid = getppid();
1,111✔
208
                if (now_ppid == ppid) /* If our ppid is still the same, then we are done */
1,111✔
209
                        break;
210

211
                /* Otherwise let's try again with the new ppid */
212
                ppid = now_ppid;
×
213
                pidref_done(&parent);
×
214
        }
215

216
        *ret = TAKE_PIDREF(parent);
1,111✔
217
        return 0;
1,111✔
218
}
219

220
void pidref_done(PidRef *pidref) {
217,121✔
221
        assert(pidref);
217,121✔
222

223
        *pidref = (PidRef) {
434,242✔
224
                .fd = safe_close(pidref->fd),
217,121✔
225
        };
226
}
217,121✔
227

228
PidRef* pidref_free(PidRef *pidref) {
2,894✔
229
        /* Regularly, this is an embedded structure. But sometimes we want it on the heap too */
230
        if (!pidref)
2,894✔
231
                return NULL;
232

233
        pidref_done(pidref);
2,894✔
234
        return mfree(pidref);
2,894✔
235
}
236

237
int pidref_copy(const PidRef *pidref, PidRef *ret) {
2,895✔
238
        _cleanup_(pidref_done) PidRef copy = PIDREF_NULL;
2,895✔
239

240
        /* If NULL is passed we'll generate a PidRef that refers to no process. This makes it easy to
241
         * copy pidref fields that might or might not reference a process yet. */
242

243
        assert(ret);
2,895✔
244

245
        if (pidref) {
2,895✔
246
                if (pidref_is_remote(pidref)) /* Propagate remote flag */
2,893✔
247
                        copy.fd = -EREMOTE;
×
248
                else if (pidref->fd >= 0) {
2,893✔
249
                        copy.fd = fcntl(pidref->fd, F_DUPFD_CLOEXEC, 3);
2,887✔
250
                        if (copy.fd < 0) {
2,887✔
251
                                if (!ERRNO_IS_RESOURCE(errno))
×
252
                                        return -errno;
×
253

254
                                copy.fd = -EBADF;
×
255
                        }
256
                }
257

258
                copy.pid = pidref->pid;
2,893✔
259
                copy.fd_id = pidref->fd_id;
2,893✔
260
        }
261

262
        *ret = TAKE_PIDREF(copy);
2,895✔
263
        return 0;
2,895✔
264
}
265

266
int pidref_dup(const PidRef *pidref, PidRef **ret) {
2,891✔
267
        _cleanup_(pidref_freep) PidRef *dup_pidref = NULL;
2,891✔
268
        int r;
2,891✔
269

270
        /* Allocates a new PidRef on the heap, making it a copy of the specified pidref. This does not try to
271
         * acquire a pidfd if we don't have one yet! */
272

273
        assert(ret);
2,891✔
274

275
        dup_pidref = newdup(PidRef, &PIDREF_NULL, 1);
2,891✔
276
        if (!dup_pidref)
2,891✔
277
                return -ENOMEM;
278

279
        r = pidref_copy(pidref, dup_pidref);
2,891✔
280
        if (r < 0)
2,891✔
281
                return r;
282

283
        *ret = TAKE_PTR(dup_pidref);
2,891✔
284
        return 0;
2,891✔
285
}
286

287
int pidref_new_from_pid(pid_t pid, PidRef **ret) {
4✔
288
        _cleanup_(pidref_freep) PidRef *n = NULL;
4✔
289
        int r;
4✔
290

291
        assert(ret);
4✔
292

293
        if (pid < 0)
4✔
294
                return -ESRCH;
295

296
        n = new(PidRef, 1);
3✔
297
        if (!n)
3✔
298
                return -ENOMEM;
299

300
        *n = PIDREF_NULL;
3✔
301

302
        r = pidref_set_pid(n, pid);
3✔
303
        if (r < 0)
3✔
304
                return r;
305

306
        *ret = TAKE_PTR(n);
3✔
307
        return 0;
3✔
308
}
309

310
int pidref_kill(const PidRef *pidref, int sig) {
15,307✔
311

312
        if (!pidref)
15,307✔
313
                return -ESRCH;
314

315
        if (pidref_is_remote(pidref))
15,307✔
316
                return -EREMOTE;
317

318
        if (pidref->fd >= 0)
15,305✔
319
                return RET_NERRNO(pidfd_send_signal(pidref->fd, sig, NULL, 0));
7,969✔
320

321
        if (pidref->pid > 0)
7,336✔
322
                return RET_NERRNO(kill(pidref->pid, sig));
13,827✔
323

324
        return -ESRCH;
325
}
326

327
int pidref_kill_and_sigcont(const PidRef *pidref, int sig) {
587✔
328
        int r;
587✔
329

330
        r = pidref_kill(pidref, sig);
587✔
331
        if (r < 0)
587✔
332
                return r;
333

334
        if (!IN_SET(sig, SIGCONT, SIGKILL))
586✔
335
                (void) pidref_kill(pidref, SIGCONT);
586✔
336

337
        return 0;
338
}
339

340
int pidref_sigqueue(const PidRef *pidref, int sig, int value) {
9,756✔
341

342
        if (!pidref)
9,756✔
343
                return -ESRCH;
344

345
        if (pidref_is_remote(pidref))
9,756✔
346
                return -EREMOTE;
347

348
        if (pidref->fd >= 0) {
9,756✔
349
                siginfo_t si;
9,756✔
350

351
                /* We can't use structured initialization here, since the structure contains various unions
352
                 * and these fields lie in overlapping (carefully aligned) unions that LLVM is allergic to
353
                 * allow assignments to */
354
                zero(si);
9,756✔
355
                si.si_signo = sig;
9,756✔
356
                si.si_code = SI_QUEUE;
9,756✔
357
                si.si_pid = getpid_cached();
9,756✔
358
                si.si_uid = getuid();
9,756✔
359
                si.si_value.sival_int = value;
9,756✔
360

361
                return RET_NERRNO(pidfd_send_signal(pidref->fd, sig, &si, 0));
9,756✔
362
        }
363

364
        if (pidref->pid > 0)
×
365
                return RET_NERRNO(sigqueue(pidref->pid, sig, (const union sigval) { .sival_int = value }));
×
366

367
        return -ESRCH;
368
}
369

370
int pidref_verify(const PidRef *pidref) {
64,052✔
371
        int r;
64,052✔
372

373
        /* This is a helper that is supposed to be called after reading information from procfs via a
374
         * PidRef. It ensures that the PID we track still matches the PIDFD we pin. If this value differs
375
         * after a procfs read, we might have read the data from a recycled PID. */
376

377
        if (!pidref_is_set(pidref))
64,052✔
378
                return -ESRCH;
379

380
        if (pidref_is_remote(pidref))
64,050✔
381
                return -EREMOTE;
382

383
        if (pidref->pid == 1)
64,049✔
384
                return 1; /* PID 1 can never go away, hence never be recycled to a different process → return 1 */
385

386
        if (pidref->fd < 0)
49,776✔
387
                return 0; /* If we don't have a pidfd we cannot validate it, hence we assume it's all OK → return 0 */
388

389
        r = pidfd_verify_pid(pidref->fd, pidref->pid);
32,353✔
390
        if (r < 0)
32,353✔
391
                return r;
1,953✔
392

393
        return 1; /* We have a pidfd and it still points to the PID we have, hence all is *really* OK → return 1 */
394
}
395

396
bool pidref_is_self(PidRef *pidref) {
38,273✔
397
        if (!pidref_is_set(pidref))
38,273✔
398
                return false;
38,273✔
399

400
        if (pidref_is_remote(pidref))
38,271✔
401
                return false;
402

403
        if (pidref->pid != getpid_cached())
38,271✔
404
                return false;
405

406
        /* PID1 cannot exit, hence no point in comparing pidfd IDs, they can never change */
407
        if (pidref->pid == 1)
1,326✔
408
                return true;
409

410
        /* Also compare pidfd ID if we can get it */
411
        if (pidref_acquire_pidfd_id(pidref) < 0)
1,326✔
412
                return true;
413

414
        uint64_t self_id;
662✔
415
        if (pidfd_get_inode_id_self_cached(&self_id) < 0)
662✔
416
                return true;
417

418
        return pidref->fd_id == self_id;
662✔
419
}
420

421
int pidref_wait(PidRef *pidref, siginfo_t *ret, int options) {
13,765✔
422
        int r;
13,765✔
423

424
        if (!pidref_is_set(pidref))
13,765✔
425
                return -ESRCH;
13,765✔
426

427
        if (pidref_is_remote(pidref))
13,765✔
428
                return -EREMOTE;
429

430
        if (pidref->pid == 1 || pidref_is_self(pidref))
13,764✔
431
                return -ECHILD;
×
432

433
        siginfo_t si = {};
13,764✔
434
        if (pidref->fd >= 0)
13,764✔
435
                r = RET_NERRNO(waitid(P_PIDFD, pidref->fd, &si, options));
4,986✔
436
        else
437
                r = RET_NERRNO(waitid(P_PID, pidref->pid, &si, options));
8,778✔
438
        if (r < 0)
×
439
                return r;
440

441
        if (ret)
13,764✔
442
                *ret = si;
9,785✔
443

444
        return 0;
445
}
446

447
int pidref_wait_for_terminate(PidRef *pidref, siginfo_t *ret) {
13,765✔
448
        int r;
13,765✔
449

450
        for (;;) {
13,765✔
451
                r = pidref_wait(pidref, ret, WEXITED);
13,765✔
452
                if (r != -EINTR)
13,765✔
453
                        return r;
13,765✔
454
        }
455
}
456

457
bool pidref_is_automatic(const PidRef *pidref) {
173,300✔
458
        return pidref && pid_is_automatic(pidref->pid);
173,300✔
459
}
460

461
void pidref_hash_func(const PidRef *pidref, struct siphash *state) {
83,479✔
462
        siphash24_compress_typesafe(pidref->pid, state);
83,479✔
463
}
83,479✔
464

465
int pidref_compare_func(const PidRef *a, const PidRef *b) {
86,624✔
466
        int r;
86,624✔
467

468
        assert(a);
86,624✔
469
        assert(b);
86,624✔
470

471
        r = CMP(pidref_is_set(a), pidref_is_set(b));
259,872✔
472
        if (r != 0)
86,624✔
473
                return r;
×
474

475
        r = CMP(pidref_is_automatic(a), pidref_is_automatic(b));
86,624✔
476
        if (r != 0)
86,624✔
477
                return r;
×
478

479
        r = CMP(pidref_is_remote(a), pidref_is_remote(b));
173,248✔
480
        if (r != 0)
86,624✔
481
                return r;
×
482

483
        r = CMP(a->pid, b->pid);
86,624✔
484
        if (r != 0)
69,983✔
485
                return r;
19,694✔
486

487
        if (a->fd_id != 0 && b->fd_id != 0)
66,930✔
488
                return CMP(a->fd_id, b->fd_id);
461✔
489

490
        return 0;
491
}
492

493
DEFINE_HASH_OPS(pidref_hash_ops, PidRef, pidref_hash_func, pidref_compare_func);
494

495
DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR(pidref_hash_ops_free,
×
496
                                    PidRef, pidref_hash_func, pidref_compare_func,
497
                                    pidref_free);
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc