Bug Summary

File:lwan-mod-rewrite.c
Warning:line 600, column 5
Attempt to free released memory

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-035938-802865-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_PROXIED = 1 << 13,
62 PATTERN_COND_HTTP10 = 1 << 14,
63 PATTERN_COND_HAS_QUERY_STRING = 1 << 15,
64 PATTERN_COND_MASK = PATTERN_COND_COOKIE | PATTERN_COND_ENV_VAR |
65 PATTERN_COND_STAT | PATTERN_COND_QUERY_VAR |
66 PATTERN_COND_POST_VAR | PATTERN_COND_HEADER |
67 PATTERN_COND_LUA | PATTERN_COND_METHOD |
68 PATTERN_COND_ACCEPT_ENCODING |
69 PATTERN_COND_PROXIED | PATTERN_COND_HTTP10 |
70 PATTERN_COND_HAS_QUERY_STRING,
71
72 PATTERN_COND_STAT__HAS_IS_FILE = 1 << 16,
73 PATTERN_COND_STAT__HAS_IS_DIR = 1 << 17,
74 PATTERN_COND_STAT__IS_FILE = 1 << 18,
75 PATTERN_COND_STAT__IS_DIR = 1 << 19,
76
77 PATTERN_COND_STAT__FILE_CHECK =
78 PATTERN_COND_STAT__HAS_IS_FILE | PATTERN_COND_STAT__IS_FILE,
79 PATTERN_COND_STAT__DIR_CHECK =
80 PATTERN_COND_STAT__HAS_IS_DIR | PATTERN_COND_STAT__IS_DIR,
81};
82
83struct pattern {
84 char *pattern;
85 char *expand_pattern;
86 struct {
87 struct lwan_key_value cookie;
88 struct lwan_key_value env_var;
89 struct lwan_key_value query_var;
90 struct lwan_key_value post_var;
91 struct lwan_key_value header;
92 struct {
93 char *path;
94 } stat;
95 struct {
96 char *script;
97 } lua;
98 enum lwan_request_flags request_flags;
99 /* FIXME: Use pahole to find alignment holes? */
100 } condition;
101 enum pattern_flag flags;
102};
103
104DEFINE_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"
, 104, __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"
, 104, __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"
, 104, __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; }
105
106struct private_data {
107 struct pattern_array patterns;
108};
109
110static enum lwan_http_status module_redirect_to(struct lwan_request *request,
111 const char *url)
112{
113 const struct lwan_key_value headers[] = {
114 {"Location", coro_strdup(request->conn->coro, url)},
115 {},
116 };
117
118 request->response.headers =
119 coro_memdup(request->conn->coro, headers, sizeof(headers));
120
121 if (LIKELY(headers[0].value && request->response.headers)__builtin_expect((!!(headers[0].value && request->
response.headers)), (1))
)
122 return HTTP_MOVED_PERMANENTLY;
123
124 return HTTP_INTERNAL_ERROR;
125}
126
127static enum lwan_http_status module_rewrite_as(struct lwan_request *request,
128 const char *url)
129{
130 request->url.value = coro_strdup(request->conn->coro, url);
131
132 if (UNLIKELY(!request->url.value)__builtin_expect(((!request->url.value)), (0)))
133 return HTTP_INTERNAL_ERROR;
134
135 request->url.len = strlen(request->url.value);
136 request->original_url = request->url;
137 request->flags |= RESPONSE_URL_REWRITTEN;
138
139 return HTTP_OK;
140}
141
142#define MAX_INT_DIGITS(3 * sizeof(int)) (3 * sizeof(int))
143
144static __attribute__((noinline)) int parse_int_len(const char *s, size_t len,
145 int default_value)
146{
147 if (UNLIKELY(len > MAX_INT_DIGITS)__builtin_expect(((len > (3 * sizeof(int)))), (0)))
148 return default_value;
149
150 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);
151}
152
153static const char *expand_string(const char *expand_pattern,
154 const char *orig,
155 char buffer[static PATH_MAX4096],
156 const struct str_find *sf,
157 int captures)
158{
159 struct lwan_strbuf strbuf;
160 const char *ptr;
161
162 ptr = strchr(expand_pattern, '%');
163 if (!ptr)
164 return expand_pattern;
165
166 if (!lwan_strbuf_init_with_fixed_buffer(&strbuf, buffer, PATH_MAX4096))
167 return NULL((void*)0);
168
169 do {
170 size_t index_len = strspn(ptr + 1, "0123456789");
171
172 if (ptr > expand_pattern) {
173 const size_t len = (size_t)(ptr - expand_pattern);
174
175 if (UNLIKELY(!lwan_strbuf_append_str(&strbuf, expand_pattern, len))__builtin_expect(((!lwan_strbuf_append_str(&strbuf, expand_pattern
, len))), (0))
)
176 return NULL((void*)0);
177
178 expand_pattern += len;
179 }
180
181 if (LIKELY(index_len > 0)__builtin_expect((!!(index_len > 0)), (1))) {
182 const int index = parse_int_len(ptr + 1, index_len, -1);
183
184 if (UNLIKELY(index < 0 || index > captures)__builtin_expect(((index < 0 || index > captures)), (0)
)
)
185 return NULL((void*)0);
186
187 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))
188 &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))
189 (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))
)
190 return NULL((void*)0);
191
192 expand_pattern += index_len;
193 } else if (UNLIKELY(!lwan_strbuf_append_char(&strbuf, '%'))__builtin_expect(((!lwan_strbuf_append_char(&strbuf, '%')
)), (0))
) {
194 return NULL((void*)0);
195 }
196
197 expand_pattern++;
198 } while ((ptr = strchr(expand_pattern, '%')));
199
200 const size_t remaining_len = strlen(expand_pattern);
201 if (remaining_len &&
202 !lwan_strbuf_append_str(&strbuf, expand_pattern, remaining_len))
203 return NULL((void*)0);
204
205 if (UNLIKELY(!lwan_strbuf_get_length(&strbuf))__builtin_expect(((!lwan_strbuf_get_length(&strbuf))), (0
))
)
206 return NULL((void*)0);
207
208 return lwan_strbuf_get_buffer(&strbuf);
209}
210
211static ALWAYS_INLINEinline __attribute__((always_inline)) const char *expand(const struct pattern *pattern,
212 const char *orig,
213 char buffer[static PATH_MAX4096],
214 const struct str_find *sf,
215 int captures)
216{
217 return expand_string(pattern->expand_pattern, orig, buffer, sf, captures);
218}
219
220#ifdef HAVE_LUA
221static void
222lua_close_defer(void *data)
223{
224 lua_close((lua_State *)data);
225}
226
227static const char *expand_lua(struct lwan_request *request,
228 struct pattern *pattern, const char *orig,
229 char buffer[static PATH_MAX4096],
230 const struct str_find *sf, int captures)
231{
232 const char *output;
233 size_t output_len;
234 int i;
235 lua_State *L;
236
237 L = lwan_lua_create_state(NULL((void*)0), pattern->expand_pattern);
238 if (UNLIKELY(!L)__builtin_expect(((!L)), (0)))
239 return NULL((void*)0);
240 coro_defer(request->conn->coro, lua_close_defer, L);
241
242 lua_getglobal(L, "handle_rewrite")lua_getfield(L, (-10002), ("handle_rewrite"));
243 if (!lua_isfunction(L, -1)(lua_type(L, (-1)) == 6)) {
244 lwan_status_error(lwan_status_error_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-mod-rewrite.c"
, 246, __FUNCTION__, "Could not obtain reference to `handle_rewrite()` function: %s"
, lwan_lua_state_last_error(L))
245 "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"
, 246, __FUNCTION__, "Could not obtain reference to `handle_rewrite()` function: %s"
, lwan_lua_state_last_error(L))
246 lwan_lua_state_last_error(L))lwan_status_error_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-mod-rewrite.c"
, 246, __FUNCTION__, "Could not obtain reference to `handle_rewrite()` function: %s"
, lwan_lua_state_last_error(L))
;
247 return NULL((void*)0);
248 }
249
250 lwan_lua_state_push_request(L, request);
251
252 lua_createtable(L, captures, 0);
253 for (i = 0; i < captures; i++) {
254 lua_pushinteger(L, i);
255 lua_pushlstring(L, orig + sf[i].sm_so,
256 (size_t)(sf[i].sm_eo - sf[i].sm_so));
257 lua_settable(L, -3);
258 }
259
260 if (lua_pcall(L, 2, 1, 0) != 0) {
261 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"
, 262, __FUNCTION__, "Could not execute `handle_rewrite()` function: %s"
, lwan_lua_state_last_error(L))
262 lwan_lua_state_last_error(L))lwan_status_error_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-mod-rewrite.c"
, 262, __FUNCTION__, "Could not execute `handle_rewrite()` function: %s"
, lwan_lua_state_last_error(L))
;
263 return NULL((void*)0);
264 }
265
266 output = lua_tolstring(L, -1, &output_len);
267 if (output_len >= PATH_MAX4096) {
268 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"
, 269, __FUNCTION__, "Rewritten URL exceeds %d bytes (got %zu bytes)"
, 4096, output_len)
269 PATH_MAX, output_len)lwan_status_error_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-mod-rewrite.c"
, 269, __FUNCTION__, "Rewritten URL exceeds %d bytes (got %zu bytes)"
, 4096, output_len)
;
270 return NULL((void*)0);
271 }
272
273 return memcpy(buffer, output, output_len + 1);
274}
275#endif
276
277static bool_Bool condition_matches(struct lwan_request *request,
278 const struct pattern *p,
279 const struct str_find *sf,
280 int captures,
281 char expanded_buf[static PATH_MAX4096])
282{
283 if (LIKELY(!(p->flags & PATTERN_COND_MASK))__builtin_expect((!!(!(p->flags & PATTERN_COND_MASK)))
, (1))
)
284 return true1;
285
286 const char *url = request->url.value;
287
288 if (p->flags & PATTERN_COND_METHOD) {
289 const enum lwan_request_flags method =
290 p->condition.request_flags & REQUEST_METHOD_MASK;
291 if (lwan_request_get_method(request) != method)
292 return false0;
293 }
294
295 if (p->flags & PATTERN_COND_ACCEPT_ENCODING) {
296 const enum lwan_request_flags accept =
297 p->condition.request_flags & REQUEST_ACCEPT_MASK;
298 if (!(lwan_request_get_accept_encoding(request) & accept))
299 return false0;
300 }
301
302 if (p->flags & PATTERN_COND_PROXIED) {
303 if (!(request->flags & p->condition.request_flags & REQUEST_PROXIED))
304 return false0;
305 }
306
307 if (p->flags & PATTERN_COND_HTTP10) {
308 if (!(request->flags & p->condition.request_flags & REQUEST_IS_HTTP_1_0))
309 return false0;
310 }
311
312 if (p->flags & PATTERN_COND_HAS_QUERY_STRING) {
313 if (!(request->flags & p->condition.request_flags & REQUEST_HAS_QUERY_STRING))
314 return false0;
315 }
316
317 if (p->flags & PATTERN_COND_COOKIE) {
318 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"
, 318, __extension__ __PRETTY_FUNCTION__); }))
;
319 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"
, 319, __extension__ __PRETTY_FUNCTION__); }))
;
320
321 const char *cookie =
322 lwan_request_get_cookie(request, p->condition.cookie.key);
323 if (!cookie)
324 return false0;
325
326 const char *val = expand_string(p->condition.cookie.value, url,
327 expanded_buf, sf, captures);
328 if (!val || !streq(val, cookie))
329 return false0;
330 }
331
332 if (p->flags & PATTERN_COND_ENV_VAR) {
333 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"
, 333, __extension__ __PRETTY_FUNCTION__); }))
;
334 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"
, 334, __extension__ __PRETTY_FUNCTION__); }))
;
335
336 const char *env_var = secure_getenv(p->condition.env_var.key);
337 if (!env_var)
338 return false0;
339
340 const char *val = expand_string(p->condition.env_var.value, url,
341 expanded_buf, sf, captures);
342 if (!val || !streq(val, env_var))
343 return false0;
344 }
345
346 if (p->flags & PATTERN_COND_QUERY_VAR) {
347 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"
, 347, __extension__ __PRETTY_FUNCTION__); }))
;
348 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"
, 348, __extension__ __PRETTY_FUNCTION__); }))
;
349
350 const char *query =
351 lwan_request_get_query_param(request, p->condition.query_var.key);
352
353 if (!query)
354 return false0;
355
356 const char *val = expand_string(p->condition.query_var.value, url,
357 expanded_buf, sf, captures);
358 if (!val || !streq(val, query))
359 return false0;
360 }
361
362 if (p->flags & PATTERN_COND_POST_VAR) {
363 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"
, 363, __extension__ __PRETTY_FUNCTION__); }))
;
364 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"
, 364, __extension__ __PRETTY_FUNCTION__); }))
;
365
366 const char *post =
367 lwan_request_get_post_param(request, p->condition.post_var.key);
368 if (!post)
369 return false0;
370
371 const char *val = expand_string(p->condition.post_var.value, url,
372 expanded_buf, sf, captures);
373 if (!val || !streq(val, post))
374 return false0;
375 }
376
377 if (p->flags & PATTERN_COND_STAT) {
378 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"
, 378, __extension__ __PRETTY_FUNCTION__); }))
;
379
380 struct stat st;
381
382 /* FIXME: Expanding path from a user-controlled URL and use the
383 * resulting path to call stat(2) on could lead to some information
384 * disclosure vulnerability. Would require the server to be configured
385 * in a certain way, though.
386 */
387 const char *path = expand_string(p->condition.stat.path, url,
388 expanded_buf, sf, captures);
389 if (!path || stat(path, &st) < 0)
390 return false0;
391
392 if ((p->flags & PATTERN_COND_STAT__FILE_CHECK) ==
393 PATTERN_COND_STAT__FILE_CHECK &&
394 !S_ISREG(st.st_mode)((((st.st_mode)) & 0170000) == (0100000)))
395 return false0;
396 if ((p->flags & PATTERN_COND_STAT__DIR_CHECK) ==
397 PATTERN_COND_STAT__DIR_CHECK &&
398 !S_ISDIR(st.st_mode)((((st.st_mode)) & 0170000) == (0040000)))
399 return false0;
400 }
401
402#ifdef HAVE_LUA
403 if (p->flags & PATTERN_COND_LUA) {
404 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"
, 404, __extension__ __PRETTY_FUNCTION__); }))
;
405
406 lua_State *L = lwan_lua_create_state(NULL((void*)0), p->condition.lua.script);
407 if (!L)
408 return false0;
409 coro_defer(request->conn->coro, lua_close_defer, L);
410
411 lua_getglobal(L, "matches")lua_getfield(L, (-10002), ("matches"));
412 if (!lua_isfunction(L, -1)(lua_type(L, (-1)) == 6)) {
413 lwan_status_error(lwan_status_error_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-mod-rewrite.c"
, 415, __FUNCTION__, "Could not obtain reference to `matches()` function: %s"
, lwan_lua_state_last_error(L))
414 "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"
, 415, __FUNCTION__, "Could not obtain reference to `matches()` function: %s"
, lwan_lua_state_last_error(L))
415 lwan_lua_state_last_error(L))lwan_status_error_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-mod-rewrite.c"
, 415, __FUNCTION__, "Could not obtain reference to `matches()` function: %s"
, lwan_lua_state_last_error(L))
;
416 return false0;
417 }
418
419 lwan_lua_state_push_request(L, request);
420
421 if (lua_pcall(L, 1, 1, 0) != 0) {
422 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"
, 423, __FUNCTION__, "Could not execute `matches()` function: %s"
, lwan_lua_state_last_error(L))
423 lwan_lua_state_last_error(L))lwan_status_error_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-mod-rewrite.c"
, 423, __FUNCTION__, "Could not execute `matches()` function: %s"
, lwan_lua_state_last_error(L))
;
424 return false0;
425 }
426
427 if (!lua_toboolean(L, -1))
428 return false0;
429 }
430#else
431 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"
, 431, __extension__ __PRETTY_FUNCTION__); }))
;
432#endif
433
434 return true1;
435}
436
437static enum lwan_http_status
438rewrite_handle_request(struct lwan_request *request,
439 struct lwan_response *response __attribute__((unused)),
440 void *instance)
441{
442 struct private_data *pd = instance;
443 const char *url = request->url.value;
444 char final_url[PATH_MAX4096];
445 struct pattern *p;
446
447 LWAN_ARRAY_FOREACH(&pd->patterns, p)for (p = (&pd->patterns)->base.base; p < ((typeof
(p))(&pd->patterns)->base.base + (&pd->patterns
)->base.elements); p++)
{
448 struct str_find sf[MAXCAPTURES32];
449 const char *expanded = NULL((void*)0);
450 const char *errmsg;
451 int captures;
452
453 captures = str_find(url, p->pattern, sf, MAXCAPTURES32, &errmsg);
454 if (captures <= 0)
455 continue;
456
457 if (!condition_matches(request, p, sf, captures, final_url))
458 continue;
459
460 switch (p->flags & PATTERN_EXPAND_MASK) {
461#ifdef HAVE_LUA
462 case PATTERN_EXPAND_LUA:
463 expanded = expand_lua(request, p, url, final_url, sf, captures);
464 break;
465#endif
466 case PATTERN_EXPAND_LWAN:
467 expanded = expand(p, url, final_url, sf, captures);
468 break;
469 }
470
471 if (LIKELY(expanded)__builtin_expect((!!(expanded)), (1))) {
472 switch (p->flags & PATTERN_HANDLE_MASK) {
473 case PATTERN_HANDLE_REDIRECT:
474 return module_redirect_to(request, expanded);
475 case PATTERN_HANDLE_REWRITE:
476 return module_rewrite_as(request, expanded);
477 }
478 }
479
480 return HTTP_INTERNAL_ERROR;
481 }
482
483 return HTTP_NOT_FOUND;
484}
485
486static void *rewrite_create(const char *prefix __attribute__((unused)),
487 void *instance __attribute__((unused)))
488{
489 struct private_data *pd = malloc(sizeof(*pd));
490
491 if (!pd)
492 return NULL((void*)0);
493
494 pattern_array_init(&pd->patterns);
495
496 return pd;
497}
498
499static void rewrite_destroy(void *instance)
500{
501 struct private_data *pd = instance;
502 struct pattern *iter;
503
504 LWAN_ARRAY_FOREACH(&pd->patterns, iter)for (iter = (&pd->patterns)->base.base; iter < (
(typeof(iter))(&pd->patterns)->base.base + (&pd
->patterns)->base.elements); iter++)
{
505 free(iter->pattern);
506 free(iter->expand_pattern);
507 if (iter->flags & PATTERN_COND_COOKIE) {
508 free(iter->condition.cookie.key);
509 free(iter->condition.cookie.value);
510 }
511 if (iter->flags & PATTERN_COND_ENV_VAR) {
512 free(iter->condition.env_var.key);
513 free(iter->condition.env_var.value);
514 }
515 if (iter->flags & PATTERN_COND_QUERY_VAR) {
516 free(iter->condition.query_var.key);
517 free(iter->condition.query_var.value);
518 }
519 if (iter->flags & PATTERN_COND_POST_VAR) {
520 free(iter->condition.post_var.key);
521 free(iter->condition.post_var.value);
522 }
523 if (iter->flags & PATTERN_COND_HEADER) {
524 free(iter->condition.header.key);
525 free(iter->condition.header.value);
526 }
527 if (iter->flags & PATTERN_COND_STAT) {
528 free(iter->condition.stat.path);
529 }
530#ifdef HAVE_LUA
531 if (iter->flags & PATTERN_COND_LUA) {
532 free(iter->condition.lua.script);
533 }
534#endif
535 }
536
537 pattern_array_reset(&pd->patterns);
538 free(pd);
539}
540
541static void *rewrite_create_from_hash(const char *prefix,
542 const struct hash *hash
543 __attribute__((unused)))
544{
545 return rewrite_create(prefix, NULL((void*)0));
546}
547
548static void parse_condition_key_value(struct pattern *pattern,
549 struct lwan_key_value *key_value,
550 enum pattern_flag condition_type,
551 struct config *config,
552 const struct config_line *line)
553{
554 char *key = NULL((void*)0), *value = NULL((void*)0);
555
556 while ((line = config_read_line(config))) {
14
Loop condition is true. Entering loop body
557 switch (line->type) {
15
Control jumps to 'case CONFIG_LINE_TYPE_LINE:' at line 572
558 case CONFIG_LINE_TYPE_SECTION:
559 config_error(config, "Unexpected section: %s", line->key);
560 goto out;
561
562 case CONFIG_LINE_TYPE_SECTION_END:
563 if (!key || !value) {
564 config_error(config, "Key/value has not been specified");
565 goto out;
566 }
567
568 *key_value = (struct lwan_key_value){key, value};
569 pattern->flags |= condition_type & PATTERN_COND_MASK;
570 return;
571
572 case CONFIG_LINE_TYPE_LINE:
573 if (key
15.1
'key' is null
|| value
15.2
'value' is null
) {
16
Taking false branch
574 config_error(config,
575 "Can only condition on a single key/value pair. "
576 "Currently has: %s=%s",
577 key, value);
578 goto out;
579 }
580
581 key = strdup(line->key);
17
Memory is allocated
582 if (!key) {
18
Assuming 'key' is non-null
19
Taking false branch
583 config_error(config,
584 "Could not copy key while parsing condition");
585 goto out;
586 }
587
588 value = strdup(line->value);
589 if (!value) {
20
Assuming 'value' is null
21
Taking true branch
590 free(key);
22
Memory is released
591 config_error(config,
592 "Could not copy value while parsing condition");
593 goto out;
23
Control jumps to line 600
594 }
595 break;
596 }
597 }
598
599out:
600 free(key);
24
Attempt to free released memory
601 free(value);
602}
603
604static void parse_condition_stat(struct pattern *pattern,
605 struct config *config,
606 const struct config_line *line)
607{
608 char *path = NULL((void*)0), *is_dir = NULL((void*)0), *is_file = NULL((void*)0);
609
610 while ((line = config_read_line(config))) {
611 switch (line->type) {
612 case CONFIG_LINE_TYPE_SECTION:
613 config_error(config, "Unexpected section: %s", line->key);
614 goto out;
615
616 case CONFIG_LINE_TYPE_SECTION_END:
617 if (!path) {
618 config_error(config, "Path not specified");
619 goto out;
620 }
621
622 pattern->condition.stat.path = path;
623 if (is_dir) {
624 pattern->flags |= PATTERN_COND_STAT__HAS_IS_DIR;
625 if (parse_bool(is_dir, false0))
626 pattern->flags |= PATTERN_COND_STAT__IS_DIR;
627 }
628 if (is_file) {
629 pattern->flags |= PATTERN_COND_STAT__HAS_IS_FILE;
630 if (parse_bool(is_file, false0))
631 pattern->flags |= PATTERN_COND_STAT__IS_FILE;
632 }
633 pattern->flags |= PATTERN_COND_STAT;
634 return;
635
636 case CONFIG_LINE_TYPE_LINE:
637 if (streq(line->key, "path")) {
638 if (path) {
639 config_error(config, "Path `%s` already specified", path);
640 goto out;
641 }
642 path = strdup(line->value);
643 if (!path) {
644 config_error(config, "Could not copy path");
645 goto out;
646 }
647 } else if (streq(line->key, "is_dir")) {
648 is_dir = line->value;
649 } else if (streq(line->key, "is_file")) {
650 is_file = line->value;
651 } else {
652 config_error(config, "Unexpected key: %s", line->key);
653 goto out;
654 }
655
656 break;
657 }
658 }
659
660out:
661 free(path);
662}
663
664static void parse_condition_accept_encoding(struct pattern *pattern,
665 struct config *config)
666{
667 const struct config_line *line;
668
669 while ((line = config_read_line(config))) {
670 switch (line->type) {
671 case CONFIG_LINE_TYPE_SECTION:
672 config_error(config, "Unexpected section: %s", line->key);
673 return;
674
675 case CONFIG_LINE_TYPE_SECTION_END:
676 pattern->flags |= PATTERN_COND_ACCEPT_ENCODING;
677 return;
678
679 case CONFIG_LINE_TYPE_LINE:
680 if (streq(line->key, "deflate")) {
681 if (parse_bool(line->value, false0))
682 pattern->condition.request_flags |= REQUEST_ACCEPT_DEFLATE;
683 } else if (streq(line->key, "gzip")) {
684 if (parse_bool(line->value, false0))
685 pattern->condition.request_flags |= REQUEST_ACCEPT_GZIP;
686 } else if (streq(line->key, "brotli")) {
687 if (parse_bool(line->value, false0))
688 pattern->condition.request_flags |= REQUEST_ACCEPT_BROTLI;
689 } else if (streq(line->key, "zstd")) {
690 if (parse_bool(line->value, false0))
691 pattern->condition.request_flags |= REQUEST_ACCEPT_ZSTD;
692 } else if (!streq(line->key, "none")) {
693 config_error(config, "Unsupported encoding for condition: %s",
694 line->key);
695 return;
696 }
697 break;
698 }
699 }
700}
701
702#ifdef HAVE_LUA
703static void parse_condition_lua(struct pattern *pattern,
704 struct config *config,
705 const struct config_line *line)
706{
707 char *script = NULL((void*)0);
708
709 while ((line = config_read_line(config))) {
710 switch (line->type) {
711 case CONFIG_LINE_TYPE_SECTION:
712 config_error(config, "Unexpected section: %s", line->key);
713 goto out;
714
715 case CONFIG_LINE_TYPE_SECTION_END:
716 if (!script) {
717 config_error(config, "Script not specified");
718 goto out;
719 }
720
721 pattern->condition.lua.script = script;
722 pattern->flags |= PATTERN_COND_LUA;
723 return;
724
725 case CONFIG_LINE_TYPE_LINE:
726 if (streq(line->key, "script")) {
727 if (script) {
728 config_error(config, "Script already specified");
729 goto out;
730 }
731 script = strdup(line->value);
732 if (!script) {
733 config_error(config, "Could not copy script");
734 goto out;
735 }
736 } else {
737 config_error(config, "Unexpected key: %s", line->key);
738 goto out;
739 }
740
741 break;
742 }
743 }
744
745out:
746 free(script);
747}
748#endif
749
750static bool_Bool get_method_from_string(struct pattern *pattern, const char *string)
751{
752#define GENERATE_CMP(upper, lower, mask, constant) \
753 if (!strcasecmp(string, #upper)) { \
754 pattern->condition.request_flags |= (mask); \
755 return true1; \
756 }
757
758 FOR_EACH_REQUEST_METHOD(GENERATE_CMP)GENERATE_CMP(GET, get, (1 << 0), ((uint32_t)(('G') | ('E'
) << 8 | ('T') << 16 | (' ') << 24))) GENERATE_CMP
(POST, post, (1 << 3 | 1 << 1 | 1 << 0), ((
uint32_t)(('P') | ('O') << 8 | ('S') << 16 | ('T'
) << 24))) GENERATE_CMP(HEAD, head, (1 << 1), ((uint32_t
)(('H') | ('E') << 8 | ('A') << 16 | ('D') <<
24))) GENERATE_CMP(OPTIONS, options, (1 << 2), ((uint32_t
)(('O') | ('P') << 8 | ('T') << 16 | ('I') <<
24))) GENERATE_CMP(DELETE, delete, (1 << 1 | 1 <<
2), ((uint32_t)(('D') | ('E') << 8 | ('L') << 16
| ('E') << 24))) GENERATE_CMP(PUT, put, (1 << 3 |
1 << 2 | 1 << 0), ((uint32_t)(('P') | ('U') <<
8 | ('T') << 16 | (' ') << 24)))
759
760#undef GENERATE_CMP
761
762 return false0;
763}
764
765static void parse_condition(struct pattern *pattern,
766 struct config *config,
767 const struct config_line *line)
768{
769 if (streq(line->value, "cookie")) {
12
Taking true branch
770 return parse_condition_key_value(pattern, &pattern->condition.cookie,
13
Calling 'parse_condition_key_value'
771 PATTERN_COND_COOKIE, config, line);
772 }
773 if (streq(line->value, "query")) {
774 return parse_condition_key_value(pattern, &pattern->condition.query_var,
775 PATTERN_COND_QUERY_VAR, config, line);
776 }
777 if (streq(line->value, "post")) {
778 return parse_condition_key_value(pattern, &pattern->condition.post_var,
779 PATTERN_COND_POST_VAR, config, line);
780 }
781 if (streq(line->value, "environment")) {
782 return parse_condition_key_value(pattern, &pattern->condition.env_var,
783 PATTERN_COND_ENV_VAR, config, line);
784 }
785 if (streq(line->value, "header")) {
786 return parse_condition_key_value(pattern, &pattern->condition.header,
787 PATTERN_COND_HEADER, config, line);
788 }
789 if (streq(line->value, "method")) {
790 struct lwan_key_value method = {};
791
792 parse_condition_key_value(pattern, &method,
793 PATTERN_COND_METHOD, config, line);
794 if (config_last_error(config))
795 return;
796
797 if (!streq(method.key, "name")) {
798 config_error(config, "Method condition requires `name`");
799 } else if (!get_method_from_string(pattern, method.value)) {
800 config_error(config, "Unknown HTTP method: %s", method.value);
801 }
802
803 free(method.key);
804 free(method.value);
805
806 return;
807 }
808 if (streq(line->value, "stat")) {
809 return parse_condition_stat(pattern, config, line);
810 }
811 if (streq(line->value, "encoding")) {
812 return parse_condition_accept_encoding(pattern, config);
813 }
814#ifdef HAVE_LUA
815 if (streq(line->value, "lua")) {
816 return parse_condition_lua(pattern, config, line);
817 }
818#endif
819
820 config_error(config, "Condition `%s' not supported", line->value);
821}
822
823static bool_Bool rewrite_parse_conf_pattern(struct private_data *pd,
824 struct config *config,
825 const struct config_line *line)
826{
827 struct pattern *pattern;
828 char *redirect_to = NULL((void*)0), *rewrite_as = NULL((void*)0);
829 bool_Bool expand_with_lua = false0;
830
831 pattern = pattern_array_append0(&pd->patterns);
832 if (!pattern
4.1
'pattern' is non-null
)
5
Taking false branch
833 goto out_no_free;
834
835 pattern->pattern = strdup(line->value);
836 if (!pattern->pattern)
6
Assuming field 'pattern' is non-null
7
Taking false branch
837 goto out;
838
839 while ((line = config_read_line(config))) {
8
Loop condition is true. Entering loop body
840 switch (line->type) {
9
Control jumps to 'case CONFIG_LINE_TYPE_SECTION:' at line 873
841 case CONFIG_LINE_TYPE_LINE:
842 if (streq(line->key, "redirect_to")) {
843 free(redirect_to);
844
845 redirect_to = strdup(line->value);
846 if (!redirect_to)
847 goto out;
848 } else if (streq(line->key, "rewrite_as")) {
849 free(rewrite_as);
850
851 rewrite_as = strdup(line->value);
852 if (!rewrite_as)
853 goto out;
854 } else if (streq(line->key, "expand_with_lua")) {
855 expand_with_lua = parse_bool(line->value, false0);
856 } else if (streq(line->key, "condition_proxied")) {
857 if (parse_bool(line->value, false0)) {
858 pattern->flags |= PATTERN_COND_PROXIED;
859 }
860 } else if (streq(line->key, "condition_http_1.0")) {
861 if (parse_bool(line->value, false0)) {
862 pattern->flags |= PATTERN_COND_HTTP10;
863 }
864 } else if (streq(line->key, "condition_has_query_string")) {
865 if (parse_bool(line->value, false0)) {
866 pattern->flags |= PATTERN_COND_HAS_QUERY_STRING;
867 }
868 } else {
869 config_error(config, "Unexpected key: %s", line->key);
870 goto out;
871 }
872 break;
873 case CONFIG_LINE_TYPE_SECTION:
874 if (streq(line->key, "condition")) {
10
Taking true branch
875 parse_condition(pattern, config, line);
11
Calling 'parse_condition'
876 } else {
877 config_error(config, "Unexpected section: %s", line->key);
878 }
879 break;
880 case CONFIG_LINE_TYPE_SECTION_END:
881 if (redirect_to && rewrite_as) {
882 config_error(
883 config,
884 "`redirect to` and `rewrite as` are mutually exclusive");
885 goto out;
886 }
887 if (redirect_to) {
888 pattern->expand_pattern = redirect_to;
889 pattern->flags |= PATTERN_HANDLE_REDIRECT;
890 } else if (rewrite_as) {
891 pattern->expand_pattern = rewrite_as;
892 pattern->flags |= PATTERN_HANDLE_REWRITE;
893 } else {
894 config_error(
895 config,
896 "either `redirect to` or `rewrite as` are required");
897 goto out;
898 }
899 if (expand_with_lua) {
900#ifdef HAVE_LUA
901 pattern->flags |= PATTERN_EXPAND_LUA;
902#else
903 config_error(config, "Lwan has been built without Lua. "
904 "`expand_with_lua` is not available");
905 goto out;
906#endif
907 } else {
908 pattern->flags |= PATTERN_EXPAND_LWAN;
909 }
910
911 return true1;
912 }
913 }
914
915out:
916 free(pattern->pattern);
917 free(redirect_to);
918 free(rewrite_as);
919out_no_free:
920 config_error(config, "Could not copy pattern");
921 return false0;
922}
923
924static bool_Bool rewrite_parse_conf(void *instance, struct config *config)
925{
926 struct private_data *pd = instance;
927 const struct config_line *line;
928
929 while ((line = config_read_line(config))) {
1
Loop condition is true. Entering loop body
930 switch (line->type) {
2
Control jumps to 'case CONFIG_LINE_TYPE_SECTION:' at line 934
931 case CONFIG_LINE_TYPE_LINE:
932 config_error(config, "Unknown option: %s", line->key);
933 break;
934 case CONFIG_LINE_TYPE_SECTION:
935 if (streq(line->key, "pattern")) {
3
Taking true branch
936 rewrite_parse_conf_pattern(pd, config, line);
4
Calling 'rewrite_parse_conf_pattern'
937 } else {
938 config_error(config, "Unknown section: %s", line->key);
939 }
940 break;
941 case CONFIG_LINE_TYPE_SECTION_END:
942 break;
943 }
944 }
945
946 return !config_last_error(config);
947}
948
949static const struct lwan_module module = {
950 .create = rewrite_create,
951 .create_from_hash = rewrite_create_from_hash,
952 .parse_conf = rewrite_parse_conf,
953 .destroy = rewrite_destroy,
954 .handle_request = rewrite_handle_request,
955 .flags = HANDLER_CAN_REWRITE_URL
956};
957
958LWAN_REGISTER_MODULE(rewrite, &module)const struct lwan_module_info __attribute__((used, section("lwan_module"
))) lwan_module_info_rewrite = {.name = "rewrite", .module = &
module}
;