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

systemd / systemd / 20401947236

20 Dec 2025 09:56PM UTC coverage: 72.701% (+0.1%) from 72.578%
20401947236

push

github

DaanDeMeyer
core/socket: modernize listen/accept_in_cgroup

4 of 9 new or added lines in 1 file covered. (44.44%)

7723 existing lines in 114 files now uncovered.

309972 of 426363 relevant lines covered (72.7%)

1133403.64 hits per line

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

93.51
/src/basic/uid-range.c
1
/* SPDX-License-Identifier: LGPL-2.1-or-later */
2

3
#include <string.h>
4

5
#include "alloc-util.h"
6
#include "errno-util.h"
7
#include "fd-util.h"
8
#include "format-util.h"
9
#include "namespace-util.h"
10
#include "path-util.h"
11
#include "pidref.h"
12
#include "process-util.h"
13
#include "sort-util.h"
14
#include "stat-util.h"
15
#include "uid-range.h"
16
#include "user-util.h"
17

18
UIDRange *uid_range_free(UIDRange *range) {
3,106✔
19
        if (!range)
3,106✔
20
                return NULL;
21

22
        free(range->entries);
359✔
23
        return mfree(range);
359✔
24
}
25

26
static bool uid_range_entry_intersect(const UIDRangeEntry *a, const UIDRangeEntry *b) {
94✔
27
        assert(a);
94✔
28
        assert(b);
94✔
29

30
        return a->start <= b->start + b->nr && a->start + a->nr >= b->start;
94✔
31
}
32

33
static int uid_range_entry_compare(const UIDRangeEntry *a, const UIDRangeEntry *b) {
282✔
34
        int r;
282✔
35

36
        assert(a);
282✔
37
        assert(b);
282✔
38

39
        r = CMP(a->start, b->start);
282✔
40
        if (r != 0)
138✔
41
                return r;
262✔
42

43
        return CMP(a->nr, b->nr);
20✔
44
}
45

46
static void uid_range_coalesce(UIDRange *range) {
367✔
47
        assert(range);
367✔
48

49
        if (range->n_entries <= 0)
367✔
50
                return;
51

52
        typesafe_qsort(range->entries, range->n_entries, uid_range_entry_compare);
297✔
53

54
        for (size_t i = 0; i < range->n_entries; i++) {
600✔
55
                UIDRangeEntry *x = range->entries + i;
303✔
56

57
                for (size_t j = i + 1; j < range->n_entries; j++) {
391✔
58
                        UIDRangeEntry *y = range->entries + j;
94✔
59
                        uid_t begin, end;
94✔
60

61
                        if (!uid_range_entry_intersect(x, y))
94✔
62
                                break;
63

64
                        begin = MIN(x->start, y->start);
88✔
65
                        end = MAX(x->start + x->nr, y->start + y->nr);
88✔
66

67
                        x->start = begin;
88✔
68
                        x->nr = end - begin;
88✔
69

70
                        if (range->n_entries > j + 1)
88✔
71
                                memmove(y, y + 1, sizeof(UIDRangeEntry) * (range->n_entries - j - 1));
81✔
72

73
                        range->n_entries--;
88✔
74
                        j--;
88✔
75
                }
76
        }
77
}
78

79
int uid_range_add_internal(UIDRange **range, uid_t start, uid_t nr, bool coalesce) {
378✔
80
        _cleanup_(uid_range_freep) UIDRange *range_new = NULL;
378✔
81
        UIDRange *p;
378✔
82

83
        assert(range);
378✔
84

85
        if (nr <= 0)
378✔
86
                return 0;
87

88
        if (start > UINT32_MAX - nr) /* overflow check */
378✔
89
                return -ERANGE;
90

91
        if (*range)
378✔
92
                p = *range;
93
        else {
94
                range_new = new0(UIDRange, 1);
118✔
95
                if (!range_new)
118✔
96
                        return -ENOMEM;
97

98
                p = range_new;
99
        }
100

101
        if (!GREEDY_REALLOC(p->entries, p->n_entries + 1))
378✔
102
                return -ENOMEM;
103

104
        p->entries[p->n_entries++] = (UIDRangeEntry) {
378✔
105
                .start = start,
106
                .nr = nr,
107
        };
108

109
        if (coalesce)
378✔
110
                uid_range_coalesce(p);
126✔
111

112
        TAKE_PTR(range_new);
378✔
113
        *range = p;
378✔
114

115
        return 0;
378✔
116
}
117

118
int uid_range_add_str(UIDRange **range, const char *s) {
10✔
119
        uid_t start, end;
10✔
120
        int r;
10✔
121

122
        assert(range);
10✔
123
        assert(s);
10✔
124

125
        r = parse_uid_range(s, &start, &end);
10✔
126
        if (r < 0)
10✔
127
                return r;
10✔
128

129
        return uid_range_add_internal(range, start, end - start + 1, /* coalesce= */ true);
10✔
130
}
131

