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

systemd / systemd / 13125386108

03 Feb 2025 10:14PM UTC coverage: 71.734% (-0.06%) from 71.79%
13125386108

push

github

web-flow
docs: Update CPE fields in package metadata spec (#36251)

Update osCPE field example to use cpe 2.3 format, as is in active use by
AmazonLinux 2023 for example.

Add appCPE field example to document the upstream application CPE for
the applicable CVEs. Often distribution source package names are
different from the upstream CPE. For example adding/removing "lib"
prefix, or adding version stream "-3" suffix. This typically leads to
guessing or fuzzy matching. Adding appCPE in such cases can help to
disambiguate (or collate) correct application CPEs; especially beyond
the lifetime of osCPE support timeframes. This also will help a lot with
packaging multiple alternative source packages of the same software
(e.g. nginx-full nginx-core); different version streams (e.g.
openssl-1.1, openssl-3); or alternative builds of upstream software with
largely the same CVEs with multiple version streams (e.g.
openjdk-{22,17,11..}, corretto-{22,17,11..}, temurin-{22,17,11..}, etc).

292625 of 407928 relevant lines covered (71.73%)

713708.1 hits per line

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

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

3
#include <errno.h>
4
#include <stdlib.h>
5
#include <string.h>
6

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

20
UIDRange *uid_range_free(UIDRange *range) {
3,854✔
21
        if (!range)
3,854✔
22
                return NULL;
23

24
        free(range->entries);
455✔
25
        return mfree(range);
455✔
26
}
27

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

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

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

38
        assert(a);
282✔
39
        assert(b);
282✔
40

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

45
        return CMP(a->nr, b->nr);
20✔
46
}
47

48
static void uid_range_coalesce(UIDRange *range) {
463✔
49
        assert(range);
463✔
50

51
        if (range->n_entries <= 0)
463✔
52
                return;
53

54
        typesafe_qsort(range->entries, range->n_entries, uid_range_entry_compare);
463✔
55

56
        for (size_t i = 0; i < range->n_entries; i++) {
932✔
57
                UIDRangeEntry *x = range->entries + i;
469✔
58

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

63
                        if (!uid_range_entry_intersect(x, y))
94✔
64
                                break;
65

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

69
                        x->start = begin;
88✔
70
                        x->nr = end - begin;
88✔
71

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

75
                        range->n_entries--;
88✔
76
                        j--;
88✔
77
                }
78
        }
79
}
80

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

85
        assert(range);
544✔
86

87
        if (nr <= 0)
544✔
88
                return 0;
89

90
        if (start > UINT32_MAX - nr) /* overflow check */
544✔
91
                return -ERANGE;
92

93
        if (*range)
544✔
94
                p = *range;
95
        else {
96
                range_new = new0(UIDRange, 1);
218✔
97
                if (!range_new)
218✔
98
                        return -ENOMEM;
99

100
                p = range_new;
101
        }
102

103
        if (!GREEDY_REALLOC(p->entries, p->n_entries + 1))
544✔
104
                return -ENOMEM;
105

106
        p->entries[p->n_entries++] = (UIDRangeEntry) {
544✔
107
                .start = start,
108
                .nr = nr,
109
        };
110

111
        if (coalesce)
544✔
112
                uid_range_coalesce(p);
226✔
113

114
        TAKE_PTR(range_new);
544✔
115
        *range = p;
544✔
116

117
        return 0;
544✔
118
}
119

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

124
        assert(range);
10✔
125
        assert(s);
10✔
126

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

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

