Bug Summary

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