Bug Summary

File:lib/sd-daemon.c
Warning:line 227, column 9
Attempt to free released memory

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-pc-linux-gnu -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name sd-daemon.c -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 -fmath-errno -ffp-contract=on -fno-rounding-math -mconstructor-aliases -fno-plt -funwind-tables=2 -target-cpu x86-64 -tune-cpu generic -debugger-tuning=gdb -fcoverage-compilation-dir=/home/buildbot/lwan-worker/clang-analyze/build/src/lib -resource-dir /usr/lib/clang/16 -include /home/buildbot/lwan-worker/clang-analyze/build/lwan-build-config.h -D _FILE_OFFSET_BITS=64 -D _TIME_BITS=64 -I /home/buildbot/lwan-worker/clang-analyze/build/src/lib/missing -I /home/buildbot/lwan-worker/clang-analyze/build -I /usr/include/luajit-2.1 -I /usr/include/valgrind -I /home/buildbot/lwan-worker/clang-analyze/build/src/lib -internal-isystem /usr/lib/clang/16/include -internal-isystem /usr/local/include -internal-isystem /usr/bin/../lib64/gcc/x86_64-pc-linux-gnu/13.2.1/../../../../x86_64-pc-linux-gnu/include -internal-externc-isystem /include -internal-externc-isystem /usr/include -Wno-unused-parameter -Wno-override-init -Wno-free-nonheap-object -std=gnu11 -fdebug-compilation-dir=/home/buildbot/lwan-worker/clang-analyze/build/src/lib -ferror-limit 19 -stack-protector 2 -fgnuc-version=4.2.1 -analyzer-output=html -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /home/buildbot/lwan-worker/clang-analyze/CLANG/2023-11-16-002104-2552623-1 -x c /home/buildbot/lwan-worker/clang-analyze/build/src/lib/sd-daemon.c
1/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3/***
4 This file is based on sd-daemon.c from systemd. Unused code has been
5 removed.
6
7 Copyright 2010 Lennart Poettering
8
9 systemd is free software; you can redistribute it and/or modify it
10 under the terms of the GNU Lesser General Public License as published by
11 the Free Software Foundation; either version 2.1 of the License, or
12 (at your option) any later version.
13
14 systemd is distributed in the hope that it will be useful, but
15 WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 Lesser General Public License for more details.
18
19 You should have received a copy of the GNU Lesser General Public License
20 along with systemd; If not, see <http://www.gnu.org/licenses/>.
21***/
22
23#define _GNU_SOURCE
24#include <assert.h>
25#include <errno(*__errno_location ()).h>
26#include <fcntl.h>
27#include <limits.h>
28#include <netinet/in.h>
29#include <stdlib.h>
30#include <sys/resource.h>
31#include <sys/socket.h>
32#include <sys/stat.h>
33#include <sys/types.h>
34#include <unistd.h>
35
36#include "sd-daemon.h"
37#include "lwan-config.h"
38
39static void unsetenv_listen_vars(void) {
40 unsetenv("LISTEN_PID");
41 unsetenv("LISTEN_FDS");
42 unsetenv("LISTEN_FDNAMES");
43}
44
45int sd_listen_fds(int unset_environment) {
46 int n, l, r, fd;
47 const char *e;
48
49 e = getenv("LISTEN_PID");
50 if (!e) {
51 r = 0;
52 goto finish;
53 }
54
55 l = parse_int(e, -1);
56 if (l <= 0) {
57 r = -EINVAL22;
58 goto finish;
59 }
60
61 /* Is this for us? */
62 if (getpid() != (pid_t)l) {
63 r = 0;
64 goto finish;
65 }
66
67 e = getenv("LISTEN_FDS");
68 if (!e) {
69 r = 0;
70 goto finish;
71 }
72
73 n = parse_int(e, -1);
74 if (!n) {
75 r = 0;
76 goto finish;
77 }
78
79 static_assert(SD_LISTEN_FDS_START < INT_MAX, "")_Static_assert(3 < 2147483647, "");
80 if (n < 0 || n > INT_MAX2147483647 - SD_LISTEN_FDS_START3) {
81 r = -EINVAL22;
82 goto finish;
83 }
84
85 for (fd = SD_LISTEN_FDS_START3; fd < SD_LISTEN_FDS_START3 + (int)n; fd++) {
86 int flags;
87
88 flags = fcntl(fd, F_GETFD1);
89 if (flags < 0) {
90 r = -errno(*__errno_location ());
91 goto finish;
92 }
93
94 if (flags & FD_CLOEXEC1)
95 continue;
96
97 if (fcntl(fd, F_SETFD2, flags | FD_CLOEXEC1) < 0) {
98 r = -errno(*__errno_location ());
99 goto finish;
100 }
101 }
102
103 r = n;
104
105finish:
106 if (unset_environment)
107 unsetenv_listen_vars();
108
109 return r;
110}
111
112/* Both strv_extend_n() and strv_split() aren't simple copies of the
113 * same functions from systemd. These are simplified versions of those
114 * functions, used only in the sd_listen_fds_with_names() ported from
115 * newer versions of systemd.
116 */
117static int strv_extend_n(char ***p, const char *s, int n) {
118 if (!p
8.1
'p' is non-null
)
9
Taking false branch
119 return -EINVAL22;
120 if (!s
9.1
's' is non-null
)
10
Taking false branch
121 return -EINVAL22;
122
123 *p = calloc((size_t)n, sizeof(char *));
11
Memory is allocated
124 if (!p
11.1
'p' is non-null
)
12
Taking false branch
125 return -ENOMEM12;
126
127 size_t s_size = strlen(s) + 1;
128 char *copies = calloc((size_t)(n + 1), s_size);
129 if (!copies) {
13
Assuming 'copies' is null
14
Taking true branch
130 free(*p);
15
Memory is released
131 return -ENOMEM12;
132 }
133 for (int i = 0; i < n; i++) {
134 char *copy = &copies[(size_t)i * s_size];
135 *p[i] = memcpy(copy, s, s_size);
136 }
137
138 return 0;
139}
140
141static int strv_split(char ***p, const char *value, const char separator) {
142 char *copy = strdup(value);
143 if (!copy)
144 return -ENOMEM12;
145
146 int n_split = 1;
147 for (char *c = copy; *c; ) {
148 char *sep_pos = strchr(c, separator);
149 if (!sep_pos)
150 break;
151
152 n_split++;
153 c = sep_pos + 1;
154 }
155
156 *p = calloc((size_t)(n_split + 1), sizeof(char *));
157 if (!*p) {
158 free(copy);
159 return -ENOMEM12;
160 }
161
162 if (n_split == 1) {
163 *p[0] = copy;
164 return 1;
165 }
166
167 int i = 0;
168 for (char *c = copy; *c; ) {
169 char *sep_pos = strchr(c, separator);
170 if (!sep_pos)
171 break;
172
173 *sep_pos = '\0';
174 *p[i++] = c;
175 c = sep_pos + 1;
176 }
177
178 return n_split;
179}
180
181int sd_listen_fds_with_names(int unset_environment, char ***names) {
182 char **l = NULL((void*)0);
183 bool_Bool have_names;
184 int n_names = 0, n_fds;
185 const char *e;
186 int r;
187
188 if (!names)
1
Assuming 'names' is non-null
2
Taking false branch
189 return sd_listen_fds(unset_environment);
190
191 e = getenv("LISTEN_FDNAMES");
3
Assuming the environment variable does not exist
192 if (e
3.1
'e' is null
) {
4
Taking false branch
193 n_names = strv_split(&l, e, ':');
194 if (n_names < 0) {
195 if (unset_environment)
196 unsetenv_listen_vars();
197 return n_names;
198 }
199
200 have_names = true1;
201 } else {
202 have_names = false0;
203 }
204
205 n_fds = sd_listen_fds(unset_environment);
206 if (n_fds <= 0) {
5
Assuming 'n_fds' is > 0
6
Taking false branch
207 r = n_fds;
208 goto fail;
209 }
210
211 if (have_names
6.1
'have_names' is false
) {
7
Taking false branch
212 if (n_names != n_fds) {
213 r = -EINVAL22;
214 goto fail;
215 }
216 } else {
217 r = strv_extend_n(&l, "unknown", n_fds);
8
Calling 'strv_extend_n'
16
Returning; memory was released via 1st parameter
218 if (r
16.1
'r' is < 0
< 0)
17
Taking true branch
219 goto fail;
18
Control jumps to line 227
220 }
221
222 *names = l;
223
224 return n_fds;
225
226fail:
227 free(l);
19
Attempt to free released memory
228 return r;
229}
230
231static int sd_is_socket_internal(int fd, int type, int listening) {
232 struct stat st_fd;
233
234 if (fd < 0)
235 return -EBADF9;
236
237 if (type < 0)
238 return -EINVAL22;
239
240 if (fstat(fd, &st_fd) < 0)
241 return -errno(*__errno_location ());
242
243 if (!S_ISSOCK(st_fd.st_mode)((((st_fd.st_mode)) & 0170000) == (0140000)))
244 return 0;
245
246 if (type != 0) {
247 int other_type = 0;
248 socklen_t l = sizeof(other_type);
249
250 if (getsockopt(fd, SOL_SOCKET1, SO_TYPE3, &other_type, &l) < 0)
251 return -errno(*__errno_location ());
252
253 if (l != sizeof(other_type))
254 return -EINVAL22;
255
256 if (other_type != type)
257 return 0;
258 }
259
260 if (listening >= 0) {
261 int accepting = 0;
262 socklen_t l = sizeof(accepting);
263
264 if (getsockopt(fd, SOL_SOCKET1, SO_ACCEPTCONN30, &accepting, &l) < 0)
265 return -errno(*__errno_location ());
266
267 if (l != sizeof(accepting))
268 return -EINVAL22;
269
270 if (!accepting != !listening)
271 return 0;
272 }
273
274 return 1;
275}
276
277union sockaddr_union {
278 struct sockaddr sa;
279 struct sockaddr_in in4;
280 struct sockaddr_in6 in6;
281};
282
283int sd_is_socket_inet(int fd, int family, int type, int listening, uint16_t port) {
284 union sockaddr_union sockaddr = {};
285 socklen_t l = sizeof(sockaddr);
286 int r;
287
288 if (family != 0 && family != AF_INET2 && family != AF_INET610)
289 return -EINVAL22;
290
291 r = sd_is_socket_internal(fd, type, listening);
292 if (r <= 0)
293 return r;
294
295 if (getsockname(fd, &sockaddr.sa, &l) < 0)
296 return -errno(*__errno_location ());
297
298 if (l < sizeof(sa_family_t))
299 return -EINVAL22;
300
301 if (sockaddr.sa.sa_family != AF_INET2 &&
302 sockaddr.sa.sa_family != AF_INET610)
303 return 0;
304
305 if (family > 0)
306 if (sockaddr.sa.sa_family != family)
307 return 0;
308
309 if (port > 0) {
310 if (sockaddr.sa.sa_family == AF_INET2) {
311 if (l < sizeof(struct sockaddr_in))
312 return -EINVAL22;
313
314 return htons(port) == sockaddr.in4.sin_port;
315 } else {
316 if (l < sizeof(struct sockaddr_in6))
317 return -EINVAL22;
318
319 return htons(port) == sockaddr.in6.sin6_port;
320 }
321 }
322
323 return 1;
324}