Bug Summary

File:lib/sd-daemon.c
Warning:line 158, column 24
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-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 -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/14.0.6 -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 /usr/include/luajit-2.1 -I /usr/include/valgrind -I /home/buildbot/lwan-worker/clang-analyze/build/src/lib -I /home/buildbot/lwan-worker/clang-analyze/build -internal-isystem /usr/lib/clang/14.0.6/include -internal-isystem /usr/local/include -internal-isystem /usr/bin/../lib64/gcc/x86_64-pc-linux-gnu/12.2.0/../../../../x86_64-pc-linux-gnu/include -internal-externc-isystem /include -internal-externc-isystem /usr/include -Wno-unused-parameter -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-01-03-043944-1058096-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);
5
Memory is allocated
143 int n_split = 0;
144
145 if (!copy)
6
Assuming 'copy' is non-null
7
Taking false branch
146 return -ENOMEM12;
147
148 for (char *c = copy; *c; ) {
8
Loop condition is false. Execution continues on line 157
149 char *sep_pos = strchr(c, separator);
150 if (!sep_pos)
151 break;
152
153 n_split++;
154 c = sep_pos + 1;
155 }
156
157 if (!n_split
8.1
'n_split' is 0
)
9
Taking true branch
158 return 0;
10
Potential leak of memory pointed to by 'c'
159
160 *p = calloc((size_t)(n_split + 1), sizeof(char *));
161 if (!*p) {
162 free(copy);
163 return -ENOMEM12;
164 }
165
166 int i = 0;
167 for (char *c = copy; *c; ) {
168 char *sep_pos = strchr(c, separator);
169 if (!sep_pos)
170 break;
171
172 *sep_pos = '\0';
173 *p[i++] = c;
174 c = sep_pos + 1;
175 }
176
177 return n_split;
178}
179
180int sd_listen_fds_with_names(int unset_environment, char ***names) {
181 char **l = NULL((void*)0);
182 bool_Bool have_names;
183 int n_names = 0, n_fds;
184 const char *e;
185 int r;
186
187 if (!names)
1
Assuming 'names' is non-null
2
Taking false branch
188 return sd_listen_fds(unset_environment);
189
190 e = getenv("LISTEN_FDNAMES");
191 if (e
2.1
'e' is non-null
) {
3
Taking true branch
192 n_names = strv_split(&l, e, ':');
4
Calling 'strv_split'
193 if (n_names < 0) {
194 if (unset_environment)
195 unsetenv_listen_vars();
196 return n_names;
197 }
198
199 have_names = true1;
200 } else {
201 have_names = false0;
202 }
203
204 n_fds = sd_listen_fds(unset_environment);
205 if (n_fds <= 0) {
206 r = n_fds;
207 goto fail;
208 }
209
210 if (have_names) {
211 if (n_names != n_fds) {
212 r = -EINVAL22;
213 goto fail;
214 }
215 } else {
216 r = strv_extend_n(&l, "unknown", n_fds);
217 if (r < 0)
218 goto fail;
219 }
220
221 *names = l;
222
223 return n_fds;
224
225fail:
226 free(l);
227 return r;
228}
229
230static int sd_is_socket_internal(int fd, int type, int listening) {
231 struct stat st_fd;
232
233 if (fd < 0)
234 return -EBADF9;
235
236 if (type < 0)
237 return -EINVAL22;
238
239 if (fstat(fd, &st_fd) < 0)
240 return -errno(*__errno_location ());
241
242 if (!S_ISSOCK(st_fd.st_mode)((((st_fd.st_mode)) & 0170000) == (0140000)))
243 return 0;
244
245 if (type != 0) {
246 int other_type = 0;
247 socklen_t l = sizeof(other_type);
248
249 if (getsockopt(fd, SOL_SOCKET1, SO_TYPE3, &other_type, &l) < 0)
250 return -errno(*__errno_location ());
251
252 if (l != sizeof(other_type))
253 return -EINVAL22;
254
255 if (other_type != type)
256 return 0;
257 }
258
259 if (listening >= 0) {
260 int accepting = 0;
261 socklen_t l = sizeof(accepting);
262
263 if (getsockopt(fd, SOL_SOCKET1, SO_ACCEPTCONN30, &accepting, &l) < 0)
264 return -errno(*__errno_location ());
265
266 if (l != sizeof(accepting))
267 return -EINVAL22;
268
269 if (!accepting != !listening)
270 return 0;
271 }
272
273 return 1;
274}
275
276union sockaddr_union {
277 struct sockaddr sa;
278 struct sockaddr_in in4;
279 struct sockaddr_in6 in6;
280};
281
282int sd_is_socket_inet(int fd, int family, int type, int listening, uint16_t port) {
283 union sockaddr_union sockaddr = {};
284 socklen_t l = sizeof(sockaddr);
285 int r;
286
287 if (family != 0 && family != AF_INET2 && family != AF_INET610)
288 return -EINVAL22;
289
290 r = sd_is_socket_internal(fd, type, listening);
291 if (r <= 0)
292 return r;
293
294 if (getsockname(fd, &sockaddr.sa, &l) < 0)
295 return -errno(*__errno_location ());
296
297 if (l < sizeof(sa_family_t))
298 return -EINVAL22;
299
300 if (sockaddr.sa.sa_family != AF_INET2 &&
301 sockaddr.sa.sa_family != AF_INET610)
302 return 0;
303
304 if (family > 0)
305 if (sockaddr.sa.sa_family != family)
306 return 0;
307
308 if (port > 0) {
309 if (sockaddr.sa.sa_family == AF_INET2) {
310 if (l < sizeof(struct sockaddr_in))
311 return -EINVAL22;
312
313 return htons(port) == sockaddr.in4.sin_port;
314 } else {
315 if (l < sizeof(struct sockaddr_in6))
316 return -EINVAL22;
317
318 return htons(port) == sockaddr.in6.sin6_port;
319 }
320 }
321
322 return 1;
323}