132
int uid_range_next_lower(const UIDRange *range, uid_t *uid) {
195✔
133
        uid_t closest = UID_INVALID, candidate;
195✔
134

135
        assert(range);
195✔
136
        assert(uid);
195✔
137

138
        if (*uid == 0)
195✔
139
                return -EBUSY;
140

141
        candidate = *uid - 1;
195✔
142

143
        for (size_t i = 0; i < range->n_entries; i++) {
241✔
144
                uid_t begin, end;
195✔
145

146
                begin = range->entries[i].start;
195✔
147
                end = range->entries[i].start + range->entries[i].nr - 1;
195✔
148

149
                if (candidate >= begin && candidate <= end) {
195✔
150
                        *uid = candidate;
149✔
151
                        return 1;
149✔
152
                }
153

154
                if (end < candidate)
46✔
155
                        closest = end;
45✔
156
        }
157

158
        if (closest == UID_INVALID)
46✔
159
                return -EBUSY;
160

161
        *uid = closest;
45✔
162
        return 1;
45✔
163
}
164

165
bool uid_range_covers(const UIDRange *range, uid_t start, uid_t nr) {
255✔
166
        if (nr == 0) /* empty range? always covered... */
255✔
167
                return true;
168

169
        if (start > UINT32_MAX - nr) /* range overflows? definitely not covered... */
254✔
170
                return false;
171

172
        if (!range)
251✔
173
                return false;
174

175
        FOREACH_ARRAY(i, range->entries, range->n_entries)
267✔
176
                if (start >= i->start &&
255✔
177
                    start + nr <= i->start + i->nr)
249✔
178
                        return true;
179

180
        return false;
181
}
182

183
int uid_map_read_one(FILE *f, uid_t *ret_base, uid_t *ret_shift, uid_t *ret_range) {
651✔
184
        uid_t uid_base, uid_shift, uid_range;
651✔
185
        int r;
651✔
186

187
        assert(f);
651✔
188

189
        errno = 0;
651✔
190
        r = fscanf(f, UID_FMT " " UID_FMT " " UID_FMT "\n", &uid_base, &uid_shift, &uid_range);
651✔
191
        if (r == EOF)
651✔
192
                return errno_or_else(ENOMSG);
312✔
193
        assert(r >= 0);
339✔
194
        if (r != 3)
339✔
195
                return -EBADMSG;
196
        if (uid_range <= 0)
339✔
197
                return -EBADMSG;
198

199
        if (ret_base)
339✔
200
                *ret_base = uid_base;
339✔
201
        if (ret_shift)
339✔
202
                *ret_shift = uid_shift;
339✔
203
        if (ret_range)
339✔
204
                *ret_range = uid_range;
289✔
205

206
        return 0;
207
}
208

209
unsigned uid_range_size(const UIDRange *range) {
7✔
210
        if (!range)
7✔
211
                return 0;
212

213
        unsigned n = 0;
6✔
214

215
        FOREACH_ARRAY(e, range->entries, range->n_entries)
16✔
216
                n += e->nr;
10✔
217

218
        return n;
219
}
220

221
bool uid_range_is_empty(const UIDRange *range) {
74✔
222

223
        if (!range)
74✔
224
                return true;
225

226
        FOREACH_ARRAY(e, range->entries, range->n_entries)
73✔
227
                if (e->nr > 0)
3✔
228
                        return false;
229

230
        return true;
231
}
232

233
int uid_range_load_userns(const char *path, UIDRangeUsernsMode mode, UIDRange **ret) {
241✔
UNCOV
234
        _cleanup_(uid_range_freep) UIDRange *range = NULL;
×
235
        _cleanup_fclose_ FILE *f = NULL;
241✔
236
        int r;
241✔
237

238
        /* If 'path' is NULL loads the UID range of the userns namespace we run. Otherwise load the data from
239
         * the specified file (which can be either uid_map or gid_map, in case caller needs to deal with GID
240
         * maps).
241
         *
242
         * To simplify things this will modify the passed array in case of later failure. */
243

244
        assert(mode >= 0);
241✔
245
        assert(mode < _UID_RANGE_USERNS_MODE_MAX);
241✔
246
        assert(ret);
241✔
247

248
        if (!path)
241✔
249
                path = IN_SET(mode, UID_RANGE_USERNS_INSIDE, UID_RANGE_USERNS_OUTSIDE) ? "/proc/self/uid_map" : "/proc/self/gid_map";
170✔
250

251
        f = fopen(path, "re");
241✔
252
        if (!f) {
241✔
UNCOV
253
                r = -errno;
×
254

255
                if (r == -ENOENT && path_startswith(path, "/proc/"))
×
UNCOV
256
                        return proc_mounted() > 0 ? -EOPNOTSUPP : -ENOSYS;
×
257

258
                return r;
259
        }
260

261
        range = new0(UIDRange, 1);
241✔
262
        if (!range)
241✔
263
                return -ENOMEM;
264

265
        for (;;) {
172✔
266
                uid_t uid_base, uid_shift, uid_range;
413✔
267

268
                r = uid_map_read_one(f, &uid_base, &uid_shift, &uid_range);
413✔
269
                if (r == -ENOMSG)
413✔
270
                        break;
271
                if (r < 0)
172✔
UNCOV
272
                        return r;
×
273

274
                r = uid_range_add_internal(
172✔
275
                                &range,
276
                                IN_SET(mode, UID_RANGE_USERNS_INSIDE, GID_RANGE_USERNS_INSIDE) ? uid_base : uid_shift,
172✔
277
                                uid_range,
278
                                /* coalesce= */ false);
279
                if (r < 0)
172✔
280
                        return r;
281
        }
282

283
        uid_range_coalesce(range);
241✔
284

285
        *ret = TAKE_PTR(range);
241✔
286
        return 0;
241✔
287
}
288

