Bug Summary

File:lwan-mod-rewrite.c
Warning:line 775, column 13
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-mod-rewrite.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 -fno-rounding-math -mconstructor-aliases -fno-plt -munwind-tables -target-cpu x86-64 -tune-cpu generic -fno-split-dwarf-inlining -debugger-tuning=gdb -resource-dir /usr/lib/clang/12.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/12.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-09-28-035851-801140-1 -x c /home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-mod-rewrite.c
1/*
2 * lwan - simple web server
3 * Copyright (c) 2015 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 <ctype.h>
23#include <limits.h>
24#include <stdlib.h>
25#include <string.h>
26#include <sys/stat.h>
27
28#include "lwan-private.h"
29
30#include "patterns.h"
31#include "lwan-array.h"
32#include "lwan-mod-rewrite.h"
33#include "lwan-strbuf.h"
34
35#ifdef HAVE_LUA
36#include <lauxlib.h>
37#include <lua.h>
38#include <lualib.h>
39
40#include "lwan-lua.h"
41#endif
42
43enum pattern_flag {
44 PATTERN_HANDLE_REWRITE = 1 << 0,
45 PATTERN_HANDLE_REDIRECT = 1 << 1,
46 PATTERN_HANDLE_MASK = PATTERN_HANDLE_REWRITE | PATTERN_HANDLE_REDIRECT,
47
48 PATTERN_EXPAND_LWAN = 1 << 2,
49 PATTERN_EXPAND_LUA = 1 << 3,
50 PATTERN_EXPAND_MASK = PATTERN_EXPAND_LWAN | PATTERN_EXPAND_LUA,
51
52 PATTERN_COND_COOKIE = 1 << 4,
53 PATTERN_COND_ENV_VAR = 1 << 5,
54 PATTERN_COND_STAT = 1 << 6,
55 PATTERN_COND_QUERY_VAR = 1 << 7,
56 PATTERN_COND_POST_VAR = 1 << 8,
57 PATTERN_COND_HEADER = 1 << 9,
58 PATTERN_COND_LUA = 1 << 10,
59 PATTERN_COND_METHOD = 1 << 11,
60 PATTERN_COND_ACCEPT_ENCODING = 1 << 12,
61 PATTERN_COND_MASK = PATTERN_COND_COOKIE | PATTERN_COND_ENV_VAR |
62 PATTERN_COND_STAT | PATTERN_COND_QUERY_VAR |
63 PATTERN_COND_POST_VAR | PATTERN_COND_HEADER |
64 PATTERN_COND_LUA | PATTERN_COND_METHOD |
65 PATTERN_COND_ACCEPT_ENCODING,
66
67 PATTERN_COND_STAT__HAS_IS_FILE = 1 << 13,
68 PATTERN_COND_STAT__HAS_IS_DIR = 1 << 14,
69 PATTERN_COND_STAT__IS_FILE = 1 << 15,
70 PATTERN_COND_STAT__IS_DIR = 1 << 16,
71
72 PATTERN_COND_STAT__FILE_CHECK =
73 PATTERN_COND_STAT__HAS_IS_FILE | PATTERN_COND_STAT__IS_FILE,
74 PATTERN_COND_STAT__DIR_CHECK =
75 PATTERN_COND_STAT__HAS_IS_DIR | PATTERN_COND_STAT__IS_DIR,
76};
77
78struct pattern {
79 char *pattern;
80 char *expand_pattern;
81 struct {
82 struct lwan_key_value cookie;
83 struct lwan_key_value env_var;
84 struct lwan_key_value query_var;
85 struct lwan_key_value post_var;
86 struct lwan_key_value header;
87 struct {
88 char *path;
89 } stat;
90 struct {
91 char *script;
92 } lua;
93 enum lwan_request_flags request_flags;
94 /* FIXME: Use pahole to find alignment holes? */
95 } condition;
96 enum pattern_flag flags;
97};
98
99DEFINE_ARRAY_TYPE(pattern_array, struct pattern)struct pattern_array { struct lwan_array base; }; __attribute__
((unused)) static inline struct pattern *pattern_array_append
( struct pattern_array *array) { return (struct pattern *)lwan_array_append_heap
(&array->base, sizeof(struct pattern)); } __attribute__
((unused)) static inline struct pattern_array *coro_pattern_array_new
(struct coro *coro) { return (struct pattern_array *)coro_lwan_array_new
(coro, 0); } __attribute__((unused)) static inline struct pattern
*pattern_array_get_array(struct pattern_array *array) { return
(struct pattern *)array->base.base; } __attribute__((unused
)) __attribute__((nonnull(1))) static inline void pattern_array_init
( struct pattern_array *array) { array->base = (struct lwan_array
){.base = ((void*)0), .elements = 0}; } __attribute__((unused
)) static inline int pattern_array_reset( struct pattern_array
*array) { return lwan_array_reset(&array->base, ((void
*)0)); } __attribute__((unused)) static inline struct pattern
*pattern_array_append0(struct pattern_array *array) { struct
pattern *element = pattern_array_append(array); if (element)
memset(element, 0, sizeof(*element)); return element; } __attribute__
((unused)) static inline void pattern_array_sort( struct pattern_array
*array, int (*cmp)(const void *a, const void *b)) { lwan_array_sort
(&array->base, sizeof(struct pattern), cmp); } __attribute__
((unused)) static inline size_t pattern_array_get_elem_index(
const struct pattern_array *array, struct pattern *elem) { (
(void) sizeof ((elem >= (struct pattern *)array->base.base
) ? 1 : 0), __extension__ ({ if (elem >= (struct pattern *
)array->base.base) ; else __assert_fail ("elem >= (struct pattern *)array->base.base"
, "/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-mod-rewrite.c"
, 99, __extension__ __PRETTY_FUNCTION__); })); ((void) sizeof
((elem < (struct pattern *)array->base.base + array->
base.elements) ? 1 : 0), __extension__ ({ if (elem < (struct
pattern *)array->base.base + array->base.elements) ; else
__assert_fail ("elem < (struct pattern *)array->base.base + array->base.elements"
, "/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-mod-rewrite.c"
, 99, __extension__ __PRETTY_FUNCTION__); })); return (size_t
)(elem - (struct pattern *)array->base.base); } __attribute__
((unused)) static inline struct pattern *pattern_array_get_elem
(const struct pattern_array *array, size_t index) { ((void) sizeof
((index <= array->base.elements) ? 1 : 0), __extension__
({ if (index <= array->base.elements) ; else __assert_fail
("index <= array->base.elements", "/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-mod-rewrite.c"
, 99, __extension__ __PRETTY_FUNCTION__); })); return &((
struct pattern *)array->base.base)[index]; } __attribute__
((unused)) static inline size_t pattern_array_len( const struct
pattern_array *array) { return array->base.elements; }
100
101struct private_data {
102 struct pattern_array patterns;
103};
104
105static enum lwan_http_status module_redirect_to(struct lwan_request *request,
106 const char *url)
107{
108 const struct lwan_key_value headers[] = {
109 {"Location", coro_strdup(request->conn->coro, url)},
110 {},
111 };
112
113 request->response.headers =
114 coro_memdup(request->conn->coro, headers, sizeof(headers));
115
116 if (LIKELY(headers[0].value && request->response.headers)__builtin_expect((!!(headers[0].value && request->
response.headers)), (1))
)
117 return HTTP_MOVED_PERMANENTLY;
118
119 return HTTP_INTERNAL_ERROR;
120}
121
122static enum lwan_http_status module_rewrite_as(struct lwan_request *request,
123 const char *url)
124{
125 request->url.value = coro_strdup(request->conn->coro, url);
126
127 if (UNLIKELY(!request->url.value)__builtin_expect(((!request->url.value)), (0)))
128 return HTTP_INTERNAL_ERROR;
129
130 request->url.len = strlen(request->url.value);
131 request->original_url = request->url;
132 request->flags |= RESPONSE_URL_REWRITTEN;
133
134 return HTTP_OK;
135}
136
137#define MAX_INT_DIGITS(3 * sizeof(int)) (3 * sizeof(int))
138
139static __attribute__((noinline)) int parse_int_len(const char *s, size_t len,
140 int default_value)
141{
142 if (UNLIKELY(len > MAX_INT_DIGITS)__builtin_expect(((len > (3 * sizeof(int)))), (0)))
143 return default_value;
144
145 return parse_int(strndupa(s, len)(__extension__ ({ const char *__old = (s); size_t __len = strnlen
(__old, (len)); char *__new = (char *) __builtin_alloca (__len
+ 1); __new[__len] = '\0'; (char *) memcpy (__new, __old, __len
); }))
, default_value);
146}
147
148static const char *expand_string(const char *expand_pattern,
149 const char *orig,
150 char buffer[static PATH_MAX4096],
151 const struct str_find *sf,
152 int captures)
153{
154 struct lwan_strbuf strbuf;
155 const char *ptr;
156
157 ptr = strchr(expand_pattern, '%');
158 if (!ptr)
159 return expand_pattern;
160
161 if (!lwan_strbuf_init_with_fixed_buffer(&strbuf, buffer, PATH_MAX4096))
162 return NULL((void*)0);
163
164 do {
165 size_t index_len = strspn(ptr + 1, "0123456789");
166
167 if (ptr > expand_pattern) {
168 const size_t len = (size_t)(ptr - expand_pattern);
169
170 if (UNLIKELY(!lwan_strbuf_append_str(&strbuf, expand_pattern, len))__builtin_expect(((!lwan_strbuf_append_str(&strbuf, expand_pattern
, len))), (0))
)
171 return NULL((void*)0);
172
173 expand_pattern += len;
174 }
175
176 if (LIKELY(index_len > 0)__builtin_expect((!!(index_len > 0)), (1))) {
177 const int index = parse_int_len(ptr + 1, index_len, -1);
178
179 if (UNLIKELY(index < 0 || index > captures)__builtin_expect(((index < 0 || index > captures)), (0)
)
)
180 return NULL((void*)0);
181
182 if (UNLIKELY(!lwan_strbuf_append_str(__builtin_expect(((!lwan_strbuf_append_str( &strbuf, orig
+ sf[index].sm_so, (size_t)(sf[index].sm_eo - sf[index].sm_so
)))), (0))
183 &strbuf, orig + sf[index].sm_so,__builtin_expect(((!lwan_strbuf_append_str( &strbuf, orig
+ sf[index].sm_so, (size_t)(sf[index].sm_eo - sf[index].sm_so
)))), (0))
184 (size_t)(sf[index].sm_eo - sf[index].sm_so)))__builtin_expect(((!lwan_strbuf_append_str( &strbuf, orig
+ sf[index].sm_so, (size_t)(sf[index].sm_eo - sf[index].sm_so
)))), (0))
)
185 return NULL((void*)0);
186
187 expand_pattern += index_len;
188 } else if (UNLIKELY(!lwan_strbuf_append_char(&strbuf, '%'))__builtin_expect(((!lwan_strbuf_append_char(&strbuf, '%')
)), (0))
) {
189 return NULL((void*)0);
190 }
191
192 expand_pattern++;
193 } while ((ptr = strchr(expand_pattern, '%')));
194
195 const size_t remaining_len = strlen(expand_pattern);
196 if (remaining_len &&
197 !lwan_strbuf_append_str(&strbuf, expand_pattern, remaining_len))
198 return NULL((void*)0);
199
200 if (UNLIKELY(!lwan_strbuf_get_length(&strbuf))__builtin_expect(((!lwan_strbuf_get_length(&strbuf))), (0
))
)
201 return NULL((void*)0);
202
203 return lwan_strbuf_get_buffer(&strbuf);
204}
205
206static ALWAYS_INLINEinline __attribute__((always_inline)) const char *expand(const struct pattern *pattern,
207 const char *orig,
208 char buffer[static PATH_MAX4096],
209 const struct str_find *sf,
210 int captures)
211{
212 return expand_string(pattern->expand_pattern, orig, buffer, sf, captures);
213}
214
215#ifdef HAVE_LUA
216static void
217lua_close_defer(void *data)
218{
219 lua_close((lua_State *)data);
220}
221
222static const char *expand_lua(struct lwan_request *request,
223 struct pattern *pattern, const char *orig,
224 char buffer[static PATH_MAX4096],
225 const struct str_find *sf, int captures)
226{
227 const char *output;
228 size_t output_len;
229 int i;
230 lua_State *L;
231
232 L = lwan_lua_create_state(NULL((void*)0), pattern->expand_pattern);
233 if (UNLIKELY(!L)__builtin_expect(((!L)), (0)))
234 return NULL((void*)0);
235 coro_defer(request->conn->coro, lua_close_defer, L);
236
237 lua_getglobal(L, "handle_rewrite")lua_getfield(L, (-10002), ("handle_rewrite"));
238 if (!lua_isfunction(L, -1)(lua_type(L, (-1)) == 6)) {
239 lwan_status_error(lwan_status_error_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-mod-rewrite.c"
, 241, __FUNCTION__, "Could not obtain reference to `handle_rewrite()` function: %s"
, lwan_lua_state_last_error(L))
240 "Could not obtain reference to `handle_rewrite()` function: %s",lwan_status_error_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-mod-rewrite.c"
, 241, __FUNCTION__, "Could not obtain reference to `handle_rewrite()` function: %s"
, lwan_lua_state_last_error(L))
241 lwan_lua_state_last_error(L))lwan_status_error_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-mod-rewrite.c"
, 241, __FUNCTION__, "Could not obtain reference to `handle_rewrite()` function: %s"
, lwan_lua_state_last_error(L))
;
242 return NULL((void*)0);
243 }
244
245 lwan_lua_state_push_request(L, request);
246
247 lua_createtable(L, captures, 0);
248 for (i = 0; i < captures; i++) {
249 lua_pushinteger(L, i);
250 lua_pushlstring(L, orig + sf[i].sm_so,
251 (size_t)(sf[i].sm_eo - sf[i].sm_so));
252 lua_settable(L, -3);
253 }
254
255 if (lua_pcall(L, 2, 1, 0) != 0) {
256 lwan_status_error("Could not execute `handle_rewrite()` function: %s",lwan_status_error_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-mod-rewrite.c"
, 257, __FUNCTION__, "Could not execute `handle_rewrite()` function: %s"
, lwan_lua_state_last_error(L))
257 lwan_lua_state_last_error(L))lwan_status_error_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-mod-rewrite.c"
, 257, __FUNCTION__, "Could not execute `handle_rewrite()` function: %s"
, lwan_lua_state_last_error(L))
;
258 return NULL((void*)0);
259 }
260
261 output = lua_tolstring(L, -1, &output_len);
262 if (output_len >= PATH_MAX4096) {
263 lwan_status_error("Rewritten URL exceeds %d bytes (got %zu bytes)",lwan_status_error_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-mod-rewrite.c"
, 264, __FUNCTION__, "Rewritten URL exceeds %d bytes (got %zu bytes)"
, 4096, output_len)
264 PATH_MAX, output_len)lwan_status_error_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-mod-rewrite.c"
, 264, __FUNCTION__, "Rewritten URL exceeds %d bytes (got %zu bytes)"
, 4096, output_len)
;
265 return NULL((void*)0);
266 }
267
268 return memcpy(buffer, output, output_len + 1);
269}
270#endif
271
272static bool_Bool condition_matches(struct lwan_request *request,
273 const struct pattern *p,
274 const struct str_find *sf,
275 int captures,
276 char expanded_buf[static PATH_MAX4096])
277{
278 if (LIKELY(!(p->flags & PATTERN_COND_MASK))__builtin_expect((!!(!(p->flags & PATTERN_COND_MASK)))
, (1))
)
279 return true1;
280
281 const char *url = request->url.value;
282
283 if (p->flags & PATTERN_COND_METHOD) {
284 const enum lwan_request_flags method =
285 p->condition.request_flags & REQUEST_METHOD_MASK;
286 if (lwan_request_get_method(request) != method)
287 return false0;
288 }
289
290 if (p->flags & PATTERN_COND_ACCEPT_ENCODING) {
291 const enum lwan_request_flags accept =
292 p->condition.request_flags & REQUEST_ACCEPT_MASK;
293 if (!(lwan_request_get_accept_encoding(request) & accept))
294 return false0;
295 }
296
297 if (p->flags & PATTERN_COND_COOKIE) {
298 assert(p->condition.cookie.key)((void) sizeof ((p->condition.cookie.key) ? 1 : 0), __extension__
({ if (p->condition.cookie.key) ; else __assert_fail ("p->condition.cookie.key"
, "/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-mod-rewrite.c"
, 298, __extension__ __PRETTY_FUNCTION__); }))
;
299 assert(p->condition.cookie.value)((void) sizeof ((p->condition.cookie.value) ? 1 : 0), __extension__
({ if (p->condition.cookie.value) ; else __assert_fail ("p->condition.cookie.value"
, "/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-mod-rewrite.c"
, 299, __extension__ __PRETTY_FUNCTION__); }))
;
300
301 const char *cookie =
302 lwan_request_get_cookie(request, p->condition.cookie.key);
303 if (!cookie)
304 return false0;
305
306 const char *val = expand_string(p->condition.cookie.value, url,
307 expanded_buf, sf, captures);
308 if (!val || !streq(val, cookie))
309 return false0;
310 }
311
312 if (p->flags & PATTERN_COND_ENV_VAR) {
313 assert(p->condition.env_var.key)((void) sizeof ((p->condition.env_var.key) ? 1 : 0), __extension__
({ if (p->condition.env_var.key) ; else __assert_fail ("p->condition.env_var.key"
, "/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-mod-rewrite.c"
, 313, __extension__ __PRETTY_FUNCTION__); }))
;
314 assert(p->condition.env_var.value)((void) sizeof ((p->condition.env_var.value) ? 1 : 0), __extension__
({ if (p->condition.env_var.value) ; else __assert_fail (
"p->condition.env_var.value", "/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-mod-rewrite.c"
, 314, __extension__ __PRETTY_FUNCTION__); }))
;
315
316 const char *env_var = secure_getenv(p->condition.env_var.key);
317 if (!env_var)
318 return false0;
319
320 const char *val = expand_string(p->condition.env_var.value, url,
321 expanded_buf, sf, captures);
322 if (!val || !streq(val, env_var))
323 return false0;
324 }
325
326 if (p->flags & PATTERN_COND_QUERY_VAR) {
327 assert(p->condition.query_var.key)((void) sizeof ((p->condition.query_var.key) ? 1 : 0), __extension__
({ if (p->condition.query_var.key) ; else __assert_fail (
"p->condition.query_var.key", "/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-mod-rewrite.c"
, 327, __extension__ __PRETTY_FUNCTION__); }))
;
328 assert(p->condition.query_var.value)((void) sizeof ((p->condition.query_var.value) ? 1 : 0), __extension__
({ if (p->condition.query_var.value) ; else __assert_fail
("p->condition.query_var.value", "/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-mod-rewrite.c"
, 328, __extension__ __PRETTY_FUNCTION__); }))
;
329
330 const char *query =
331 lwan_request_get_query_param(request, p->condition.query_var.key);
332
333 if (!query)
334 return false0;
335
336 const char *val = expand_string(p->condition.query_var.value, url,
337 expanded_buf, sf, captures);
338 if (!val || !streq(val, query))
339 return false0;
340 }
341
342 if (p->flags & PATTERN_COND_POST_VAR) {
343 assert(p->condition.post_var.key)((void) sizeof ((p->condition.post_var.key) ? 1 : 0), __extension__
({ if (p->condition.post_var.key) ; else __assert_fail ("p->condition.post_var.key"
, "/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-mod-rewrite.c"
, 343, __extension__ __PRETTY_FUNCTION__); }))
;
344 assert(p->condition.post_var.value)((void) sizeof ((p->condition.post_var.value) ? 1 : 0), __extension__
({ if (p->condition.post_var.value) ; else __assert_fail (
"p->condition.post_var.value", "/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-mod-rewrite.c"
, 344, __extension__ __PRETTY_FUNCTION__); }))
;
345
346 const char *post =
347 lwan_request_get_post_param(request, p->condition.post_var.key);
348 if (!post)
349 return false0;
350
351 const char *val = expand_string(p->condition.post_var.value, url,
352 expanded_buf, sf, captures);
353 if (!val || !streq(val, post))
354 return false0;
355 }
356
357 if (p->flags & PATTERN_COND_STAT) {
358 assert(p->condition.stat.path)((void) sizeof ((p->condition.stat.path) ? 1 : 0), __extension__
({ if (p->condition.stat.path) ; else __assert_fail ("p->condition.stat.path"
, "/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-mod-rewrite.c"
, 358, __extension__ __PRETTY_FUNCTION__); }))
;
359
360 struct stat st;
361
362 /* FIXME: Expanding path from a user-controlled URL and use the
363 * resulting path to call stat(2) on could lead to some information
364 * disclosure vulnerability. Would require the server to be configured
365 * in a certain way, though.
366 */
367 const char *path = expand_string(p->condition.stat.path, url,
368 expanded_buf, sf, captures);
369 if (!path || stat(path, &st) < 0)
370 return false0;
371
372 if ((p->flags & PATTERN_COND_STAT__FILE_CHECK) ==
373 PATTERN_COND_STAT__FILE_CHECK &&
374 !S_ISREG(st.st_mode)((((st.st_mode)) & 0170000) == (0100000)))
375 return false0;
376 if ((p->flags & PATTERN_COND_STAT__DIR_CHECK) ==
377 PATTERN_COND_STAT__DIR_CHECK &&
378 !S_ISDIR(st.st_mode)((((st.st_mode)) & 0170000) == (0040000)))
379 return false0;
380 }
381
382#ifdef HAVE_LUA
383 if (p->flags & PATTERN_COND_LUA) {
384 assert(p->condition.lua.script)((void) sizeof ((p->condition.lua.script) ? 1 : 0), __extension__
({ if (p->condition.lua.script) ; else __assert_fail ("p->condition.lua.script"
, "/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-mod-rewrite.c"
, 384, __extension__ __PRETTY_FUNCTION__); }))
;
385
386 lua_State *L = lwan_lua_create_state(NULL((void*)0), p->condition.lua.script);
387 if (!L)
388 return false0;
389 coro_defer(request->conn->coro, lua_close_defer, L);
390
391 lua_getglobal(L, "matches")lua_getfield(L, (-10002), ("matches"));
392 if (!lua_isfunction(L, -1)(lua_type(L, (-1)) == 6)) {
393 lwan_status_error(lwan_status_error_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-mod-rewrite.c"
, 395, __FUNCTION__, "Could not obtain reference to `matches()` function: %s"
, lwan_lua_state_last_error(L))
394 "Could not obtain reference to `matches()` function: %s",lwan_status_error_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-mod-rewrite.c"
, 395, __FUNCTION__, "Could not obtain reference to `matches()` function: %s"
, lwan_lua_state_last_error(L))
395 lwan_lua_state_last_error(L))lwan_status_error_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-mod-rewrite.c"
, 395, __FUNCTION__, "Could not obtain reference to `matches()` function: %s"
, lwan_lua_state_last_error(L))
;
396 return false0;
397 }
398
399 lwan_lua_state_push_request(L, request);
400
401 if (lua_pcall(L, 1, 1, 0) != 0) {
402 lwan_status_error("Could not execute `matches()` function: %s",lwan_status_error_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-mod-rewrite.c"
, 403, __FUNCTION__, "Could not execute `matches()` function: %s"
, lwan_lua_state_last_error(L))
403 lwan_lua_state_last_error(L))lwan_status_error_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-mod-rewrite.c"
, 403, __FUNCTION__, "Could not execute `matches()` function: %s"
, lwan_lua_state_last_error(L))
;
404 return false0;
405 }
406
407 if (!lua_toboolean(L, -1))
408 return false0;
409 }
410#else
411 assert(!(p->flags & PATTERN_COND_LUA))((void) sizeof ((!(p->flags & PATTERN_COND_LUA)) ? 1 :
0), __extension__ ({ if (!(p->flags & PATTERN_COND_LUA
)) ; else __assert_fail ("!(p->flags & PATTERN_COND_LUA)"
, "/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-mod-rewrite.c"
, 411, __extension__ __PRETTY_FUNCTION__); }))
;
412#endif
413
414 return true1;
415}
416
417static enum lwan_http_status
418rewrite_handle_request(struct lwan_request *request,
419 struct lwan_response *response __attribute__((unused)),
420 void *instance)
421{
422 struct private_data *pd = instance;
423 const char *url = request->url.value;
424 char final_url[PATH_MAX4096];
425 struct pattern *p;
426
427 LWAN_ARRAY_FOREACH(&pd->patterns, p)for (p = (&pd->patterns)->base.base; p < ((typeof
(p))(&pd->patterns)->base.base + (&pd->patterns
)->base.elements); p++)
{
428 struct str_find sf[MAXCAPTURES32];
429 const char *expanded = NULL((void*)0);
430 const char *errmsg;
431 int captures;
432
433 captures = str_find(url, p->pattern, sf, MAXCAPTURES32, &errmsg);
434 if (captures <= 0)
435 continue;
436
437 if (!condition_matches(request, p, sf, captures, final_url))
438 continue;
439
440 switch (p->flags & PATTERN_EXPAND_MASK) {
441#ifdef HAVE_LUA
442 case PATTERN_EXPAND_LUA:
443 expanded = expand_lua(request, p, url, final_url, sf, captures);
444 break;
445#endif
446 case PATTERN_EXPAND_LWAN:
447 expanded = expand(p, url, final_url, sf, captures);
448 break;
449 }
450
451 if (LIKELY(expanded)__builtin_expect((!!(expanded)), (1))) {
452 switch (p->flags & PATTERN_HANDLE_MASK) {
453 case PATTERN_HANDLE_REDIRECT:
454 return module_redirect_to(request, expanded);
455 case PATTERN_HANDLE_REWRITE:
456 return module_rewrite_as(request, expanded);
457 }
458 }
459
460 return HTTP_INTERNAL_ERROR;
461 }
462
463 return HTTP_NOT_FOUND;
464}
465
466static void *rewrite_create(const char *prefix __attribute__((unused)),
467 void *instance __attribute__((unused)))
468{
469 struct private_data *pd = malloc(sizeof(*pd));
470
471 if (!pd)
472 return NULL((void*)0);
473
474 pattern_array_init(&pd->patterns);
475
476 return pd;
477}
478
479static void rewrite_destroy(void *instance)
480{
481 struct private_data *pd = instance;
482 struct pattern *iter;
483
484 LWAN_ARRAY_FOREACH(&pd->patterns, iter)for (iter = (&pd->patterns)->base.base; iter < (
(typeof(iter))(&pd->patterns)->base.base + (&pd
->patterns)->base.elements); iter++)
{
485 free(iter->pattern);
486 free(iter->expand_pattern);
487 if (iter->flags & PATTERN_COND_COOKIE) {
488 free(iter->condition.cookie.key);
489 free(iter->condition.cookie.value);
490 }
491 if (iter->flags & PATTERN_COND_ENV_VAR) {
492 free(iter->condition.env_var.key);
493 free(iter->condition.env_var.value);
494 }
495 if (iter->flags & PATTERN_COND_QUERY_VAR) {
496 free(iter->condition.query_var.key);
497 free(iter->condition.query_var.value);
498 }
499 if (iter->flags & PATTERN_COND_POST_VAR) {
500 free(iter->condition.post_var.key);
501 free(iter->condition.post_var.value);
502 }
503 if (iter->flags & PATTERN_COND_HEADER) {
504 free(iter->condition.header.key);
505 free(iter->condition.header.value);
506 }
507 if (iter->flags & PATTERN_COND_STAT) {
508 free(iter->condition.stat.path);
509 }
510#ifdef HAVE_LUA
511 if (iter->flags & PATTERN_COND_LUA) {
512 free(iter->condition.lua.script);
513 }
514#endif
515 }
516
517 pattern_array_reset(&pd->patterns);
518 free(pd);
519}
520
521static void *rewrite_create_from_hash(const char *prefix,
522 const struct hash *hash
523 __attribute__((unused)))
524{
525 return rewrite_create(prefix, NULL((void*)0));
526}
527
528static void parse_condition_key_value(struct pattern *pattern,
529 struct lwan_key_value *key_value,
530 enum pattern_flag condition_type,
531 struct config *config,
532 const struct config_line *line)
533{
534 char *key = NULL((void*)0), *value = NULL((void*)0);
535
536 while ((line = config_read_line(config))) {
19
Loop condition is true. Entering loop body
28
Loop condition is true. Entering loop body
537 switch (line->type) {
20
Control jumps to 'case CONFIG_LINE_TYPE_LINE:' at line 552
29
Control jumps to 'case CONFIG_LINE_TYPE_SECTION_END:' at line 542
538 case CONFIG_LINE_TYPE_SECTION:
539 config_error(config, "Unexpected section: %s", line->key);
540 goto out;
541
542 case CONFIG_LINE_TYPE_SECTION_END:
543 if (!key
29.1
'key' is non-null
|| !value
29.2
'value' is non-null
) {
30
Taking false branch
544 config_error(config, "Key/value has not been specified");
545 goto out;
546 }
547
548 *key_value = (struct lwan_key_value){key, value};
549 pattern->flags |= condition_type & PATTERN_COND_MASK;
550 return;
551
552 case CONFIG_LINE_TYPE_LINE:
553 if (key
20.1
'key' is null
|| value
20.2
'value' is null
) {
21
Taking false branch
554