134
int uid_range_next_lower(const UIDRange *range, uid_t *uid) {
183✔
135
        uid_t closest = UID_INVALID, candidate;
183✔
136

137
        assert(range);
183✔
138
        assert(uid);
183✔
139

140
        if (*uid == 0)
183✔
141
                return -EBUSY;
142

143
        candidate = *uid - 1;
183✔
144

145
        for (size_t i = 0; i < range->n_entries; i++) {
229✔
146
                uid_t begin, end;
183✔
147

148
                begin = range->entries[i].start;
183✔
149
                end = range->entries[i].start + range->entries[i].nr - 1;
183✔
150

151
                if (candidate >= begin && candidate <= end) {
183✔
152
                        *uid = candidate;
137✔
153
                        return 1;
137✔
154
                }
155

156
                if (end < candidate)
46✔
157
                        closest = end;
45✔
158
        }
159

160
        if (closest == UID_INVALID)
46✔
161
                return -EBUSY;
162

163
        *uid = closest;
45✔
164
        return 1;
45✔
165
}
166

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

171
        if (start > UINT32_MAX - nr) /* range overflows? definitely not covered... */
308✔
172
                return false;
173

174
        if (!range)
305✔
175
                return false;
176

177
        FOREACH_ARRAY(i, range->entries, range->n_entries)
321✔
178
                if (start >= i->start &&
309✔
179
                    start + nr <= i->start + i->nr)
303✔
180
                        return true;
181

182
        return false;
183
}
184

185
int uid_map_read_one(FILE *f, uid_t *ret_base, uid_t *ret_shift, uid_t *ret_range) {
628✔
186
        uid_t uid_base, uid_shift, uid_range;
628✔
187
        int r;
628✔
188

189
        assert(f);
628✔
190

191
        errno = 0;
628✔
192
        r = fscanf(f, UID_FMT " " UID_FMT " " UID_FMT "\n", &uid_base, &uid_shift, &uid_range);
628✔
193
        if (r == EOF)
628✔
194
                return errno_or_else(ENOMSG);
272✔
195
        assert(r >= 0);
356✔
196
        if (r != 3)
356✔
197
                return -EBADMSG;
198
        if (uid_range <= 0)
356✔
199
                return -EBADMSG;
200

201
        if (ret_base)
356✔
202
                *ret_base = uid_base;
356✔
203
        if (ret_shift)
356✔
204
                *ret_shift = uid_shift;
356✔
205
        if (ret_range)
356✔
206
                *ret_range = uid_range;
314✔
207

208
        return 0;
209
}
210

211
int uid_range_load_userns(const char *path, UIDRangeUsernsMode mode, UIDRange **ret) {
237✔
212
        _cleanup_(uid_range_freep) UIDRange *range = NULL;
×
213
        _cleanup_fclose_ FILE *f = NULL;
237✔
214
        int r;
237✔
215

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

222
        assert(mode >= 0);
237✔
223
        assert(mode < _UID_RANGE_USERNS_MODE_MAX);
237✔
224
        assert(ret);
237✔
225

226
        if (!path)
237✔
227
                path = IN_SET(mode, UID_RANGE_USERNS_INSIDE, UID_RANGE_USERNS_OUTSIDE) ? "/proc/self/uid_map" : "/proc/self/gid_map";
236✔
228

229
        f = fopen(path, "re");
237✔
230
        if (!f) {
237✔
231
                r = -errno;
×
232

233
                if (r == -ENOENT && path_startswith(path, "/proc/"))
×
234
                        return proc_mounted() > 0 ? -EOPNOTSUPP : -ENOSYS;
×
235

236
                return r;
237
        }
238

239
        range = new0(UIDRange, 1);
237✔
240
        if (!range)
237✔
241
                return -ENOMEM;
242

243
        for (;;) {
238✔
244
                uid_t uid_base, uid_shift, uid_range;
475✔
245

246
                r = uid_map_read_one(f, &uid_base, &uid_shift, &uid_range);
475✔
247
                if (r == -ENOMSG)
475✔
248
                        break;
249
                if (r < 0)
238✔
250
                        return r;
×
251

252
                r = uid_range_add_internal(
238✔
253
                                &range,
254
                                IN_SET(mode, UID_RANGE_USERNS_INSIDE, GID_RANGE_USERNS_INSIDE) ? uid_base : uid_shift,
238✔
255
                                uid_range,
256
                                /* coalesce = */ false);
257
                if (r < 0)
238✔
258
                        return r;
259
        }
260

261
        uid_range_coalesce(range);
237✔
262

263
        *ret = TAKE_PTR(range);
237✔
264
        return 0;
237✔
265
}
266

