Bug Summary

File:build-scan/../src/basic/khash.c
Warning:line 141, column 25
Potential leak of memory pointed to by 'h'

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 khash.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/basic/libbasic.a.p -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 -I . -I .. -I /usr/include/blkid -I /usr/include/libmount -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/basic/khash.c
1/* SPDX-License-Identifier: LGPL-2.1+ */
2
3#include <linux1/if_alg.h>
4#include <stdbool.h>
5#include <sys/socket.h>
6
7#include "alloc-util.h"
8#include "fd-util.h"
9#include "hexdecoct.h"
10#include "khash.h"
11#include "macro.h"
12#include "missing.h"
13#include "string-util.h"
14#include "util.h"
15
16/* On current kernels the maximum digest (according to "grep digestsize /proc/crypto | sort -u") is actually 32, but
17 * let's add some extra room, the few wasted bytes don't really matter... */
18#define LONGEST_DIGEST128 128
19
20struct khash {
21 int fd;
22 char *algorithm;
23 uint8_t digest[LONGEST_DIGEST128+1];
24 size_t digest_size;
25 bool_Bool digest_valid;
26};
27
28int khash_supported(void) {
29 static const union {
30 struct sockaddr sa;
31 struct sockaddr_alg alg;
32 } sa = {
33 .alg.salg_family = AF_ALG38,
34 .alg.salg_type = "hash",
35 .alg.salg_name = "sha256", /* a very common algorithm */
36 };
37
38 static int cached = -1;
39
40 if (cached < 0) {
41 _cleanup_close___attribute__((cleanup(closep))) int fd1 = -1, fd2 = -1;
42 uint8_t buf[LONGEST_DIGEST128+1];
43
44 fd1 = socket(AF_ALG38, SOCK_SEQPACKETSOCK_SEQPACKET|SOCK_CLOEXECSOCK_CLOEXEC, 0);
45 if (fd1 < 0) {
46 /* The kernel returns EAFNOSUPPORT if AF_ALG is not supported at all */
47 if (IN_SET(errno, EAFNOSUPPORT, EOPNOTSUPP)({ _Bool _found = 0; static __attribute__ ((unused)) char _static_assert__macros_need_to_be_extended
[20 - sizeof((int[]){97, 95})/sizeof(int)]; switch((*__errno_location
())) { case 97: case 95: _found = 1; break; default: break; }
_found; })
)
48 return (cached = false0);
49
50 return -errno(*__errno_location ());
51 }
52
53 if (bind(fd1, &sa.sa, sizeof(sa)) < 0) {
54 /* The kernel returns ENOENT if the selected algorithm is not supported at all. We use a check
55 * for SHA256 as a proxy for whether the whole API is supported at all. After all it's one of
56 * the most common hash functions, and if it isn't supported, that's ample indication that
57 * something is really off. */
58
59 if (IN_SET(errno, ENOENT, EOPNOTSUPP)({ _Bool _found = 0; static __attribute__ ((unused)) char _static_assert__macros_need_to_be_extended
[20 - sizeof((int[]){2, 95})/sizeof(int)]; switch((*__errno_location
())) { case 2: case 95: _found = 1; break; default: break; }
_found; })
)
60 return (cached = false0);
61
62 return -errno(*__errno_location ());
63 }
64
65 fd2 = accept4(fd1, NULL((void*)0), 0, SOCK_CLOEXECSOCK_CLOEXEC);
66 if (fd2 < 0) {
67 if (errno(*__errno_location ()) == EOPNOTSUPP95)
68 return (cached = false0);
69
70 return -errno(*__errno_location ());
71 }
72
73 if (recv(fd2, buf, sizeof(buf), 0) < 0) {
74 /* On some kernels we get ENOKEY for non-keyed hash functions (such as sha256), let's refuse
75 * using the API in those cases, since the kernel is
76 * broken. https://github.com/systemd/systemd/issues/8278 */
77
78 if (IN_SET(errno, ENOKEY, EOPNOTSUPP)({ _Bool _found = 0; static __attribute__ ((unused)) char _static_assert__macros_need_to_be_extended
[20 - sizeof((int[]){126, 95})/sizeof(int)]; switch((*__errno_location
())) { case 126: case 95: _found = 1; break; default: break;
} _found; })
)
79 return (cached = false0);
80 }
81
82 cached = true1;
83 }
84
85 return cached;
86}
87
88int khash_new_with_key(khash **ret, const char *algorithm, const void *key, size_t key_size) {
89 union {
90 struct sockaddr sa;
91 struct sockaddr_alg alg;
92 } sa = {
93 .alg.salg_family = AF_ALG38,
94 .alg.salg_type = "hash",
95 };
96
97 _cleanup_(khash_unrefp)__attribute__((cleanup(khash_unrefp))) khash *h = NULL((void*)0);
98 _cleanup_close___attribute__((cleanup(closep))) int fd = -1;
99 int supported;
100 ssize_t n;
101
102 assert(ret)do { if ((__builtin_expect(!!(!(ret)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("ret"), "../src/basic/khash.c", 102, __PRETTY_FUNCTION__
); } while (0)
;
2
Assuming 'ret' is non-null
3
Taking false branch
4
Loop condition is false. Exiting loop
103 assert(key || key_size == 0)do { if ((__builtin_expect(!!(!(key || key_size == 0)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("key || key_size == 0"), "../src/basic/khash.c"
, 103, __PRETTY_FUNCTION__); } while (0)
;
5
Taking false branch
6
Loop condition is false. Exiting loop
104
105 /* Filter out an empty algorithm early, as we do not support an algorithm by that name. */
106 if (isempty(algorithm))
7
Taking false branch
107 return -EINVAL22;
108
109 /* Overly long hash algorithm names we definitely do not support */
110 if (strlen(algorithm) >= sizeof(sa.alg.salg_name))
8
Assuming the condition is false
9
Taking false branch
111 return -EOPNOTSUPP95;
112
113 supported = khash_supported();
114 if (supported < 0)
10
Assuming 'supported' is >= 0
11
Taking false branch
115 return supported;
116 if (supported == 0)
12
Assuming 'supported' is not equal to 0
13
Taking false branch
117 return -EOPNOTSUPP95;
118
119 fd = socket(AF_ALG38, SOCK_SEQPACKETSOCK_SEQPACKET|SOCK_CLOEXECSOCK_CLOEXEC, 0);
120 if (fd < 0)
14
Assuming 'fd' is >= 0
15
Taking false branch
121 return -errno(*__errno_location ());
122
123 strcpy((char*) sa.alg.salg_name, algorithm);
124 if (bind(fd, &sa.sa, sizeof(sa)) < 0) {
16
Assuming the condition is false
17
Taking false branch
125 if (errno(*__errno_location ()) == ENOENT2)
126 return -EOPNOTSUPP95;
127 return -errno(*__errno_location ());
128 }
129
130 if (key
17.1
'key' is null
) {
18
Taking false branch
131 if (setsockopt(fd, SOL_ALG279, ALG_SET_KEY1, key, key_size) < 0)
132 return -errno(*__errno_location ());
133 }
134
135 h = new0(khash, 1)((khash*) calloc((1), sizeof(khash)));
19
Memory is allocated
136 if (!h)
20
Assuming 'h' is non-null
21
Taking false branch
137 return -ENOMEM12;
138
139 h->fd = accept4(fd, NULL((void*)0), 0, SOCK_CLOEXECSOCK_CLOEXEC);
140 if (h->fd < 0)
22
Assuming field 'fd' is < 0
23
Taking true branch
141 return -errno(*__errno_location ());
24
Potential leak of memory pointed to by 'h'
142
143 h->algorithm = strdup(algorithm);
144 if (!h->algorithm)
145 return -ENOMEM12;
146
147 /* Temporary fix for rc kernel bug: https://bugzilla.redhat.com/show_bug.cgi?id=1395896 */
148 (void) send(h->fd, NULL((void*)0), 0, 0);
149
150 /* Figure out the digest size */
151 n = recv(h->fd, h->digest, sizeof(h->digest), 0);
152 if (n < 0)
153 return -errno(*__errno_location ());
154 if (n >= LONGEST_DIGEST128) /* longer than what we expected? If so, we don't support this */
155 return -EOPNOTSUPP95;
156
157 h->digest_size = (size_t) n;
158 h->digest_valid = true1;
159
160 /* Temporary fix for rc kernel bug: https://bugzilla.redhat.com/show_bug.cgi?id=1395896 */
161 (void) send(h->fd, NULL((void*)0), 0, 0);
162
163 *ret = h;
164 h = NULL((void*)0);
165
166 return 0;
167}
168
169int khash_new(khash **ret, const char *algorithm) {
170 return khash_new_with_key(ret, algorithm, NULL((void*)0), 0);
1
Calling 'khash_new_with_key'
171}
172
173khash* khash_unref(khash *h) {
174 if (!h)
175 return NULL((void*)0);
176
177 safe_close(h->fd);
178 free(h->algorithm);
179 return mfree(h);
180}
181
182int khash_dup(khash *h, khash **ret) {
183 _cleanup_(khash_unrefp)__attribute__((cleanup(khash_unrefp))) khash *copy = NULL((void*)0);
184
185 assert(h)do { if ((__builtin_expect(!!(!(h)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("h"), "../src/basic/khash.c", 185, __PRETTY_FUNCTION__
); } while (0)
;
186 assert(ret)do { if ((__builtin_expect(!!(!(ret)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("ret"), "../src/basic/khash.c", 186, __PRETTY_FUNCTION__
); } while (0)
;
187
188 copy = newdup(khash, h, 1)((khash*) memdup_multiply(h, sizeof(khash), (1)));
189 if (!copy)
190 return -ENOMEM12;
191
192 copy->fd = -1;
193 copy->algorithm = strdup(h->algorithm);
194 if (!copy->algorithm)
195 return -ENOMEM12;
196
197 copy->fd = accept4(h->fd, NULL((void*)0), 0, SOCK_CLOEXECSOCK_CLOEXEC);
198 if (copy->fd < 0)
199 return -errno(*__errno_location ());
200
201 *ret = TAKE_PTR(copy)({ typeof(copy) _ptr_ = (copy); (copy) = ((void*)0); _ptr_; }
)
;
202
203 return 0;
204}
205
206const char *khash_get_algorithm(khash *h) {
207 assert(h)do { if ((__builtin_expect(!!(!(h)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("h"), "../src/basic/khash.c", 207, __PRETTY_FUNCTION__
); } while (0)
;
208
209 return h->algorithm;
210}
211
212size_t khash_get_size(khash *h) {
213 assert(h)do { if ((__builtin_expect(!!(!(h)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("h"), "../src/basic/khash.c", 213, __PRETTY_FUNCTION__
); } while (0)
;
214
215 return h->digest_size;
216}
217
218int khash_reset(khash *h) {
219 ssize_t n;
220
221 assert(h)do { if ((__builtin_expect(!!(!(h)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("h"), "../src/basic/khash.c", 221, __PRETTY_FUNCTION__
); } while (0)
;
222
223 n = send(h->fd, NULL((void*)0), 0, 0);
224 if (n < 0)
225 return -errno(*__errno_location ());
226
227 h->digest_valid = false0;
228
229 return 0;
230}
231
232int khash_put(khash *h, const void *buffer, size_t size) {
233 ssize_t n;
234
235 assert(h)do { if ((__builtin_expect(!!(!(h)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("h"), "../src/basic/khash.c", 235, __PRETTY_FUNCTION__
); } while (0)
;
236 assert(buffer || size == 0)do { if ((__builtin_expect(!!(!(buffer || size == 0)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("buffer || size == 0"), "../src/basic/khash.c"
, 236, __PRETTY_FUNCTION__); } while (0)
;
237
238 if (size <= 0)
239 return 0;
240
241 n = send(h->fd, buffer, size, MSG_MOREMSG_MORE);
242 if (n < 0)
243 return -errno(*__errno_location ());
244
245 h->digest_valid = false0;
246
247 return 0;
248}
249
250int khash_put_iovec(khash *h, const struct iovec *iovec, size_t n) {
251 struct msghdr mh = {
252 mh.msg_iov = (struct iovec*) iovec,
253 mh.msg_iovlen = n,
254 };
255 ssize_t k;
256
257 assert(h)do { if ((__builtin_expect(!!(!(h)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("h"), "../src/basic/khash.c", 257, __PRETTY_FUNCTION__
); } while (0)
;
258 assert(iovec || n == 0)do { if ((__builtin_expect(!!(!(iovec || n == 0)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("iovec || n == 0"), "../src/basic/khash.c"
, 258, __PRETTY_FUNCTION__); } while (0)
;
259
260 if (n <= 0)
261 return 0;
262
263 k = sendmsg(h->fd, &mh, MSG_MOREMSG_MORE);
264 if (k < 0)
265 return -errno(*__errno_location ());
266
267 h->digest_valid = false0;
268
269 return 0;
270}
271
272static int retrieve_digest(khash *h) {
273 ssize_t n;
274
275 assert(h)do { if ((__builtin_expect(!!(!(h)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("h"), "../src/basic/khash.c", 275, __PRETTY_FUNCTION__
); } while (0)
;
276
277 if (h->digest_valid)
278 return 0;
279
280 n = recv(h->fd, h->digest, h->digest_size, 0);
281 if (n < 0)
282 return n;
283 if ((size_t) n != h->digest_size) /* digest size changed? */
284 return -EIO5;
285
286 h->digest_valid = true1;
287
288 return 0;
289}
290
291int khash_digest_data(khash *h, const void **ret) {
292 int r;
293
294 assert(h)do { if ((__builtin_expect(!!(!(h)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("h"), "../src/basic/khash.c", 294, __PRETTY_FUNCTION__
); } while (0)
;
295 assert(ret)do { if ((__builtin_expect(!!(!(ret)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("ret"), "../src/basic/khash.c", 295, __PRETTY_FUNCTION__
); } while (0)
;
296
297 r = retrieve_digest(h);
298 if (r < 0)
299 return r;
300
301 *ret = h->digest;
302 return 0;
303}
304
305int khash_digest_string(khash *h, char **ret) {
306 int r;
307 char *p;
308
309 assert(h)do { if ((__builtin_expect(!!(!(h)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("h"), "../src/basic/khash.c", 309, __PRETTY_FUNCTION__
); } while (0)
;
310 assert(ret)do { if ((__builtin_expect(!!(!(ret)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("ret"), "../src/basic/khash.c", 310, __PRETTY_FUNCTION__
); } while (0)
;
311
312 r = retrieve_digest(h);
313 if (r < 0)
314 return r;
315
316 p = hexmem(h->digest, h->digest_size);
317 if (!p)
318 return -ENOMEM12;
319
320 *ret = p;
321 return 0;
322}