Bug Summary

File:build-scan/../src/coredump/coredump-vacuum.c
Warning:line 204, column 48
Potential leak of memory pointed to by 'n'

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 coredump-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 static -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 test-coredump-vacuum.p -I . -I .. -I src/basic -I ../src/basic -I src/shared -I ../src/shared -I src/systemd -I ../src/systemd -I src/journal -I ../src/journal -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 -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 hidden -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/coredump/coredump-vacuum.c
1/* SPDX-License-Identifier: LGPL-2.1+ */
2
3#include <sys/statvfs.h>
4
5#include "alloc-util.h"
6#include "coredump-vacuum.h"
7#include "dirent-util.h"
8#include "fd-util.h"
9#include "fs-util.h"
10#include "hashmap.h"
11#include "macro.h"
12#include "string-util.h"
13#include "time-util.h"
14#include "user-util.h"
15#include "util.h"
16
17#define DEFAULT_MAX_USE_LOWER(uint64_t) (1ULL*1024ULL*1024ULL) (uint64_t) (1ULL*1024ULL*1024ULL) /* 1 MiB */
18#define DEFAULT_MAX_USE_UPPER(uint64_t) (4ULL*1024ULL*1024ULL*1024ULL) (uint64_t) (4ULL*1024ULL*1024ULL*1024ULL) /* 4 GiB */
19#define DEFAULT_KEEP_FREE_UPPER(uint64_t) (4ULL*1024ULL*1024ULL*1024ULL) (uint64_t) (4ULL*1024ULL*1024ULL*1024ULL) /* 4 GiB */
20#define DEFAULT_KEEP_FREE(uint64_t) (1024ULL*1024ULL) (uint64_t) (1024ULL*1024ULL) /* 1 MB */
21
22struct vacuum_candidate {
23 unsigned n_files;
24 char *oldest_file;
25 usec_t oldest_mtime;
26};
27
28static void vacuum_candidate_free(struct vacuum_candidate *c) {
29 if (!c)
30 return;
31
32 free(c->oldest_file);
33 free(c);
34}
35
36DEFINE_TRIVIAL_CLEANUP_FUNC(struct vacuum_candidate*, vacuum_candidate_free)static inline void vacuum_candidate_freep(struct vacuum_candidate
* *p) { if (*p) vacuum_candidate_free(*p); }
;
37
38static void vacuum_candidate_hashmap_free(Hashmap *h) {
39 hashmap_free_with_destructor(h, vacuum_candidate_free)({ ({ void *_item; while ((_item = hashmap_steal_first(h))) vacuum_candidate_free
(_item); }); hashmap_free(h); })
;
40}
41
42DEFINE_TRIVIAL_CLEANUP_FUNC(Hashmap*, vacuum_candidate_hashmap_free)static inline void vacuum_candidate_hashmap_freep(Hashmap* *p
) { if (*p) vacuum_candidate_hashmap_free(*p); }
;
43
44static int uid_from_file_name(const char *filename, uid_t *uid) {
45 const char *p, *e, *u;
46
47 p = startswith(filename, "core.");
48 if (!p)
49 return -EINVAL22;
50
51 /* Skip the comm field */
52 p = strchr(p, '.');
53 if (!p)
54 return -EINVAL22;
55 p++;
56
57 /* Find end up UID */
58 e = strchr(p, '.');
59 if (!e)
60 return -EINVAL22;
61
62 u = strndupa(p, e-p)(__extension__ ({ const char *__old = (p); size_t __len = strnlen
(__old, (e-p)); char *__new = (char *) __builtin_alloca (__len
+ 1); __new[__len] = '\0'; (char *) memcpy (__new, __old, __len
); }))
;
63 return parse_uid(u, uid);
64}
65
66static bool_Bool vacuum_necessary(int fd, uint64_t sum, uint64_t keep_free, uint64_t max_use) {
67 uint64_t fs_size = 0, fs_free = (uint64_t) -1;
68 struct statvfs sv;
69
70 assert(fd >= 0)do { if ((__builtin_expect(!!(!(fd >= 0)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("fd >= 0"), "../src/coredump/coredump-vacuum.c"
, 70, __PRETTY_FUNCTION__); } while (0)
;
71
72 if (fstatvfs(fd, &sv) >= 0) {
73 fs_size = sv.f_frsize * sv.f_blocks;
74 fs_free = sv.f_frsize * sv.f_bfree;
75 }
76
77 if (max_use == (uint64_t) -1) {
78
79 if (fs_size > 0) {
80 max_use = PAGE_ALIGN(fs_size / 10)ALIGN_TO((fs_size / 10), page_size()); /* 10% */
81
82 if (max_use > DEFAULT_MAX_USE_UPPER(uint64_t) (4ULL*1024ULL*1024ULL*1024ULL))
83 max_use = DEFAULT_MAX_USE_UPPER(uint64_t) (4ULL*1024ULL*1024ULL*1024ULL);
84
85 if (max_use < DEFAULT_MAX_USE_LOWER(uint64_t) (1ULL*1024ULL*1024ULL))
86 max_use = DEFAULT_MAX_USE_LOWER(uint64_t) (1ULL*1024ULL*1024ULL);
87 } else
88 max_use = DEFAULT_MAX_USE_LOWER(uint64_t) (1ULL*1024ULL*1024ULL);
89 } else
90 max_use = PAGE_ALIGN(max_use)ALIGN_TO((max_use), page_size());
91
92 if (max_use > 0 && sum > max_use)
93 return true1;
94
95 if (keep_free == (uint64_t) -1) {
96
97 if (fs_size > 0) {
98 keep_free = PAGE_ALIGN((fs_size * 3) / 20)ALIGN_TO(((fs_size * 3) / 20), page_size()); /* 15% */
99
100 if (keep_free > DEFAULT_KEEP_FREE_UPPER(uint64_t) (4ULL*1024ULL*1024ULL*1024ULL))
101 keep_free = DEFAULT_KEEP_FREE_UPPER(uint64_t) (4ULL*1024ULL*1024ULL*1024ULL);
102 } else
103 keep_free = DEFAULT_KEEP_FREE(uint64_t) (1024ULL*1024ULL);
104 } else
105 keep_free = PAGE_ALIGN(keep_free)ALIGN_TO((keep_free), page_size());
106
107 if (keep_free > 0 && fs_free < keep_free)
108 return true1;
109
110 return false0;
111}
112
113int coredump_vacuum(int exclude_fd, uint64_t keep_free, uint64_t max_use) {
114 _cleanup_closedir___attribute__((cleanup(closedirp))) DIR *d = NULL((void*)0);
115 struct stat exclude_st;
116 int r;
117
118 if (keep_free == 0 && max_use == 0)
1
Assuming 'keep_free' is not equal to 0
119 return 0;
120
121 if (exclude_fd >= 0) {
2
Assuming 'exclude_fd' is < 0
3
Taking false branch
122 if (fstat(exclude_fd, &exclude_st) < 0)
123 return log_error_errno(errno, "Failed to fstat(): %m")({ int _level = ((3)), _e = (((*__errno_location ()))), _realm
= (LOG_REALM_SYSTEMD); (log_get_max_level_realm(_realm) >=
((_level) & 0x07)) ? log_internal_realm(((_realm) <<
10 | (_level)), _e, "../src/coredump/coredump-vacuum.c", 123
, __func__, "Failed to fstat(): %m") : -abs(_e); })
;
124 }
125
126 /* This algorithm will keep deleting the oldest file of the
127 * user with the most coredumps until we are back in the size
128 * limits. Note that vacuuming for journal files is different,
129 * because we rely on rate-limiting of the messages there,
130 * to avoid being flooded. */
131
132 d = opendir("/var/lib/systemd/coredump");
133 if (!d) {
4
Assuming 'd' is non-null
5
Taking false branch
134 if (errno(*__errno_location ()) == ENOENT2)
135 return 0;
136
137 return log_error_errno(errno, "Can't open coredump directory: %m")({ int _level = ((3)), _e = (((*__errno_location ()))), _realm
= (LOG_REALM_SYSTEMD); (log_get_max_level_realm(_realm) >=
((_level) & 0x07)) ? log_internal_realm(((_realm) <<
10 | (_level)), _e, "../src/coredump/coredump-vacuum.c", 137
, __func__, "Can't open coredump directory: %m") : -abs(_e); }
)
;
138 }
139
140 for (;;) {
6
Loop condition is true. Entering loop body
141 _cleanup_(vacuum_candidate_hashmap_freep)__attribute__((cleanup(vacuum_candidate_hashmap_freep))) Hashmap *h = NULL((void*)0);
142 struct vacuum_candidate *worst = NULL((void*)0);
143 struct dirent *de;
144 uint64_t sum = 0;
145
146 rewinddir(d);
147
148 FOREACH_DIRENT(de, d, goto fail)for ((*__errno_location ()) = 0, de = readdir(d);; (*__errno_location
()) = 0, de = readdir(d)) if (!de) { if ((*__errno_location (
)) > 0) { goto fail; } break; } else if (hidden_or_backup_file
((de)->d_name)) continue; else
{
7
Loop condition is true. Entering loop body
8
Assuming 'de' is non-null
9
Taking false branch
10
Assuming the condition is false
11
Taking false branch
149 struct vacuum_candidate *c;
150 struct stat st;
151 uid_t uid;
152 usec_t t;
153
154 r = uid_from_file_name(de->d_name, &uid);
155 if (r < 0)
12
Assuming 'r' is >= 0
13
Taking false branch
156 continue;
157
158 if (fstatat(dirfd(d), de->d_name, &st, AT_NO_AUTOMOUNT0x800|AT_SYMLINK_NOFOLLOW0x100) < 0) {
14
Assuming the condition is false
15
Taking false branch
159 if (errno(*__errno_location ()) == ENOENT2)
160 continue;
161
162 log_warning_errno(errno, "Failed to stat /var/lib/systemd/coredump/%s: %m", de->d_name)({ int _level = ((4)), _e = (((*__errno_location ()))), _realm
= (LOG_REALM_SYSTEMD); (log_get_max_level_realm(_realm) >=
((_level) & 0x07)) ? log_internal_realm(((_realm) <<
10 | (_level)), _e, "../src/coredump/coredump-vacuum.c", 162
, __func__, "Failed to stat /var/lib/systemd/coredump/%s: %m"
, de->d_name) : -abs(_e); })
;
163 continue;
164 }
165
166 if (!S_ISREG(st.st_mode)((((st.st_mode)) & 0170000) == (0100000)))
16
Assuming the condition is true
17
Taking false branch
167 continue;
168
169 if (exclude_fd
17.1
'exclude_fd' is < 0
>= 0 &&
170 exclude_st.st_dev == st.st_dev &&
171 exclude_st.st_ino == st.st_ino)
172 continue;
173
174 r = hashmap_ensure_allocated(&h, NULL)internal_hashmap_ensure_allocated(&h, ((void*)0) );
175 if (r < 0)
18
Assuming 'r' is >= 0
19
Taking false branch
176 return log_oom()log_oom_internal(LOG_REALM_SYSTEMD, "../src/coredump/coredump-vacuum.c"
, 176, __func__)
;
177
178 t = timespec_load(&st.st_mtim);
179
180 c = hashmap_get(h, UID_TO_PTR(uid)((void*) (((uintptr_t) (uid))+1)));
181 if (c) {
20
Assuming 'c' is null
21
Taking false branch
182
183 if (t < c->oldest_mtime) {
184 char *n;
185
186 n = strdup(de->d_name);
187 if (!n)
188 return log_oom()log_oom_internal(LOG_REALM_SYSTEMD, "../src/coredump/coredump-vacuum.c"
, 188, __func__)
;
189
190 free(c->oldest_file);
191 c->oldest_file = n;
192 c->oldest_mtime = t;
193 }
194
195 } else {
196 _cleanup_(vacuum_candidate_freep)__attribute__((cleanup(vacuum_candidate_freep))) struct vacuum_candidate *n = NULL((void*)0);
197
198 n = new0(struct vacuum_candidate, 1)((struct vacuum_candidate*) calloc((1), sizeof(struct vacuum_candidate
)))
;
22
Memory is allocated
199 if (!n)
23
Assuming 'n' is non-null
24
Taking false branch
200 return log_oom()log_oom_internal(LOG_REALM_SYSTEMD, "../src/coredump/coredump-vacuum.c"
, 200, __func__)
;
201
202 n->oldest_file = strdup(de->d_name);
203 if (!n->oldest_file)
25
Assuming field 'oldest_file' is null
26
Taking true branch
204 return log_oom()log_oom_internal(LOG_REALM_SYSTEMD, "../src/coredump/coredump-vacuum.c"
, 204, __func__)
;
27
Potential leak of memory pointed to by 'n'
205
206 n->oldest_mtime = t;
207
208 r = hashmap_put(h, UID_TO_PTR(uid)((void*) (((uintptr_t) (uid))+1)), n);
209 if (r < 0)
210 return log_oom()log_oom_internal(LOG_REALM_SYSTEMD, "../src/coredump/coredump-vacuum.c"
, 210, __func__)
;
211
212 c = TAKE_PTR(n)({ typeof(n) _ptr_ = (n); (n) = ((void*)0); _ptr_; });
213 }
214
215 c->n_files++;
216
217 if (!worst ||
218 worst->n_files < c->n_files ||
219 (worst->n_files == c->n_files && c->oldest_mtime < worst->oldest_mtime))
220 worst = c;
221
222 sum += st.st_blocks * 512;
223 }
224
225 if (!worst)
226 break;
227
228 r = vacuum_necessary(dirfd(d), sum, keep_free, max_use);
229 if (r <= 0)
230 return r;
231
232 r = unlinkat_deallocate(dirfd(d), worst->oldest_file, 0);
233 if (r == -ENOENT2)
234 continue;
235 if (r < 0)
236 return log_error_errno(r, "Failed to remove file %s: %m", worst->oldest_file)({ int _level = ((3)), _e = ((r)), _realm = (LOG_REALM_SYSTEMD
); (log_get_max_level_realm(_realm) >= ((_level) & 0x07
)) ? log_internal_realm(((_realm) << 10 | (_level)), _e
, "../src/coredump/coredump-vacuum.c", 236, __func__, "Failed to remove file %s: %m"
, worst->oldest_file) : -abs(_e); })
;
237
238 log_info("Removed old coredump %s.", worst->oldest_file)({ int _level = (((6))), _e = ((0)), _realm = (LOG_REALM_SYSTEMD
); (log_get_max_level_realm(_realm) >= ((_level) & 0x07
)) ? log_internal_realm(((_realm) << 10 | (_level)), _e
, "../src/coredump/coredump-vacuum.c", 238, __func__, "Removed old coredump %s."
, worst->oldest_file) : -abs(_e); })
;
239 }
240
241 return 0;
242
243fail:
244 return log_error_errno(errno, "Failed to read directory: %m")({ int _level = ((3)), _e = (((*__errno_location ()))), _realm
= (LOG_REALM_SYSTEMD); (log_get_max_level_realm(_realm) >=
((_level) & 0x07)) ? log_internal_realm(((_realm) <<
10 | (_level)), _e, "../src/coredump/coredump-vacuum.c", 244
, __func__, "Failed to read directory: %m") : -abs(_e); })
;
245}