289
int uid_range_load_userns_by_fd(int userns_fd, UIDRangeUsernsMode mode, UIDRange **ret) {
70✔
290
        _cleanup_(pidref_done_sigkill_wait) PidRef pidref = PIDREF_NULL;
70✔
291
        int r;
70✔
292

293
        assert(userns_fd >= 0);
70✔
294
        assert(mode >= 0);
70✔
295
        assert(mode < _UID_RANGE_USERNS_MODE_MAX);
70✔
296
        assert(ret);
70✔
297

298
        r = userns_enter_and_pin(userns_fd, &pidref);
70✔
299
        if (r < 0)
70✔
300
                return r;
301

302
        const char *p = procfs_file_alloca(
70✔
303
                        pidref.pid,
304
                        IN_SET(mode, UID_RANGE_USERNS_INSIDE, UID_RANGE_USERNS_OUTSIDE) ? "uid_map" : "gid_map");
305

306
        return uid_range_load_userns(p, mode, ret);
70✔
307
}
308

309
bool uid_range_overlaps(const UIDRange *range, uid_t start, uid_t nr) {
18✔
310

311
        if (!range)
18✔
312
                return false;
313

314
        /* Avoid overflow */
315
        if (start > UINT32_MAX - nr)
18✔
UNCOV
316
                nr = UINT32_MAX - start;
×
317

318
        if (nr == 0)
18✔
319
                return false;
320

321
        FOREACH_ARRAY(entry, range->entries, range->n_entries)
18✔
322
                if (start < entry->start + entry->nr &&
18✔
323
                    start + nr >= entry->start)
18✔
324
                        return true;
325

326
        return false;
327
}
328

329
bool uid_range_equal(const UIDRange *a, const UIDRange *b) {
4✔
330
        if (a == b)
4✔
331
                return true;
332

333
        if (!a || !b)
4✔
334
                return false;
335

336
        if (a->n_entries != b->n_entries)
3✔
337
                return false;
338

339
        for (size_t i = 0; i < a->n_entries; i++) {
5✔
340
                if (a->entries[i].start != b->entries[i].start)
3✔
341
                        return false;
342
                if (a->entries[i].nr != b->entries[i].nr)
3✔
343
                        return false;
344
        }
345

346
        return true;
347
}
348

349
int uid_map_search_root(pid_t pid, UIDRangeUsernsMode mode, uid_t *ret) {
51✔
350
        int r;
51✔
351

352
        assert(pid_is_valid(pid));
51✔
353
        assert(IN_SET(mode, UID_RANGE_USERNS_OUTSIDE, GID_RANGE_USERNS_OUTSIDE));
51✔
354

355
        const char *p = procfs_file_alloca(pid, mode == UID_RANGE_USERNS_OUTSIDE ? "uid_map" : "gid_map");
51✔
356
        _cleanup_fclose_ FILE *f = fopen(p, "re");
102✔
357
        if (!f) {
51✔
358
                if (errno != ENOENT)
×
UNCOV
359
                        return -errno;
×
360

361
                r = proc_mounted();
×
UNCOV
362
                if (r < 0)
×
363
                        return -ENOENT; /* original error, if we can't determine /proc/ state */
364

UNCOV
365
                return r ? -ENOPKG : -ENOSYS;
×
366
        }
367

UNCOV
368
        for (;;) {
×
369
                uid_t uid_base = UID_INVALID, uid_shift = UID_INVALID;
51✔
370

371
                r = uid_map_read_one(f, &uid_base, &uid_shift, /* ret_range= */ NULL);
51✔
372
                if (r < 0)
51✔
373
                        return r;
51✔
374

375
                if (uid_base == 0) {
50✔
376
                        if (ret)
50✔
377
                                *ret = uid_shift;
50✔
378
                        return 0;
50✔
379
                }
380
        }
381
}
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