Bug Summary

File:lib/sd-daemon.c
Warning:line 178, column 16
Potential leak of memory pointed to by 'c'

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)
119 return -EINVAL22;
120 if (!s)
121 return -EINVAL22;
122
123 *p = calloc((size_t)n, sizeof(char *));
124 if (!p)
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) {
130 free(*p);
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);
6
Memory is allocated
143 if (!copy)
7
Assuming 'copy' is non-null
8
Taking false branch
144 return -ENOMEM12;
145
146 int n_split = 1;
147 for (char *c = copy; *c; ) {
9
Loop condition is true. Entering loop body
12
Loop condition is false. Execution continues on line 156
148 char *sep_pos = strchr(c, separator);
149 if (!sep_pos)
10
Assuming 'sep_pos' is non-null
11
Taking false branch
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) {
13
Assuming the condition is false
14
Taking false branch
158 free(copy);
159 return -ENOMEM12;
160 }
161
162 if (n_split
14.1
'n_split' is not equal to 1
== 1) {
15
Taking false branch
163 *p[0] = copy;
164 return 1;
165 }
166
167 int i = 0;
168 for (char *c = copy; *c; ) {
16
Loop condition is true. Entering loop body
169 char *sep_pos = strchr(c, separator);
170 if (!sep_pos)
17
Assuming 'sep_pos' is null
18
Taking true branch
171 break;
172
173 *sep_pos = '\0';
174 *p[i++] = c;
175 c = sep_pos + 1;
176 }
177
178 return n_split;
19
Execution continues on line 178
20
Potential leak of memory pointed to by 'c'
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 exists
192 if (e
3.1
'e' is non-null
) {
4
Taking true branch
193 n_names = strv_split(&l, e, ':');
5
Calling 'strv_split'
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) {
207 r = n_fds;
208 goto fail;
209 }
210
211 if (have_names) {
212 if (n_names != n_fds) {
213 r = -EINVAL22;
214 goto fail;
215 }
216 } else {
217 r = strv_extend_n(&l, "unknown", n_fds);
218 if (r < 0)
219 goto fail;
220 }
221
222 *names = l;
223
224 return n_fds;
225
226fail:
227 free(l);
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}