Bug Summary

File:build-scan/../src/journal/journal-vacuum.c
Warning:line 209, column 33
Potential leak of memory pointed to by 'p'

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-unknown-linux-gnu -analyze -disable-free -disable-llvm-verifier -discard-value-names -main-file-name journal-vacuum.c -analyzer-store=region -analyzer-opt-analyze-nested-blocks -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model pic -pic-level 2 -fhalf-no-semantic-interposition -mframe-pointer=all -relaxed-aliasing -menable-no-infs -menable-no-nans -menable-unsafe-fp-math -fno-signed-zeros -mreassociate -freciprocal-math -fdenormal-fp-math=preserve-sign,preserve-sign -ffp-contract=fast -fno-rounding-math -ffast-math -ffinite-math-only -mconstructor-aliases -munwind-tables -target-cpu x86-64 -tune-cpu generic -fno-split-dwarf-inlining -debugger-tuning=gdb -resource-dir /usr/lib64/clang/12.0.0 -include config.h -I src/journal/libjournal-client.a.p -I src/journal -I ../src/journal -I src/basic -I ../src/basic -I src/shared -I ../src/shared -I src/systemd -I ../src/systemd -I src/journal-remote -I ../src/journal-remote -I src/nspawn -I ../src/nspawn -I src/resolve -I ../src/resolve -I src/timesync -I ../src/timesync -I ../src/time-wait-sync -I src/login -I ../src/login -I src/udev -I ../src/udev -I src/libudev -I ../src/libudev -I src/core -I ../src/core -I ../src/libsystemd/sd-bus -I ../src/libsystemd/sd-device -I ../src/libsystemd/sd-hwdb -I ../src/libsystemd/sd-id128 -I ../src/libsystemd/sd-netlink -I ../src/libsystemd/sd-network -I src/libsystemd-network -I ../src/libsystemd-network -I . -I .. -D _FILE_OFFSET_BITS=64 -internal-isystem /usr/local/include -internal-isystem /usr/lib64/clang/12.0.0/include -internal-externc-isystem /include -internal-externc-isystem /usr/include -Wwrite-strings -Wno-unused-parameter -Wno-missing-field-initializers -Wno-unused-result -Wno-format-signedness -Wno-error=nonnull -std=gnu99 -fconst-strings -fdebug-compilation-dir /home/mrc0mmand/repos/@redhat-plumbers/systemd-rhel8/build-scan -ferror-limit 19 -fvisibility default -stack-protector 2 -fgnuc-version=4.2.1 -fcolor-diagnostics -analyzer-output=html -faddrsig -o /tmp/scan-build-2021-07-16-221226-1465241-1 -x c ../src/journal/journal-vacuum.c
1/* SPDX-License-Identifier: LGPL-2.1+ */
2
3#include <fcntl.h>
4#include <sys/stat.h>
5#include <unistd.h>
6
7#include "sd-id128.h"
8
9#include "alloc-util.h"
10#include "dirent-util.h"
11#include "fd-util.h"
12#include "fs-util.h"
13#include "journal-def.h"
14#include "journal-file.h"
15#include "journal-vacuum.h"
16#include "parse-util.h"
17#include "string-util.h"
18#include "util.h"
19#include "xattr-util.h"
20
21struct vacuum_info {
22 uint64_t usage;
23 char *filename;
24
25 uint64_t realtime;
26
27 sd_id128_t seqnum_id;
28 uint64_t seqnum;
29 bool_Bool have_seqnum;
30};
31
32static int vacuum_compare(const void *_a, const void *_b) {
33 const struct vacuum_info *a, *b;
34
35 a = _a;
36 b = _b;
37
38 if (a->have_seqnum && b->have_seqnum &&
39 sd_id128_equal(a->seqnum_id, b->seqnum_id)) {
40 if (a->seqnum < b->seqnum)
41 return -1;
42 else if (a->seqnum > b->seqnum)
43 return 1;
44 else
45 return 0;
46 }
47
48 if (a->realtime < b->realtime)
49 return -1;
50 else if (a->realtime > b->realtime)
51 return 1;
52 else if (a->have_seqnum && b->have_seqnum)
53 return memcmp(&a->seqnum_id, &b->seqnum_id, 16);
54 else
55 return strcmp(a->filename, b->filename);
56}
57
58static void patch_realtime(
59 int fd,
60 const char *fn,
61 const struct stat *st,
62 unsigned long long *realtime) {
63
64 usec_t x, crtime = 0;
65
66 /* The timestamp was determined by the file name, but let's
67 * see if the file might actually be older than the file name
68 * suggested... */
69
70 assert(fd >= 0)do { if ((__builtin_expect(!!(!(fd >= 0)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("fd >= 0"), "../src/journal/journal-vacuum.c"
, 70, __PRETTY_FUNCTION__); } while (0)
;
71 assert(fn)do { if ((__builtin_expect(!!(!(fn)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("fn"), "../src/journal/journal-vacuum.c"
, 71, __PRETTY_FUNCTION__); } while (0)
;
72 assert(st)do { if ((__builtin_expect(!!(!(st)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("st"), "../src/journal/journal-vacuum.c"
, 72, __PRETTY_FUNCTION__); } while (0)
;
73 assert(realtime)do { if ((__builtin_expect(!!(!(realtime)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("realtime"), "../src/journal/journal-vacuum.c"
, 73, __PRETTY_FUNCTION__); } while (0)
;
74
75 x = timespec_load(&st->st_ctim);
76 if (x > 0 && x != USEC_INFINITY((usec_t) -1) && x < *realtime)
77 *realtime = x;
78
79 x = timespec_load(&st->st_atim);
80 if (x > 0 && x != USEC_INFINITY((usec_t) -1) && x < *realtime)
81 *realtime = x;
82
83 x = timespec_load(&st->st_mtim);
84 if (x > 0 && x != USEC_INFINITY((usec_t) -1) && x < *realtime)
85 *realtime = x;
86
87 /* Let's read the original creation time, if possible. Ideally
88 * we'd just query the creation time the FS might provide, but
89 * unfortunately there's currently no sane API to query
90 * it. Hence let's implement this manually... */
91
92 if (fd_getcrtime_at(fd, fn, &crtime, 0) >= 0) {
93 if (crtime < *realtime)
94 *realtime = crtime;
95 }
96}
97
98static int journal_file_empty(int dir_fd, const char *name) {
99 _cleanup_close___attribute__((cleanup(closep))) int fd;
100 struct stat st;
101 le64_t n_entries;
102 ssize_t n;
103
104 fd = openat(dir_fd, name, O_RDONLY00|O_CLOEXEC02000000|O_NOFOLLOW0400000|O_NONBLOCK04000|O_NOATIME01000000);
105 if (fd < 0) {
106 /* Maybe failed due to O_NOATIME and lack of privileges? */
107 fd = openat(dir_fd, name, O_RDONLY00|O_CLOEXEC02000000|O_NOFOLLOW0400000|O_NONBLOCK04000);
108 if (fd < 0)
109 return -errno(*__errno_location ());
110 }
111
112 if (fstat(fd, &st) < 0)
113 return -errno(*__errno_location ());
114
115 /* If an offline file doesn't even have a header we consider it empty */
116 if (st.st_size < (off_t) sizeof(Header))
117 return 1;
118
119 /* If the number of entries is empty, we consider it empty, too */
120 n = pread(fd, &n_entries, sizeof(n_entries), offsetof(Header, n_entries)__builtin_offsetof(Header, n_entries));
121 if (n < 0)
122 return -errno(*__errno_location ());
123 if (n != sizeof(n_entries))
124 return -EIO5;
125
126 return le64toh(n_entries) <= 0;
127}
128
129int journal_directory_vacuum(
130 const char *directory,
131 uint64_t max_use,
132 uint64_t n_max_files,
133 usec_t max_retention_usec,
134 usec_t *oldest_usec,
135 bool_Bool verbose) {
136
137 _cleanup_closedir___attribute__((cleanup(closedirp))) DIR *d = NULL((void*)0);
138 struct vacuum_info *list = NULL((void*)0);
139 unsigned n_list = 0, i, n_active_files = 0;
140 size_t n_allocated = 0;
141 uint64_t sum = 0, freed = 0;
142 usec_t retention_limit = 0;
143 char sbytes[FORMAT_BYTES_MAX8];
144 struct dirent *de;
145 int r;
146
147 assert(directory)do { if ((__builtin_expect(!!(!(directory)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("directory"), "../src/journal/journal-vacuum.c"
, 147, __PRETTY_FUNCTION__); } while (0)
;
1
Assuming 'directory' is non-null
2
Taking false branch
3
Loop condition is false. Exiting loop
148
149 if (max_use <= 0 && max_retention_usec <= 0 && n_max_files <= 0)
4
Assuming 'max_use' is > 0
150 return 0;
151
152 if (max_retention_usec > 0) {
5
Assuming 'max_retention_usec' is <= 0
6
Taking false branch
153 retention_limit = now(CLOCK_REALTIME0);
154 if (retention_limit > max_retention_usec)
155 retention_limit -= max_retention_usec;
156 else
157 max_retention_usec = retention_limit = 0;
158 }
159
160 d = opendir(directory);
161 if (!d)
7
Assuming 'd' is non-null
8
Taking false branch
162 return -errno(*__errno_location ());
163
164 FOREACH_DIRENT_ALL(de, d, r = -errno; goto finish)for ((*__errno_location ()) = 0, de = readdir(d);; (*__errno_location
()) = 0, de = readdir(d)) if (!de) { if ((*__errno_location (
)) > 0) { r = -(*__errno_location ()); goto finish; } break
; } else
{
9
Loop condition is true. Entering loop body
10
Assuming 'de' is non-null
11
Taking false branch
165
166 unsigned long long seqnum = 0, realtime;
167 _cleanup_free___attribute__((cleanup(freep))) char *p = NULL((void*)0);
168 sd_id128_t seqnum_id;
169 bool_Bool have_seqnum;
170 uint64_t size;
171 struct stat st;
172 size_t q;
173
174 if (fstatat(dirfd(d), de->d_name, &st, AT_SYMLINK_NOFOLLOW0x100) < 0) {
12
Assuming the condition is false
13
Taking false branch
175 log_debug_errno(errno, "Failed to stat file %s while vacuuming, ignoring: %m", de->d_name)({ int _level = ((7)), _e = (((*__errno_location ()))), _realm
= (LOG_REALM_SYSTEMD); (log_get_max_level_realm(_realm) >=
((_level) & 0x07)) ? log_internal_realm(((_realm) <<
10 | (_level)), _e, "../src/journal/journal-vacuum.c", 175, __func__
, "Failed to stat file %s while vacuuming, ignoring: %m", de->
d_name) : -abs(_e); })
;
176 continue;
177 }
178
179 if (!S_ISREG(st.st_mode)((((st.st_mode)) & 0170000) == (0100000)))
14
Assuming the condition is true
15
Taking false branch
180 continue;
181
182 q = strlen(de->d_name);
183
184 if (endswith(de->d_name, ".journal")) {
16
Assuming the condition is true
17
Taking true branch
185
186 /* Vacuum archived files. Active files are
187 * left around */
188
189 if (q < 1 + 32 + 1 + 16 + 1 + 16 + 8) {
18
Assuming the condition is false
19
Taking false branch
190 n_active_files++;
191 continue;
192 }
193
194 if (de->d_name[q-8-16-1] != '-' ||
20
Assuming the condition is false
23
Taking false branch
195 de->d_name[q-8-16-1-16-1] != '-' ||
21
Assuming the condition is false
196 de->d_name[q-8-16-1-16-1-32-1] != '@') {
22
Assuming the condition is false
197 n_active_files++;
198 continue;
199 }
200
201 p = strdup(de->d_name);
24
Memory is allocated
202 if (!p) {
25
Assuming 'p' is non-null
26
Taking false branch
203 r = -ENOMEM12;
204 goto finish;
205 }
206
207 de->d_name[q-8-16-1-16-1] = 0;
208 if (sd_id128_from_string(de->d_name + q-8-16-1-16-1-32, &seqnum_id) < 0) {
27
Assuming the condition is true
28
Taking true branch
209 n_active_files++;
29
Potential leak of memory pointed to by 'p'
210 continue;
211 }
212
213 if (sscanf(de->d_name + q-8-16-1-16, "%16llx-%16llx.journal", &seqnum, &realtime) != 2) {
214 n_active_files++;
215 continue;
216 }
217
218 have_seqnum = true1;
219
220 } else if (endswith(de->d_name, ".journal~")) {
221 unsigned long long tmp;
222
223 /* Vacuum corrupted files */
224
225 if (q < 1 + 16 + 1 + 16 + 8 + 1) {
226 n_active_files++;
227 continue;
228 }
229
230 if (de->d_name[q-1-8-16-1] != '-' ||
231 de->d_name[q-1-8-16-1-16-1] != '@') {
232 n_active_files++;
233 continue;
234 }
235
236 p = strdup(de->d_name);
237 if (!p) {
238 r = -ENOMEM12;
239 goto finish;
240 }
241
242 if (sscanf(de->d_name + q-1-8-16-1-16, "%16llx-%16llx.journal~", &realtime, &tmp) != 2) {
243 n_active_files++;
244 continue;
245 }
246
247 have_seqnum = false0;
248 } else {
249 /* We do not vacuum unknown files! */
250 log_debug("Not vacuuming unknown file %s.", de->d_name)({ int _level = (((7))), _e = ((0)), _realm = (LOG_REALM_SYSTEMD
); (log_get_max_level_realm(_realm) >= ((_level) & 0x07
)) ? log_internal_realm(((_realm) << 10 | (_level)), _e
, "../src/journal/journal-vacuum.c", 250, __func__, "Not vacuuming unknown file %s."
, de->d_name) : -abs(_e); })
;
251 continue;
252 }
253
254 size = 512UL * (uint64_t) st.st_blocks;
255
256 r = journal_file_empty(dirfd(d), p);
257 if (r < 0) {
258 log_debug_errno(r, "Failed check if %s is empty, ignoring: %m", p)({ int _level = ((7)), _e = ((r)), _realm = (LOG_REALM_SYSTEMD
); (log_get_max_level_realm(_realm) >= ((_level) & 0x07
)) ? log_internal_realm(((_realm) << 10 | (_level)), _e
, "../src/journal/journal-vacuum.c", 258, __func__, "Failed check if %s is empty, ignoring: %m"
, p) : -abs(_e); })
;
259 continue;
260 }
261 if (r > 0) {
262 /* Always vacuum empty non-online files. */
263
264 r = unlinkat_deallocate(dirfd(d), p, 0);
265 if (r >= 0) {
266
267 log_full(verbose ? LOG_INFO : LOG_DEBUG,({ int _level = (((verbose ? 6 : 7))), _e = ((0)), _realm = (
LOG_REALM_SYSTEMD); (log_get_max_level_realm(_realm) >= ((
_level) & 0x07)) ? log_internal_realm(((_realm) << 10
| (_level)), _e, "../src/journal/journal-vacuum.c", 268, __func__
, "Deleted empty archived journal %s/%s (%s).", directory, p,
format_bytes(sbytes, sizeof(sbytes), size)) : -abs(_e); })
268 "Deleted empty archived journal %s/%s (%s).", directory, p, format_bytes(sbytes, sizeof(sbytes), size))({ int _level = (((verbose ? 6 : 7))), _e = ((0)), _realm = (
LOG_REALM_SYSTEMD); (log_get_max_level_realm(_realm) >= ((
_level) & 0x07)) ? log_internal_realm(((_realm) << 10
| (_level)), _e, "../src/journal/journal-vacuum.c", 268, __func__
, "Deleted empty archived journal %s/%s (%s).", directory, p,
format_bytes(sbytes, sizeof(sbytes), size)) : -abs(_e); })
;
269
270 freed += size;
271 } else if (r != -ENOENT2)
272 log_warning_errno(r, "Failed to delete empty archived journal %s/%s: %m", directory, p)({ int _level = ((4)), _e = ((r)), _realm = (LOG_REALM_SYSTEMD
); (log_get_max_level_realm(_realm) >= ((_level) & 0x07
)) ? log_internal_realm(((_realm) << 10 | (_level)), _e
, "../src/journal/journal-vacuum.c", 272, __func__, "Failed to delete empty archived journal %s/%s: %m"
, directory, p) : -abs(_e); })
;
273
274 continue;
275 }
276
277 patch_realtime(dirfd(d), p, &st, &realtime);
278
279 if (!GREEDY_REALLOC(list, n_allocated, n_list + 1)greedy_realloc((void**) &(list), &(n_allocated), (n_list
+ 1), sizeof((list)[0]))
) {
280 r = -ENOMEM12;
281 goto finish;
282 }
283
284 list[n_list].filename = TAKE_PTR(p)({ typeof(p) _ptr_ = (p); (p) = ((void*)0); _ptr_; });
285 list[n_list].usage = size;
286 list[n_list].seqnum = seqnum;
287 list[n_list].realtime = realtime;
288 list[n_list].seqnum_id = seqnum_id;
289 list[n_list].have_seqnum = have_seqnum;
290 n_list++;
291
292 sum += size;
293 }
294
295 qsort_safe(list, n_list, sizeof(struct vacuum_info), vacuum_compare);
296
297 for (i = 0; i < n_list; i++) {
298 unsigned left;
299
300 left = n_active_files + n_list - i;
301
302 if ((max_retention_usec <= 0 || list[i].realtime >= retention_limit) &&
303 (max_use <= 0 || sum <= max_use) &&
304 (n_max_files <= 0 || left <= n_max_files))
305 break;
306
307 r = unlinkat_deallocate(dirfd(d), list[i].filename, 0);
308 if (r >= 0) {
309 log_full(verbose ? LOG_INFO : LOG_DEBUG, "Deleted archived journal %s/%s (%s).", directory, list[i].filename, format_bytes(sbytes, sizeof(sbytes), list[i].usage))({ int _level = (((verbose ? 6 : 7))), _e = ((0)), _realm = (
LOG_REALM_SYSTEMD); (log_get_max_level_realm(_realm) >= ((
_level) & 0x07)) ? log_internal_realm(((_realm) << 10
| (_level)), _e, "../src/journal/journal-vacuum.c", 309, __func__
, "Deleted archived journal %s/%s (%s).", directory, list[i].
filename, format_bytes(sbytes, sizeof(sbytes), list[i].usage)
) : -abs(_e); })
;
310 freed += list[i].usage;
311
312 if (list[i].usage < sum)
313 sum -= list[i].usage;
314 else
315 sum = 0;
316
317 } else if (r != -ENOENT2)
318 log_warning_errno(r, "Failed to delete archived journal %s/%s: %m", directory, list[i].filename)({ int _level = ((4)), _e = ((r)), _realm = (LOG_REALM_SYSTEMD
); (log_get_max_level_realm(_realm) >= ((_level) & 0x07
)) ? log_internal_realm(((_realm) << 10 | (_level)), _e
, "../src/journal/journal-vacuum.c", 318, __func__, "Failed to delete archived journal %s/%s: %m"
, directory, list[i].filename) : -abs(_e); })
;
319 }
320
321 if (oldest_usec && i < n_list && (*oldest_usec == 0 || list[i].realtime < *oldest_usec))
322 *oldest_usec = list[i].realtime;
323
324 r = 0;
325
326finish:
327 for (i = 0; i < n_list; i++)
328 free(list[i].filename);
329 free(list);
330
331 log_full(verbose ? LOG_INFO : LOG_DEBUG, "Vacuuming done, freed %s of archived journals from %s.", format_bytes(sbytes, sizeof(sbytes), freed), directory)({ int _level = (((verbose ? 6 : 7))), _e = ((0)), _realm = (
LOG_REALM_SYSTEMD); (log_get_max_level_realm(_realm) >= ((
_level) & 0x07)) ? log_internal_realm(((_realm) << 10
| (_level)), _e, "../src/journal/journal-vacuum.c", 331, __func__
, "Vacuuming done, freed %s of archived journals from %s.", format_bytes
(sbytes, sizeof(sbytes), freed), directory) : -abs(_e); })
;
332
333 return r;
334}