Bug Summary

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