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

systemd / systemd / 28557109148

01 Jul 2026 11:57PM UTC coverage: 72.68% (-0.2%) from 72.877%
28557109148

push

github

web-flow
core: assorted hardening fixes flagged by kres (#42840)

2 of 16 new or added lines in 5 files covered. (12.5%)

4972 existing lines in 84 files now uncovered.

341360 of 469675 relevant lines covered (72.68%)

1370997.73 hits per line

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

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

3
#include <unistd.h>
4

5
#include "alloc-util.h"
6
#include "extract-word.h"
7
#include "fd-util.h"
8
#include "fileio.h"
9
#include "parse-util.h"
10
#include "process-util.h"
11
#include "procfs-util.h"
12
#include "stdio-util.h"
13
#include "string-util.h"
14
#include "time-util.h"
15

16
int procfs_get_pid_max(uint64_t *ret) {
26,988✔
17
        _cleanup_free_ char *value = NULL;
26,988✔
18
        int r;
26,988✔
19

20
        assert(ret);
26,988✔
21

22
        r = read_one_line_file("/proc/sys/kernel/pid_max", &value);
26,988✔
23
        if (r < 0)
26,988✔
24
                return r;
25

26
        return safe_atou64(value, ret);
26,988✔
27
}
28

29
int procfs_get_threads_max(uint64_t *ret) {
26,987✔
30
        _cleanup_free_ char *value = NULL;
26,987✔
31
        int r;
26,987✔
32

33
        assert(ret);
26,987✔
34

35
        r = read_one_line_file("/proc/sys/kernel/threads-max", &value);
26,987✔
36
        if (r < 0)
26,987✔
37
                return r;
38

39
        return safe_atou64(value, ret);
26,987✔
40
}
41

42
int procfs_tasks_set_limit(uint64_t limit) {
2✔
43
        char buffer[DECIMAL_STR_MAX(uint64_t)+1];
2✔
44
        uint64_t pid_max;
2✔
45
        int r;
2✔
46

47
        if (limit == 0) /* This makes no sense, we are userspace and hence count as tasks too, and we want to live,
2✔
48
                         * hence the limit conceptually has to be above 0. Also, most likely if anyone asks for a zero
49
                         * limit they probably mean "no limit", hence let's better refuse this to avoid
50
                         * confusion. */
51
                return -EINVAL;
2✔
52

53
        /* The Linux kernel doesn't allow this value to go below 20, hence don't allow this either, higher values than
54
         * TASKS_MAX are not accepted by the pid_max sysctl. We'll treat anything this high as "unbounded" and hence
55
         * set it to the maximum. */
56
        limit = CLAMP(limit, 20U, TASKS_MAX);
2✔
57

58
        r = procfs_get_pid_max(&pid_max);
2✔
59
        if (r < 0)
2✔
60
                return r;
61

62
        /* As pid_max is about the numeric pid_t range we'll bump it if necessary, but only ever increase it, never
63
         * decrease it, as threads-max is the much more relevant sysctl. */
64
        if (limit > pid_max-1) {
2✔
65
                xsprintf(buffer, "%" PRIu64, limit+1); /* Add one, since PID 0 is not a valid PID */
×
66
                r = write_string_file("/proc/sys/kernel/pid_max", buffer, WRITE_STRING_FILE_DISABLE_BUFFER);
×
67
                if (r < 0)
×
68
                        return r;
69
        }
70

71
        xsprintf(buffer, "%" PRIu64, limit);
2✔
72
        r = write_string_file("/proc/sys/kernel/threads-max", buffer, WRITE_STRING_FILE_DISABLE_BUFFER);
2✔
73
        if (r < 0) {
2✔
74
                uint64_t threads_max;
2✔
75

76
                /* Hmm, we couldn't write this? If so, maybe it was already set properly? In that case let's not
77
                 * generate an error */
78

79
                if (procfs_get_threads_max(&threads_max) < 0)
2✔
80
                        return r; /* return original error */
1✔
81

82
                if (MIN(pid_max - 1, threads_max) != limit)
2✔
83
                        return r; /* return original error */
84

85
                /* Yay! Value set already matches what we were trying to set, hence consider this a success. */
86
        }
87

88
        return 0;
89
}
90

91
int procfs_tasks_get_current(uint64_t *ret) {
1✔
92
        _cleanup_free_ char *value = NULL;
1✔
93
        const char *p, *nr;
1✔
94
        size_t n;
1✔
95
        int r;
1✔
96

97
        assert(ret);
1✔
98

99
        r = read_one_line_file("/proc/loadavg", &value);
1✔
100
        if (r < 0)
1✔
101
                return r;
102

103
        /* Look for the second part of the fourth field, which is separated by a slash from the first part. None of the
104
         * earlier fields use a slash, hence let's use this to find the right spot. */
105
        p = strchr(value, '/');
1✔
106
        if (!p)
1✔
107
                return -EINVAL;
108

109
        p++;
1✔
110
        n = strspn(p, DIGITS);
1✔
111
        nr = strndupa_safe(p, n);
1✔
112

113
        return safe_atou64(nr, ret);
1✔
114
}
115

116
static uint64_t calc_gcd64(uint64_t a, uint64_t b) {
1✔
117

118
        while (b > 0) {
2✔
119
                uint64_t t;
1✔
120

121
                t = a % b;
1✔
122

123
                a = b;
1✔
124
                b = t;
1✔
125
        }
126

127
        return a;
1✔
128
}
129

130
int procfs_cpu_get_usage(nsec_t *ret) {
1✔
131
        _cleanup_free_ char *first_line = NULL;
1✔
132
        unsigned long user_ticks, nice_ticks, system_ticks, irq_ticks, softirq_ticks,
1✔
133
                guest_ticks = 0, guest_nice_ticks = 0;
1✔
134
        uint64_t sum, gcd, a, b;
1✔
135
        const char *p;
1✔
136
        int r;
1✔
137

138
        assert(ret);
1✔
139

140
        r = read_one_line_file("/proc/stat", &first_line);
1✔
141
        if (r < 0)
1✔
142
                return r;
143

144
        p = first_word(first_line, "cpu");
1✔
145
        if (!p)
1✔
146
                return -EINVAL;
147

148
        if (sscanf(p, "%lu %lu %lu %*u %*u %lu %lu %*u %lu %lu",
1✔
149
                   &user_ticks,
150
                   &nice_ticks,
151
                   &system_ticks,
152
                   &irq_ticks,
153
                   &softirq_ticks,
154
                   &guest_ticks,
155
                   &guest_nice_ticks) < 5) /* we only insist on the first five fields */
156
                return -EINVAL;
157

158
        uint64_t ticks_per_second = sysconf_clock_ticks_cached();
1✔
159

160
        sum = (uint64_t) user_ticks + (uint64_t) nice_ticks + (uint64_t) system_ticks +
1✔
161
                (uint64_t) irq_ticks + (uint64_t) softirq_ticks +
1✔
162
                (uint64_t) guest_ticks + (uint64_t) guest_nice_ticks;
1✔
163

164
        /* Let's reduce this fraction before we apply it to avoid overflows when converting this to μsec */
165
        gcd = calc_gcd64(NSEC_PER_SEC, ticks_per_second);
1✔
166

167
        a = NSEC_PER_SEC / gcd;
1✔
168
        b = ticks_per_second / gcd;
1✔
169

170
        *ret = DIV_ROUND_UP((nsec_t) sum * (nsec_t) a, (nsec_t) b);
1✔
171
        return 0;
1✔
172
}
173

174
int convert_meminfo_value_to_uint64_bytes(const char *s, uint64_t *ret) {
293✔
175
        _cleanup_free_ char *w = NULL;
293✔
176
        uint64_t v;
293✔
177
        int r;
293✔
178

179
        assert(s);
293✔
180
        assert(ret);
293✔
181

182
        r = extract_first_word(&s, &w, /* separators= */ NULL, /* flags= */ 0);
293✔
183
        if (r < 0)
293✔
184
                return r;
185
        if (r == 0)
293✔
186
                return -EINVAL;
187

188
        /* Ensure the line ends in "kB" */
189
        if (!streq(s, "kB"))
293✔
190
                return -EINVAL;
191

192
        r = safe_atou64(w, &v);
292✔
193
        if (r < 0)
292✔
194
                return r;
195
        if (v == UINT64_MAX)
292✔
196
                return -EINVAL;
197

198
        if (!MUL_ASSIGN_SAFE(&v, U64_KB))
292✔
199
                return -EOVERFLOW;
200

201
        *ret = v;
292✔
202
        return 0;
292✔
203
}
204

205
int procfs_memory_get(uint64_t *ret_total, uint64_t *ret_used) {
124✔
206
        uint64_t mem_total = UINT64_MAX, mem_available = UINT64_MAX;
124✔
207
        _cleanup_fclose_ FILE *f = NULL;
124✔
208
        int r;
124✔
209

210
        f = fopen("/proc/meminfo", "re");
124✔
211
        if (!f)
124✔
UNCOV
212
                return -errno;
×
213

214
        for (;;) {
372✔
215
                _cleanup_free_ char *line = NULL;
248✔
216
                uint64_t *v;
372✔
217
                char *p;
372✔
218

219
                r = read_line(f, LONG_LINE_MAX, &line);
372✔
220
                if (r < 0)
372✔
221
                        return r;
222
                if (r == 0)
372✔
223
                        return -EINVAL; /* EOF: Couldn't find one or both fields? */
224

225
                p = first_word(line, "MemTotal:");
372✔
226
                if (p)
372✔
227
                        v = &mem_total;
228
                else {
229
                        p = first_word(line, "MemAvailable:");
248✔
230
                        if (p)
248✔
231
                                v = &mem_available;
232
                        else
233
                                continue;
124✔
234
                }
235

236
                r = convert_meminfo_value_to_uint64_bytes(p, v);
248✔
237
                if (r < 0)
248✔
238
                        return r;
239

240
                if (mem_total != UINT64_MAX && mem_available != UINT64_MAX)
248✔
241
                        break;
242
        }
243

244
        if (mem_available > mem_total)
124✔
245
                return -EINVAL;
246

247
        if (ret_total)
124✔
248
                *ret_total = mem_total;
53✔
249
        if (ret_used)
124✔
250
                *ret_used = mem_total - mem_available;
71✔
251
        return 0;
252
}
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