Bug Summary

File:lwan.c
Warning:line 333, column 17
Potential memory leak

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-pc-linux-gnu -analyze -disable-free -disable-llvm-verifier -discard-value-names -main-file-name lwan.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 -mframe-pointer=all -fmath-errno -fno-rounding-math -mconstructor-aliases -fno-plt -munwind-tables -target-cpu x86-64 -fno-split-dwarf-inlining -debugger-tuning=gdb -resource-dir /usr/lib/clang/11.0.1 -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.0 -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/local/include -internal-isystem /usr/lib/clang/11.0.1/include -internal-externc-isystem /include -internal-externc-isystem /usr/include -Wno-unused-parameter -Wno-free-nonheap-object -std=gnu99 -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 -o /home/buildbot/lwan-worker/clang-analyze/CLANG/2021-02-06-063540-3980280-1 -x c /home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan.c
1/*
2 * lwan - simple web server
3 * Copyright (c) 2012, 2013 Leandro A. F. Pereira <leandro@hardinfo.org>
4 *
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License
7 * as published by the Free Software Foundation; either version 2
8 * of the License, or any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
18 * USA.
19 */
20
21#define _GNU_SOURCE
22#include <assert.h>
23#include <ctype.h>
24#include <dlfcn.h>
25#include <errno(*__errno_location ()).h>
26#include <fcntl.h>
27#include <libproc.h>
28#include <limits.h>
29#include <signal.h>
30#include <stdlib.h>
31#include <string.h>
32#include <sys/resource.h>
33#include <sys/socket.h>
34#include <sys/types.h>
35#include <unistd.h>
36
37#include "lwan-private.h"
38
39#include "lwan-config.h"
40#include "lwan-http-authorize.h"
41
42#if defined(HAVE_LUA)
43#include "lwan-lua.h"
44#endif
45
46/* Ideally, this would check if all items in enum lwan_request_flags,
47 * when bitwise-or'd together, would not have have any bit set that
48 * is also set in REQUEST_METHOD_MASK. */
49static_assert(REQUEST_ACCEPT_DEFLATE > REQUEST_METHOD_MASK,extern int (*__Static_assert_function (void)) [!!sizeof (struct
{ int __error_if_negative: (REQUEST_ACCEPT_DEFLATE > REQUEST_METHOD_MASK
) ? 2 : -1; })]
50 "enough bits to store request methods")extern int (*__Static_assert_function (void)) [!!sizeof (struct
{ int __error_if_negative: (REQUEST_ACCEPT_DEFLATE > REQUEST_METHOD_MASK
) ? 2 : -1; })]
;
51
52/* See detect_fastest_monotonic_clock() */
53clockid_t monotonic_clock_id = CLOCK_MONOTONIC1;
54
55static const struct lwan_config default_config = {
56 .listener = "localhost:8080",
57 .keep_alive_timeout = 15,
58 .quiet = false0,
59 .reuse_port = false0,
60 .proxy_protocol = false0,
61 .allow_cors = false0,
62 .expires = 1 * ONE_WEEK(((60 * 60) * 24) * 7),
63 .n_threads = 0,
64 .max_post_data_size = 10 * DEFAULT_BUFFER_SIZE4096,
65 .allow_post_temp_file = false0,
66};
67
68LWAN_HANDLER(brew_coffee)static enum lwan_http_status lwan_handler_brew_coffee( struct
lwan_request *, struct lwan_response *, void *); static const
struct lwan_handler_info __attribute__((used, section("lwan_handler"
))) lwan_handler_info_brew_coffee = {.name = "brew_coffee", .
handler = lwan_handler_brew_coffee}; static enum lwan_http_status
lwan_handler_brew_coffee( struct lwan_request *request __attribute__
((unused)), struct lwan_response *response __attribute__((unused
)), void *data __attribute__((unused)))
69{
70 /* Placeholder handler so that __start_lwan_handler and __stop_lwan_handler
71 * symbols will get defined.
72 */
73 return HTTP_I_AM_A_TEAPOT;
74}
75
76__attribute__((no_sanitize_address))
77static void *find_handler(const char *name)
78{
79 const struct lwan_handler_info *handler;
80
81 LWAN_SECTION_FOREACH(lwan_handler, handler)for (handler = ({ extern const typeof(*handler) __start_lwan_handler
[]; __start_lwan_handler; }); handler < ({ extern const typeof
(*handler) __stop_lwan_handler[]; __stop_lwan_handler; }); (handler
)++)
{
82 if (streq(handler->name, name))
83 return handler->handler;
84 }
85
86 return NULL((void*)0);
87}
88
89__attribute__((no_sanitize_address))
90static const struct lwan_module *find_module(const char *name)
91{
92 const struct lwan_module_info *module;
93
94 LWAN_SECTION_FOREACH(lwan_module, module)for (module = ({ extern const typeof(*module) __start_lwan_module
[]; __start_lwan_module; }); module < ({ extern const typeof
(*module) __stop_lwan_module[]; __stop_lwan_module; }); (module
)++)
{
95 if (streq(module->name, name))
96 return module->module;
97 }
98
99 return NULL((void*)0);
100}
101
102static void destroy_urlmap(void *data)
103{
104 struct lwan_url_map *url_map = data;
105
106 if (url_map->module) {
107 const struct lwan_module *module = url_map->module;
108
109 if (module->destroy)
110 module->destroy(url_map->data);
111 } else if (url_map->data && url_map->flags & HANDLER_DATA_IS_HASH_TABLE) {
112 hash_free(url_map->data);
113 }
114
115 free(url_map->authorization.realm);
116 free(url_map->authorization.password_file);
117 free((char *)url_map->prefix);
118 free(url_map);
119}
120
121static struct lwan_url_map *add_url_map(struct lwan_trie *t, const char *prefix,
122 const struct lwan_url_map *map)
123{
124 struct lwan_url_map *copy = malloc(sizeof(*copy));
125
126 if (!copy)
127 lwan_status_critical_perror("Could not copy URL map")lwan_status_critical_perror_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan.c"
, 127, __FUNCTION__, "Could not copy URL map")
;
128
129 memcpy(copy, map, sizeof(*copy));
130
131 copy->prefix = strdup(prefix ? prefix : copy->prefix);
132 if (!copy->prefix)
133 lwan_status_critical_perror("Could not copy URL prefix")lwan_status_critical_perror_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan.c"
, 133, __FUNCTION__, "Could not copy URL prefix")
;
134
135 copy->prefix_len = strlen(copy->prefix);
136 lwan_trie_add(t, copy->prefix, copy);
137
138 return copy;
139}
140
141static bool_Bool can_override_header(const char *name)
142{
143 /* NOTE: Update lwan_prepare_response_header_full() in lwan-response.c
144 * if new headers are added here. */
145
146 if (!strcasecmp(name, "Date"))
147 return false0;
148 if (!strcasecmp(name, "Expires"))
149 return false0;
150 if (!strcasecmp(name, "WWW-Authenticate"))
151 return false0;
152 if (!strcasecmp(name, "Connection"))
153 return false0;
154 if (!strcasecmp(name, "Content-Type"))
155 return false0;
156 if (!strcasecmp(name, "Transfer-Encoding"))
157 return false0;
158 if (!strncasecmp(name, "Access-Control-Allow-",
159 sizeof("Access-Control-Allow-") - 1))
160 return false0;
161
162 return true1;
163}
164
165static void build_response_headers(struct lwan *l,
166 const struct lwan_key_value *kv)
167{
168 bool_Bool set_server = false0;
169
170 assert(l)((void) sizeof ((l) ? 1 : 0), __extension__ ({ if (l) ; else __assert_fail
("l", "/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan.c"
, 170, __extension__ __PRETTY_FUNCTION__); }))
;
171
172 lwan_strbuf_init(&l->headers);
173
174 for (; kv && kv->key; kv++) {
175 if (!can_override_header(kv->key)) {
176 lwan_status_warning("Cannot override header '%s'", kv->key)lwan_status_warning_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan.c"
, 176, __FUNCTION__, "Cannot override header '%s'", kv->key
)
;
177 } else {
178 if (!strcasecmp(kv->key, "Server"))
179 set_server = true1;
180
181 lwan_strbuf_append_printf(&l->headers, "\r\n%s: %s", kv->key,
182 kv->value);
183 }
184 }
185
186 if (!set_server)
187 lwan_strbuf_append_strz(&l->headers, "\r\nServer: lwan");
188
189 lwan_strbuf_append_strz(&l->headers, "\r\n\r\n");
190}
191
192static void parse_global_headers(struct config *c,
193 struct lwan *lwan)
194{
195 struct lwan_key_value_array hdrs;
196 const struct config_line *l;
197 struct lwan_key_value *kv;
198
199 lwan_key_value_array_init(&hdrs);
200
201 while ((l = config_read_line(c))) {
202 switch (l->type) {
203 case CONFIG_LINE_TYPE_SECTION:
204 config_error(
205 c, "No sections are supported under the 'headers' section");
206 goto cleanup;
207
208 case CONFIG_LINE_TYPE_LINE:
209 kv = lwan_key_value_array_append(&hdrs);
210 if (!kv) {
211 lwan_status_critical_perror(lwan_status_critical_perror_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan.c"
, 212, __FUNCTION__, "Could not allocate memory for custom response header"
)
212 "Could not allocate memory for custom response header")lwan_status_critical_perror_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan.c"
, 212, __FUNCTION__, "Could not allocate memory for custom response header"
)
;
213 }
214
215 kv->key = strdup(l->key);
216 if (!kv->key) {
217 lwan_status_critical_perror(lwan_status_critical_perror_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan.c"
, 218, __FUNCTION__, "Could not allocate memory for custom response header"
)
218 "Could not allocate memory for custom response header")lwan_status_critical_perror_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan.c"
, 218, __FUNCTION__, "Could not allocate memory for custom response header"
)
;
219 }
220
221 kv->value = strdup(l->value);
222 if (!kv->value) {
223 lwan_status_critical_perror(lwan_status_critical_perror_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan.c"
, 224, __FUNCTION__, "Could not allocate memory for custom response header"
)
224 "Could not allocate memory for custom response header")lwan_status_critical_perror_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan.c"
, 224, __FUNCTION__, "Could not allocate memory for custom response header"
)
;
225 }
226 break;
227
228 case CONFIG_LINE_TYPE_SECTION_END:
229 kv = lwan_key_value_array_append(&hdrs);
230 if (!kv) {
231 lwan_status_critical_perror(lwan_status_critical_perror_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan.c"
, 232, __FUNCTION__, "Could not allocate memory for custom response header"
)
232 "Could not allocate memory for custom response header")lwan_status_critical_perror_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan.c"
, 232, __FUNCTION__, "Could not allocate memory for custom response header"
)
;
233 }
234
235 kv->key = NULL((void*)0);
236 kv->value = NULL((void*)0);
237
238 build_response_headers(lwan, lwan_key_value_array_get_array(&hdrs));
239 goto cleanup;
240 }
241 }
242
243 config_error(c, "EOF while looking for end of 'headers' section");
244
245cleanup:
246 LWAN_ARRAY_FOREACH (&hdrs, kv)for (kv = (&hdrs)->base.base; kv < ((typeof(kv))(&
hdrs)->base.base + (&hdrs)->base.elements); kv++)
{
247 free(kv->key);
248 free(kv->value);
249 }
250 lwan_key_value_array_reset(&hdrs);
251}
252
253static void parse_listener_prefix_authorization(struct config *c,
254 const struct config_line *l,
255 struct lwan_url_map *url_map)
256{
257 if (!streq(l->value, "basic")) {
258 config_error(c, "Only basic authorization supported");
259 return;
260 }
261
262 memset(&url_map->authorization, 0, sizeof(url_map->authorization));
263
264 while ((l = config_read_line(c))) {
265 switch (l->type) {
266 case CONFIG_LINE_TYPE_LINE:
267 if (streq(l->key, "realm")) {
268 free(url_map->authorization.realm);
269 url_map->authorization.realm = strdup(l->value);
270 } else if (streq(l->key, "password_file")) {
271 free(url_map->authorization.password_file);
272 url_map->authorization.password_file = realpath(l->value, NULL((void*)0));
273 if (!url_map->authorization.password_file)
274 config_error(c, "Could not determine full path for password file: %s", l->value);
275 }
276 break;
277
278 case CONFIG_LINE_TYPE_SECTION:
279 config_error(c, "Unexpected section: %s", l->key);
280 goto error;
281
282 case CONFIG_LINE_TYPE_SECTION_END:
283 if (!url_map->authorization.realm)
284 url_map->authorization.realm = strdup("Lwan");
285 if (!url_map->authorization.password_file)
286 url_map->authorization.password_file = strdup("htpasswd");
287
288 url_map->flags |= HANDLER_MUST_AUTHORIZE;
289 return;
290 }
291 }
292
293 config_error(c, "Could not find end of authorization section");
294
295error:
296 free(url_map->authorization.realm);
297 free(url_map->authorization.password_file);
298}
299
300__attribute__((no_sanitize_address))
301static const char *get_module_name(const struct lwan_module *module)
302{
303 const struct lwan_module_info *iter;
304
305 LWAN_SECTION_FOREACH(lwan_module, iter)for (iter = ({ extern const typeof(*iter) __start_lwan_module
[]; __start_lwan_module; }); iter < ({ extern const typeof
(*iter) __stop_lwan_module[]; __stop_lwan_module; }); (iter)++
)
{
306 if (iter->module == module)
307 return iter->name;
308 }
309
310 return "<unknown>";
311}
312
313static void parse_listener_prefix(struct config *c,
314 const struct config_line *l,
315 struct lwan *lwan,
316 const struct lwan_module *module,
317 void *handler)
318{
319 struct lwan_url_map url_map = {};
320 struct hash *hash = hash_str_new(free, free);
321 char *prefix = strdupa(l->value)(__extension__ ({ const char *__old = (l->value); size_t __len
= strlen (__old) + 1; char *__new = (char *) __builtin_alloca
(__len); (char *) memcpy (__new, __old, __len); }))
;
322 struct config *isolated;
323
324 if (!hash)
21
Assuming 'hash' is non-null
22
Taking false branch
325 lwan_status_critical("Could not allocate hash table")lwan_status_critical_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan.c"
, 325, __FUNCTION__, "Could not allocate hash table")
;
326
327 isolated = config_isolate_section(c, l);
328 if (!isolated) {
23
Assuming 'isolated' is non-null
24
Taking false branch
329 config_error(c, "Could not isolate configuration file");
330 goto out;
331 }
332
333 while ((l = config_read_line(c))) {
25
Loop condition is true. Entering loop body
29
Potential memory leak
334 switch (l->type) {
26
Control jumps to 'case CONFIG_LINE_TYPE_LINE:' at line 335
335 case CONFIG_LINE_TYPE_LINE:
336 hash_add(hash, strdup(l->key), strdup(l->value));
27
Memory is allocated
337 break;
28
Execution continues on line 333
338
339 case CONFIG_LINE_TYPE_SECTION:
340 if (streq(l->key, "authorization")) {
341 parse_listener_prefix_authorization(c, l, &url_map);
342 } else if (!config_skip_section(c, l)) {
343 config_error(c, "Could not skip section");
344 goto out;
345 }
346 break;
347
348 case CONFIG_LINE_TYPE_SECTION_END:
349 goto add_map;
350 }
351 }
352
353 config_error(c, "Expecting section end while parsing prefix");
354 goto out;
355
356add_map:
357 assert((handler && !module) || (!handler && module))((void) sizeof (((handler && !module) || (!handler &&
module)) ? 1 : 0), __extension__ ({ if ((handler && !
module) || (!handler && module)) ; else __assert_fail
("(handler && !module) || (!handler && module)"
, "/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan.c"
, 357, __extension__ __PRETTY_FUNCTION__); }))
;
358
359 if (handler) {
360 url_map.handler = handler;
361 url_map.flags |= HANDLER_PARSE_MASK | HANDLER_DATA_IS_HASH_TABLE;
362 url_map.data = hash;
363 url_map.module = NULL((void*)0);
364
365 hash = NULL((void*)0);
366 } else if (module->create_from_hash && module->handle_request) {
367 lwan_status_debug("Initializing module %s from config",lwan_status_debug_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan.c"
, 368, __FUNCTION__, "Initializing module %s from config", get_module_name
(module))
368 get_module_name(module))lwan_status_debug_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan.c"
, 368, __FUNCTION__, "Initializing module %s from config", get_module_name
(module))
;
369
370 url_map.data = module->create_from_hash(prefix, hash);
371 if (!url_map.data) {
372 config_error(c, "Could not create module instance");
373 goto out;
374 }
375
376 if (module->parse_conf && !module->parse_conf(url_map.data, isolated)) {
377 const char *msg = config_last_error(isolated);
378
379 config_error(c, "Error from module: %s", msg ? msg : "Unknown");
380 goto out;
381 }
382
383 url_map.handler = module->handle_request;
384 url_map.flags |= module->flags;
385 url_map.module = module;
386 } else if (UNLIKELY(!module->create_from_hash)__builtin_expect(((!module->create_from_hash)), (0))) {
387 config_error(c, "Module isn't prepared to load settings from a file; "
388 "create_from_hash() method isn't present");
389 goto out;
390 } else if (UNLIKELY(!module->handle_request)__builtin_expect(((!module->handle_request)), (0))) {
391 config_error(c, "Module does not have handle_request() method");
392 goto out;
393 }
394
395 add_url_map(&lwan->url_map_trie, prefix, &url_map);
396
397out:
398 hash_free(hash);
399 config_close(isolated);
400}
401
402void lwan_set_url_map(struct lwan *l, const struct lwan_url_map *map)
403{
404 lwan_trie_destroy(&l->url_map_trie);
405 if (UNLIKELY(!lwan_trie_init(&l->url_map_trie, destroy_urlmap))__builtin_expect(((!lwan_trie_init(&l->url_map_trie, destroy_urlmap
))), (0))
)
406 lwan_status_critical_perror("Could not initialize trie")lwan_status_critical_perror_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan.c"
, 406, __FUNCTION__, "Could not initialize trie")
;
407
408 for (; map->prefix; map++) {
409 struct lwan_url_map *copy = add_url_map(&l->url_map_trie, NULL((void*)0), map);
410
411 if (copy->module && copy->module->create) {
412 lwan_status_debug("Initializing module %s from struct",lwan_status_debug_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan.c"
, 413, __FUNCTION__, "Initializing module %s from struct", get_module_name
(copy->module))
413 get_module_name(copy->module))lwan_status_debug_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan.c"
, 413, __FUNCTION__, "Initializing module %s from struct", get_module_name
(copy->module))
;
414
415 copy->data = copy->module->create(map->prefix, copy->args);
416 if (!copy->data) {
417 lwan_status_critical("Could not initialize module %s",lwan_status_critical_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan.c"
, 418, __FUNCTION__, "Could not initialize module %s", get_module_name
(copy->module))
418 get_module_name(copy->module))lwan_status_critical_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan.c"
, 418, __FUNCTION__, "Could not initialize module %s", get_module_name
(copy->module))
;
419 }
420
421 copy->flags = copy->module->flags;
422 copy->handler = copy->module->handle_request;
423 } else {
424 copy->flags = HANDLER_PARSE_MASK;
425 }
426 }
427}
428
429static void parse_listener(struct config *c,
430 const struct config_line *l,
431 struct lwan *lwan)
432{
433 free(lwan->config.listener);
434 lwan->config.listener = strdup(l->value);
435
436 while ((l = config_read_line(c))) {
14
Loop condition is true. Entering loop body
437 switch (l->type) {
15
Control jumps to 'case CONFIG_LINE_TYPE_SECTION:' at line 441
438 case CONFIG_LINE_TYPE_LINE:
439 config_error(c, "Expecting prefix section");
440 return;
441 case CONFIG_LINE_TYPE_SECTION:
442 if (l->key[0] == '&') {
16
Assuming the condition is false
17
Taking false branch
443 void *handler = find_handler(l->key + 1);
444 if (handler) {
445 parse_listener_prefix(c, l, lwan, NULL((void*)0), handler);
446 continue;
447 }
448
449 config_error(c, "Could not find handler name: %s", l->key + 1);
450 return;
451 }
452
453 const struct lwan_module *module = find_module(l->key);
454 if (module) {
18
Assuming 'module' is non-null
19
Taking true branch
455 parse_listener_prefix(c, l, lwan, module, NULL((void*)0));
20
Calling 'parse_listener_prefix'
456 continue;
457 }
458
459 config_error(c, "Invalid section or module not found: %s", l->key);
460 return;
461 case CONFIG_LINE_TYPE_SECTION_END:
462 return;
463 }
464 }
465
466 config_error(c, "Expecting section end while parsing listener");
467}
468
469const char *lwan_get_config_path(char *path_buf, size_t path_buf_len)
470{
471 char buffer[PATH_MAX4096];
472
473 if (proc_pidpath(getpid(), buffer, sizeof(buffer)) < 0)
474 goto out;
475
476 char *path = strrchr(buffer, '/');
477 if (!path)
478 goto out;
479 int ret = snprintf(path_buf, path_buf_len, "%s.conf", path + 1);
480 if (ret < 0 || ret >= (int)path_buf_len)
481 goto out;
482
483 return path_buf;
484
485out:
486 return "lwan.conf";
487}
488
489static bool_Bool setup_from_config(struct lwan *lwan, const char *path)
490{
491 const struct config_line *line;
492 struct config *conf;
493 bool_Bool has_listener = false0;
494 char path_buf[PATH_MAX4096];
495
496 if (!path
3.1
'path' is null
)
4
Taking true branch
497 path = lwan_get_config_path(path_buf, sizeof(path_buf));
498 lwan_status_info("Loading configuration file: %s", path)lwan_status_info_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan.c"
, 498, __FUNCTION__, "Loading configuration file: %s", path)
;
499
500 conf = config_open(path);
501 if (!conf)
5
Assuming 'conf' is non-null
6
Taking false branch
502 return false0;
503
504 if (!lwan_trie_init(&lwan->url_map_trie, destroy_urlmap))
7
Assuming the condition is false
8
Taking false branch
505 return false0;
506
507 while ((line = config_read_line(conf))) {
9
Loop condition is true. Entering loop body
508 switch (line->type) {
10
Control jumps to 'case CONFIG_LINE_TYPE_SECTION:' at line 554
509 case CONFIG_LINE_TYPE_LINE:
510 if (streq(line->key, "keep_alive_timeout")) {
511 lwan->config.keep_alive_timeout = (unsigned int)parse_long(
512 line->value, default_config.keep_alive_timeout);
513 } else if (streq(line->key, "quiet")) {
514 lwan->config.quiet =
515 parse_bool(line->value, default_config.quiet);
516 } else if (streq(line->key, "reuse_port")) {
517 lwan->config.reuse_port =
518 parse_bool(line->value, default_config.reuse_port);
519 } else if (streq(line->key, "proxy_protocol")) {
520 lwan->config.proxy_protocol =
521 parse_bool(line->value, default_config.proxy_protocol);
522 } else if (streq(line->key, "allow_cors")) {
523 lwan->config.allow_cors =
524 parse_bool(line->value, default_config.allow_cors);
525 } else if (streq(line->key, "expires")) {
526 lwan->config.expires =
527 parse_time_period(line->value, default_config.expires);
528 } else if (streq(line->key, "error_template")) {
529 free(lwan->config.error_template);
530 lwan->config.error_template = strdup(line->value);
531 } else if (streq(line->key, "threads")) {
532 long n_threads =
533 parse_long(line->value, default_config.n_threads);
534 if (n_threads < 0)
535 config_error(conf, "Invalid number of threads: %ld",
536 n_threads);
537 lwan->config.n_threads = (unsigned int)n_threads;
538 } else if (streq(line->key, "max_post_data_size")) {
539 long max_post_data_size = parse_long(
540 line->value, (long)default_config.max_post_data_size);
541 if (max_post_data_size < 0)
542 config_error(conf, "Negative maximum post data size");
543 else if (max_post_data_size > 128 * (1 << 20))
544 config_error(conf,
545 "Maximum post data can't be over 128MiB");
546 lwan->config.max_post_data_size = (size_t)max_post_data_size;
547 } else if (streq(line->key, "allow_temp_files")) {
548 lwan->config.allow_post_temp_file =
549 !!strstr(line->value, "post");
550 } else {
551 config_error(conf, "Unknown config key: %s", line->key);
552 }
553 break;
554 case CONFIG_LINE_TYPE_SECTION:
555 if (streq(line->key, "listener")) {
11
Taking true branch
556 if (!has_listener
11.1
'has_listener' is false
) {
12
Taking true branch
557 parse_listener(conf, line, lwan);
13
Calling 'parse_listener'
558 has_listener = true1;
559 } else {
560 config_error(conf, "Only one listener supported");
561 }
562 } else if (streq(line->key, "straitjacket")) {
563 lwan_straitjacket_enforce_from_config(conf);
564 } else if (streq(line->key, "headers")) {
565 parse_global_headers(conf, lwan);
566 } else {
567 config_error(conf, "Unknown section type: %s", line->key);
568 }
569 break;
570 case CONFIG_LINE_TYPE_SECTION_END:
571 config_error(conf, "Unexpected section end");
572 }
573 }
574
575 if (config_last_error(conf)) {
576 lwan_status_critical("Error on config file \"%s\", line %d: %s", path,lwan_status_critical_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan.c"
, 577, __FUNCTION__, "Error on config file \"%s\", line %d: %s"
, path, config_cur_line(conf), config_last_error(conf))
577 config_cur_line(conf), config_last_error(conf))lwan_status_critical_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan.c"
, 577, __FUNCTION__, "Error on config file \"%s\", line %d: %s"
, path, config_cur_line(conf), config_last_error(conf))
;
578 }
579
580 config_close(conf);
581
582 return true1;
583}
584
585static void try_setup_from_config(struct lwan *l,
586 const struct lwan_config *config)
587{
588 if (!setup_from_config(l, config->config_file_path)) {
3
Calling 'setup_from_config'
589 if (config->config_file_path) {
590 lwan_status_critical("Could not read config file: %s",lwan_status_critical_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan.c"
, 591, __FUNCTION__, "Could not read config file: %s", config
->config_file_path)
591 config->config_file_path)lwan_status_critical_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan.c"
, 591, __FUNCTION__, "Could not read config file: %s", config
->config_file_path)
;
592 }
593 }
594
595 lwan_status_init(l); /* `quiet` key might have changed value. */
596
597 l->config.request_flags =
598 (l->config.proxy_protocol ? REQUEST_ALLOW_PROXY_REQS : 0) |
599 (l->config.allow_cors ? REQUEST_ALLOW_CORS : 0);
600}
601
602static rlim_t setup_open_file_count_limits(void)
603{
604 struct rlimit r;
605
606 if (getrlimit(RLIMIT_NOFILERLIMIT_NOFILE, &r) < 0) {
607 lwan_status_perror("Could not obtain maximum number of file "lwan_status_perror_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan.c"
, 609, __FUNCTION__, "Could not obtain maximum number of file "
"descriptors. Assuming %d", 256)
608 "descriptors. Assuming %d",lwan_status_perror_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan.c"
, 609, __FUNCTION__, "Could not obtain maximum number of file "
"descriptors. Assuming %d", 256)
609 OPEN_MAX)lwan_status_perror_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan.c"
, 609, __FUNCTION__, "Could not obtain maximum number of file "
"descriptors. Assuming %d", 256)
;
610 return OPEN_MAX256;
611 }
612
613 if (r.rlim_max != r.rlim_cur) {
614 const rlim_t current = r.rlim_cur;
615
616 if (r.rlim_max == RLIM_INFINITY0xffffffffffffffffuLL && r.rlim_cur < OPEN_MAX256) {
617 r.rlim_cur = OPEN_MAX256;
618 } else if (r.rlim_cur < r.rlim_max) {
619 r.rlim_cur = r.rlim_max;
620 } else {
621 /* Shouldn't happen, so just return the current value. */
622 goto out;
623 }
624
625 if (setrlimit(RLIMIT_NOFILERLIMIT_NOFILE, &r) < 0) {
626 lwan_status_perror("Could not raise maximum number of file "lwan_status_perror_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan.c"
, 628, __FUNCTION__, "Could not raise maximum number of file "
"descriptors to %" "l" "u" ". Leaving at " "%" "l" "u", r.rlim_max
, current)
627 "descriptors to %" PRIu64 ". Leaving at "lwan_status_perror_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan.c"
, 628, __FUNCTION__, "Could not raise maximum number of file "
"descriptors to %" "l" "u" ". Leaving at " "%" "l" "u", r.rlim_max
, current)
628 "%" PRIu64, r.rlim_max, current)lwan_status_perror_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan.c"
, 628, __FUNCTION__, "Could not raise maximum number of file "
"descriptors to %" "l" "u" ". Leaving at " "%" "l" "u", r.rlim_max
, current)
;
629 r.rlim_cur = current;
630 }
631 }
632
633out:
634 return r.rlim_cur;
635}
636
637static void allocate_connections(struct lwan *l, size_t max_open_files)
638{
639 const size_t sz = max_open_files * sizeof(struct lwan_connection);
640
641 l->conns = lwan_aligned_alloc(sz, 64);
642 if (UNLIKELY(!l->conns)__builtin_expect(((!l->conns)), (0)))
643 lwan_status_critical_perror("lwan_alloc_aligned")lwan_status_critical_perror_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan.c"
, 643, __FUNCTION__, "lwan_alloc_aligned")
;
644
645 memset(l->conns, 0, sz);
646}
647
648static void get_number_of_cpus(struct lwan *l)
649{
650 long n_online_cpus = sysconf(_SC_NPROCESSORS_ONLN_SC_NPROCESSORS_ONLN);
651 long n_available_cpus = sysconf(_SC_NPROCESSORS_CONF_SC_NPROCESSORS_CONF);
652
653 if (n_online_cpus < 0) {
654 lwan_status_warning(lwan_status_warning_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan.c"
, 655, __FUNCTION__, "Could not get number of online CPUs, assuming 1 CPU"
)
655 "Could not get number of online CPUs, assuming 1 CPU")lwan_status_warning_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan.c"
, 655, __FUNCTION__, "Could not get number of online CPUs, assuming 1 CPU"
)
;
656 n_online_cpus = 1;
657 }
658
659 if (n_available_cpus < 0) {
660 lwan_status_warning(lwan_status_warning_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan.c"
, 662, __FUNCTION__, "Could not get number of available CPUs, assuming %ld CPUs"
, n_online_cpus)
661 "Could not get number of available CPUs, assuming %ld CPUs",lwan_status_warning_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan.c"
, 662, __FUNCTION__, "Could not get number of available CPUs, assuming %ld CPUs"
, n_online_cpus)
662 n_online_cpus)lwan_status_warning_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan.c"
, 662, __FUNCTION__, "Could not get number of available CPUs, assuming %ld CPUs"
, n_online_cpus)
;
663 n_available_cpus = n_online_cpus;
664 }
665
666 l->online_cpus = (unsigned int)n_online_cpus;
667 l->available_cpus = (unsigned int)n_available_cpus;
668}
669
670void lwan_init(struct lwan *l) { lwan_init_with_config(l, &default_config); }
1
Calling 'lwan_init_with_config'
671
672const struct lwan_config *lwan_get_default_config(void)
673{
674 return &default_config;
675}
676
677static char *dup_or_null(const char *s)
678{
679 return s ? strdup(s) : NULL((void*)0);
680}
681
682void lwan_init_with_config(struct lwan *l, const struct lwan_config *config)
683{
684 /* Load defaults */
685 memset(l, 0, sizeof(*l));
686 memcpy(&l->config, config, sizeof(*config));
687 l->config.listener = dup_or_null(l->config.listener);
688 l->config.config_file_path = dup_or_null(l->config.config_file_path);
689
690 /* Initialize status first, as it is used by other things during
691 * their initialization. */
692 lwan_status_init(l);
693
694 /* These will only print debugging messages. Debug messages are always
695 * printed if we're on a debug build, so the quiet setting will be
696 * respected. */
697 lwan_job_thread_init();
698 lwan_tables_init();
699
700 try_setup_from_config(l, config);
2
Calling 'try_setup_from_config'
701
702 if (!lwan_strbuf_get_length(&l->headers))
703 build_response_headers(l, config->global_headers);
704
705 lwan_response_init(l);
706
707 /* Continue initialization as normal. */
708 lwan_status_debug("Initializing lwan web server")lwan_status_debug_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan.c"
, 708, __FUNCTION__, "Initializing lwan web server")
;
709
710 get_number_of_cpus(l);
711 if (!l->config.n_threads) {
712 l->thread.count = l->online_cpus;
713 if (l->thread.count == 1)
714 l->thread.count = 2;
715 } else if (l->config.n_threads > 3 * l->online_cpus) {
716 l->thread.count = l->online_cpus * 3;
717
718 lwan_status_warning("%d threads requested, but only %d online CPUs "lwan_status_warning_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan.c"
, 721, __FUNCTION__, "%d threads requested, but only %d online CPUs "
"(out of %d configured CPUs); capping to %d threads", l->
config.n_threads, l->online_cpus, l->available_cpus, 3 *
l->online_cpus)
719 "(out of %d configured CPUs); capping to %d threads",lwan_status_warning_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan.c"
, 721, __FUNCTION__, "%d threads requested, but only %d online CPUs "
"(out of %d configured CPUs); capping to %d threads", l->
config.n_threads, l->online_cpus, l->available_cpus, 3 *
l->online_cpus)
720 l->config.n_threads, l->online_cpus, l->available_cpus,lwan_status_warning_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan.c"
, 721, __FUNCTION__, "%d threads requested, but only %d online CPUs "
"(out of %d configured CPUs); capping to %d threads", l->
config.n_threads, l->online_cpus, l->available_cpus, 3 *
l->online_cpus)
721 3 * l->online_cpus)lwan_status_warning_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan.c"
, 721, __FUNCTION__, "%d threads requested, but only %d online CPUs "
"(out of %d configured CPUs); capping to %d threads", l->
config.n_threads, l->online_cpus, l->available_cpus, 3 *
l->online_cpus)
;
722 } else if (l->config.n_threads > 255) {
723 l->thread.count = 256;
724
725 lwan_status_warning("%d threads requested, but max 256 supported",lwan_status_warning_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan.c"
, 726, __FUNCTION__, "%d threads requested, but max 256 supported"
, l->config.n_threads)
726 l->config.n_threads)lwan_status_warning_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan.c"
, 726, __FUNCTION__, "%d threads requested, but max 256 supported"
, l->config.n_threads)
;
727 } else {
728 l->thread.count = l->config.n_threads;
729 }
730
731 rlim_t max_open_files = setup_open_file_count_limits();
732 allocate_connections(l, (size_t)max_open_files);
733
734 l->thread.max_fd = (unsigned)max_open_files / (unsigned)l->thread.count;
735 lwan_status_info("Using %d threads, maximum %d sockets per thread",lwan_status_info_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan.c"
, 736, __FUNCTION__, "Using %d threads, maximum %d sockets per thread"
, l->thread.count, l->thread.max_fd)
736 l->thread.count, l->thread.max_fd)lwan_status_info_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan.c"
, 736, __FUNCTION__, "Using %d threads, maximum %d sockets per thread"
, l->thread.count, l->thread.max_fd)
;
737
738 signal(SIGPIPE13, SIG_IGN((__sighandler_t) 1));
739
740 lwan_readahead_init();
741 lwan_thread_init(l);
742 lwan_socket_init(l);
743 lwan_http_authorize_init();
744}
745
746void lwan_shutdown(struct lwan *l)
747{
748 lwan_status_info("Shutting down")lwan_status_info_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan.c"
, 748, __FUNCTION__, "Shutting down")
;
749
750 free(l->config.listener);
751 free(l->config.error_template);
752 free(l->config.config_file_path);
753
754 lwan_job_thread_shutdown();
755 lwan_thread_shutdown(l);
756
757 lwan_status_debug("Shutting down URL handlers")lwan_status_debug_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan.c"
, 757, __FUNCTION__, "Shutting down URL handlers")
;
758 lwan_trie_destroy(&l->url_map_trie);
759
760 lwan_strbuf_free(&l->headers);
761 free(l->conns);
762
763 lwan_response_shutdown(l);
764 lwan_tables_shutdown();
765 lwan_status_shutdown(l);
766 lwan_http_authorize_shutdown();
767 lwan_readahead_shutdown();
768}
769
770static ALWAYS_INLINEinline __attribute__((always_inline)) int schedule_client(struct lwan *l, int fd)
771{
772 struct lwan_thread *thread = l->conns[fd].thread;
773
774 lwan_thread_add_client(thread, fd);
775
776 return (int)(thread - l->thread.threads);
777}
778
779static volatile sig_atomic_t main_socket = -1;
780
781static_assert(sizeof(main_socket) >= sizeof(int),extern int (*__Static_assert_function (void)) [!!sizeof (struct
{ int __error_if_negative: (sizeof(main_socket) >= sizeof
(int)) ? 2 : -1; })]
782 "size of sig_atomic_t > size of int")extern int (*__Static_assert_function (void)) [!!sizeof (struct
{ int __error_if_negative: (sizeof(main_socket) >= sizeof
(int)) ? 2 : -1; })]
;
783
784static void sigint_handler(int signal_number __attribute__((unused)))
785{
786 if (main_socket < 0)
787 return;
788
789 shutdown((int)main_socket, SHUT_RDWRSHUT_RDWR);
790 close((int)main_socket);
791
792 main_socket = -1;
793}
794
795enum herd_accept { HERD_MORE = 0, HERD_GONE = -1, HERD_SHUTDOWN = 1 };
796
797struct core_bitmap {
798 uint64_t bitmap[4];
799};
800
801static ALWAYS_INLINEinline __attribute__((always_inline)) enum herd_accept
802accept_one(struct lwan *l, struct core_bitmap *cores)
803{
804 int fd = accept4((int)main_socket, NULL((void*)0), NULL((void*)0), SOCK_NONBLOCKSOCK_NONBLOCK | SOCK_CLOEXECSOCK_CLOEXEC);
805
806 if (LIKELY(fd >= 0)__builtin_expect((!!(fd >= 0)), (1))) {
807 int core = schedule_client(l, fd);
808
809 cores->bitmap[core / 64] |= UINT64_C(1)1UL<<(core % 64);
810
811 return HERD_MORE;
812 }
813
814 switch (errno(*__errno_location ())) {
815 case EAGAIN11:
816 return HERD_GONE;
817
818 case EBADF9:
819 case ECONNABORTED103:
820 case EINVAL22:
821 if (main_socket < 0) {
822 lwan_status_info("Signal 2 (Interrupt) received")lwan_status_info_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan.c"
, 822, __FUNCTION__, "Signal 2 (Interrupt) received")
;
823 } else {
824 lwan_status_info("Main socket closed for unknown reasons")lwan_status_info_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan.c"
, 824, __FUNCTION__, "Main socket closed for unknown reasons"
)
;
825 }
826 return HERD_SHUTDOWN;
827
828 default:
829 lwan_status_perror("accept")lwan_status_perror_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan.c"
, 829, __FUNCTION__, "accept")
;
830 return HERD_MORE;
831 }
832}
833
834void lwan_main_loop(struct lwan *l)
835{
836 struct core_bitmap cores = {};
837
838 assert(main_socket == -1)((void) sizeof ((main_socket == -1) ? 1 : 0), __extension__ (
{ if (main_socket == -1) ; else __assert_fail ("main_socket == -1"
, "/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan.c"
, 838, __extension__ __PRETTY_FUNCTION__); }))
;
839 main_socket = l->main_socket;
840
841 if (signal(SIGINT2, sigint_handler) == SIG_ERR((__sighandler_t) -1))
842 lwan_status_critical("Could not set signal handler")lwan_status_critical_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan.c"
, 842, __FUNCTION__, "Could not set signal handler")
;
843
844 lwan_status_info("Ready to serve")lwan_status_info_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan.c"
, 844, __FUNCTION__, "Ready to serve")
;
845
846 while (true1) {
847 enum herd_accept ha;
848
849 fcntl(l->main_socket, F_SETFL4, 0);
850 ha = accept_one(l, &cores);
851 if (ha == HERD_MORE) {
852 fcntl(l->main_socket, F_SETFL4, O_NONBLOCK04000);
853
854 do {
855 ha = accept_one(l, &cores);
856 } while (ha == HERD_MORE);
857 }
858
859 if (UNLIKELY(ha > HERD_MORE)__builtin_expect(((ha > HERD_MORE)), (0)))
860 break;
861
862 for (size_t i = 0; i < N_ELEMENTS(cores.bitmap)((!sizeof(char[1 - 2 * __builtin_types_compatible_p( __typeof__
(cores.bitmap), __typeof__(&(cores.bitmap)[0]))])) | sizeof
(cores.bitmap) / sizeof(cores.bitmap[0]))
; i++) {
863 for (uint64_t c = cores.bitmap[i]; c; c ^= c & -c) {
864 size_t core = (size_t)__builtin_ctzl(c);
865 lwan_thread_nudge(&l->thread.threads[i * 64 + core]);
866 }
867 }
868 cores = (struct core_bitmap){};
869 }
870}
871
872#ifdef CLOCK_MONOTONIC_COARSE6
873__attribute__((constructor)) static void detect_fastest_monotonic_clock(void)
874{
875 struct timespec ts;
876
877 if (!clock_gettime(CLOCK_MONOTONIC_COARSE6, &ts))
878 monotonic_clock_id = CLOCK_MONOTONIC_COARSE6;
879}
880#endif
881
882void lwan_set_thread_name(const char *name)
883{
884 char thread_name[16];
885 char process_name[PATH_MAX4096];
886 char *tmp;
887 int ret;
888
889 if (proc_pidpath(getpid(), process_name, sizeof(process_name)) < 0)
890 return;
891
892 tmp = strrchr(process_name, '/');
893 if (!tmp)
894 return;
895
896 ret = snprintf(thread_name, sizeof(thread_name), "%s %s", tmp + 1, name);
897 if (ret < 0)
898 return;
899
900 pthread_set_name_np(pthread_self(), thread_name);
901}