267
int uid_range_load_userns_by_fd(int userns_fd, UIDRangeUsernsMode mode, UIDRange **ret) {
×
268
        _cleanup_(sigkill_waitp) pid_t pid = 0;
×
269
        int r;
×
270

271
        assert(userns_fd >= 0);
×
272
        assert(mode >= 0);
×
273
        assert(mode < _UID_RANGE_USERNS_MODE_MAX);
×
274
        assert(ret);
×
275

276
        r = userns_enter_and_pin(userns_fd, &pid);
×
277
        if (r < 0)
×
278
                return r;
279

280
        const char *p = procfs_file_alloca(
×
281
                        pid,
282
                        IN_SET(mode, UID_RANGE_USERNS_INSIDE, UID_RANGE_USERNS_OUTSIDE) ? "uid_map" : "gid_map");
283

284
        return uid_range_load_userns(p, mode, ret);
×
285
}
286

287
bool uid_range_overlaps(const UIDRange *range, uid_t start, uid_t nr) {
×
288

289
        if (!range)
×
290
                return false;
291

292
        /* Avoid overflow */
293
        if (start > UINT32_MAX - nr)
×
294
                nr = UINT32_MAX - start;
×
295

296
        if (nr == 0)
×
297
                return false;
298

299
        FOREACH_ARRAY(entry, range->entries, range->n_entries)
×
300
                if (start < entry->start + entry->nr &&
×
301
                    start + nr >= entry->start)
×
302
                        return true;
303

304
        return false;
305
}
306

307
bool uid_range_equal(const UIDRange *a, const UIDRange *b) {
4✔
308
        if (a == b)
4✔
309
                return true;
310

311
        if (!a || !b)
4✔
312
                return false;
313

314
        if (a->n_entries != b->n_entries)
3✔
315
                return false;
316

317
        for (size_t i = 0; i < a->n_entries; i++) {
5✔
318
                if (a->entries[i].start != b->entries[i].start)
3✔
319
                        return false;
320
                if (a->entries[i].nr != b->entries[i].nr)
3✔
321
                        return false;
322
        }
323

324
        return true;
325
}
326

327
int uid_map_search_root(pid_t pid, UIDRangeUsernsMode mode, uid_t *ret) {
45✔
328
        int r;
45✔
329

330
        assert(pid_is_valid(pid));
45✔
331
        assert(IN_SET(mode, UID_RANGE_USERNS_OUTSIDE, GID_RANGE_USERNS_OUTSIDE));
45✔
332

333
        const char *p = procfs_file_alloca(pid, mode == UID_RANGE_USERNS_OUTSIDE ? "uid_map" : "gid_map");
66✔
334
        _cleanup_fclose_ FILE *f = fopen(p, "re");
90✔
335
        if (!f) {
45✔
336
                if (errno != ENOENT)
×
337
                        return -errno;
×
338

339
                r = proc_mounted();
×
340
                if (r < 0)
×
341
                        return -ENOENT; /* original error, if we can't determine /proc/ state */
342

343
                return r ? -ENOPKG : -ENOSYS;
×
344
        }
345

346
        for (;;) {
×
347
                uid_t uid_base = UID_INVALID, uid_shift = UID_INVALID;
45✔
348

349
                r = uid_map_read_one(f, &uid_base, &uid_shift, /* ret_uid_range= */ NULL);
45✔
350
                if (r < 0)
45✔
351
                        return r;
45✔
352

353
                if (uid_base == 0) {
42✔
354
                        if (ret)
42✔
355
                                *ret = uid_shift;
42✔
356
                        return 0;
42✔
357
                }
358
        }
359
}
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