Bug Summary

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