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/15.0.7 -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/15.0.7/include -internal-isystem /usr/local/include -internal-isystem /usr/bin/../lib64/gcc/x86_64-pc-linux-gnu/12.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-01-27-043450-1480122-1 -x c /home/buildbot/lwan-worker/clang-analyze/build/src/lib/sd-daemon.c
1 | |
2 | |
3 | |
4 | |
5 | |
6 | |
7 | |
8 | |
9 | |
10 | |
11 | |
12 | |
13 | |
14 | |
15 | |
16 | |
17 | |
18 | |
19 | |
20 | |
21 | |
22 | |
23 | #define _GNU_SOURCE |
24 | #include <assert.h> |
25 | #include <errno.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 | |
39 | static void unsetenv_listen_vars(void) { |
40 | unsetenv("LISTEN_PID"); |
41 | unsetenv("LISTEN_FDS"); |
42 | unsetenv("LISTEN_FDNAMES"); |
43 | } |
44 | |
45 | int 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 = -EINVAL; |
58 | goto finish; |
59 | } |
60 | |
61 | |
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, ""); |
80 | if (n < 0 || n > INT_MAX - SD_LISTEN_FDS_START) { |
81 | r = -EINVAL; |
82 | goto finish; |
83 | } |
84 | |
85 | for (fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + (int)n; fd++) { |
86 | int flags; |
87 | |
88 | flags = fcntl(fd, F_GETFD); |
89 | if (flags < 0) { |
90 | r = -errno; |
91 | goto finish; |
92 | } |
93 | |
94 | if (flags & FD_CLOEXEC) |
95 | continue; |
96 | |
97 | if (fcntl(fd, F_SETFD, flags | FD_CLOEXEC) < 0) { |
98 | r = -errno; |
99 | goto finish; |
100 | } |
101 | } |
102 | |
103 | r = n; |
104 | |
105 | finish: |
106 | if (unset_environment) |
107 | unsetenv_listen_vars(); |
108 | |
109 | return r; |
110 | } |
111 | |
112 | |
113 | |
114 | |
115 | |
116 | |
117 | static int strv_extend_n(char ***p, const char *s, int n) { |
118 | if (!p) |
| |
119 | return -EINVAL; |
120 | if (!s) |
| |
121 | return -EINVAL; |
122 | |
123 | *p = calloc((size_t)n, sizeof(char *)); |
| |
124 | if (!p) |
| |
125 | return -ENOMEM; |
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 | |
|
| |
130 | free(*p); |
| |
131 | return -ENOMEM; |
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 | |
141 | static int strv_split(char ***p, const char *value, const char separator) { |
142 | char *copy = strdup(value); |
143 | int n_split = 0; |
144 | |
145 | if (!copy) |
146 | return -ENOMEM; |
147 | |
148 | for (char *c = copy; *c; ) { |
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) |
158 | return 0; |
159 | |
160 | *p = calloc((size_t)(n_split + 1), sizeof(char *)); |
161 | if (!*p) { |
162 | free(copy); |
163 | return -ENOMEM; |
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 | |
180 | int sd_listen_fds_with_names(int unset_environment, char ***names) { |
181 | char **l = NULL; |
182 | 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 | |
|
| |
188 | return sd_listen_fds(unset_environment); |
189 | |
190 | e = getenv("LISTEN_FDNAMES"); |
| 3 | | Assuming the environment variable does not exist | |
|
191 | if (e) { |
| |
192 | n_names = strv_split(&l, e, ':'); |
193 | if (n_names < 0) { |
194 | if (unset_environment) |
195 | unsetenv_listen_vars(); |
196 | return n_names; |
197 | } |
198 | |
199 | have_names = true; |
200 | } else { |
201 | have_names = false; |
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 = -EINVAL; |
213 | goto fail; |
214 | } |
215 | } else { |
216 | r = strv_extend_n(&l, "unknown", n_fds); |
| |
| 16 | | Returning; memory was released via 1st parameter | |
|
217 | if (r < 0) |
| |
218 | goto fail; |
| 18 | | Control jumps to line 226 | |
|
219 | } |
220 | |
221 | *names = l; |
222 | |
223 | return n_fds; |
224 | |
225 | fail: |
226 | free(l); |
| 19 | | Attempt to free released memory |
|
227 | return r; |
228 | } |
229 | |
230 | static int sd_is_socket_internal(int fd, int type, int listening) { |
231 | struct stat st_fd; |
232 | |
233 | if (fd < 0) |
234 | return -EBADF; |
235 | |
236 | if (type < 0) |
237 | return -EINVAL; |
238 | |
239 | if (fstat(fd, &st_fd) < 0) |
240 | return -errno; |
241 | |
242 | if (!S_ISSOCK(st_fd.st_mode)) |
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_SOCKET, SO_TYPE, &other_type, &l) < 0) |
250 | return -errno; |
251 | |
252 | if (l != sizeof(other_type)) |
253 | return -EINVAL; |
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_SOCKET, SO_ACCEPTCONN, &accepting, &l) < 0) |
264 | return -errno; |
265 | |
266 | if (l != sizeof(accepting)) |
267 | return -EINVAL; |
268 | |
269 | if (!accepting != !listening) |
270 | return 0; |
271 | } |
272 | |
273 | return 1; |
274 | } |
275 | |
276 | union sockaddr_union { |
277 | struct sockaddr sa; |
278 | struct sockaddr_in in4; |
279 | struct sockaddr_in6 in6; |
280 | }; |
281 | |
282 | int 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_INET && family != AF_INET6) |
288 | return -EINVAL; |
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; |
296 | |
297 | if (l < sizeof(sa_family_t)) |
298 | return -EINVAL; |
299 | |
300 | if (sockaddr.sa.sa_family != AF_INET && |
301 | sockaddr.sa.sa_family != AF_INET6) |
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_INET) { |
310 | if (l < sizeof(struct sockaddr_in)) |
311 | return -EINVAL; |
312 | |
313 | return htons(port) == sockaddr.in4.sin_port; |
314 | } else { |
315 | if (l < sizeof(struct sockaddr_in6)) |
316 | return -EINVAL; |
317 | |
318 | return htons(port) == sockaddr.in6.sin6_port; |
319 | } |
320 | } |
321 | |
322 | return 1; |
323 | } |