Bug Summary

File:lib/lwan-strbuf.c
Warning:line 340, column 22
Use of memory after it is freed

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-pc-linux-gnu -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name lwan-strbuf.c -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 -ffp-contract=on -fno-rounding-math -mconstructor-aliases -fno-plt -funwind-tables=2 -target-cpu x86-64 -tune-cpu generic -debugger-tuning=gdb -fcoverage-compilation-dir=/home/buildbot/lwan-worker/clang-analyze/build/src/lib -resource-dir /usr/lib/clang/17 -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 /home/buildbot/lwan-worker/clang-analyze/build -I /usr/include/luajit-2.1 -I /usr/include/valgrind -I /home/buildbot/lwan-worker/clang-analyze/build/src/lib -internal-isystem /usr/lib/clang/17/include -internal-isystem /usr/local/include -internal-isystem /usr/bin/../lib64/gcc/x86_64-pc-linux-gnu/13.2.1/../../../../x86_64-pc-linux-gnu/include -internal-externc-isystem /include -internal-externc-isystem /usr/include -Wno-override-init -Wno-unused-parameter -Wno-free-nonheap-object -std=gnu11 -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 -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /home/buildbot/lwan-worker/clang-analyze/CLANG/2024-03-27-035253-1297550-1 -x c /home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-strbuf.c
1/*
2 * lwan - web server
3 * Copyright (c) 2012 L. A. F. Pereira <l@tia.mat.br>
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 <errno(*__errno_location ()).h>
23#include <fcntl.h>
24#include <limits.h>
25#include <stdarg.h>
26#include <stddef.h>
27#include <stdlib.h>
28#include <string.h>
29#include <sys/stat.h>
30
31#include "lwan-private.h"
32
33static const unsigned int BUFFER_MALLOCD = 1 << 0;
34static const unsigned int STRBUF_MALLOCD = 1 << 1;
35static const unsigned int BUFFER_FIXED = 1 << 2;
36static const unsigned int GROW_BUFFER_FAILED = 1 << 3;
37
38bool_Bool lwan_strbuf_has_grow_buffer_failed_flag(const struct lwan_strbuf *s)
39{
40 return s->flags & GROW_BUFFER_FAILED;
41}
42
43static inline size_t align_size(size_t unaligned_size)
44{
45 const size_t aligned_size = lwan_nextpow2(unaligned_size);
46
47 if (UNLIKELY(unaligned_size >= aligned_size)__builtin_expect(((unaligned_size >= aligned_size)), (0)))
48 return 0;
49
50 return aligned_size;
51}
52
53static ALWAYS_INLINEinline __attribute__((always_inline))
54bool_Bool grow_buffer_if_needed_internal(struct lwan_strbuf *s, size_t size)
55{
56 if (s->flags & BUFFER_FIXED)
57 return size < s->capacity;
58
59 if (!(s->flags & BUFFER_MALLOCD)) {
60 const size_t aligned_size = align_size(LWAN_MAX(size + 1, s->used)({ const __typeof__((size + 1) + 0) lwan_tmp_id6 = (size + 1)
; const __typeof__((s->used) + 0) lwan_tmp_id7 = (s->used
); lwan_tmp_id6 < lwan_tmp_id7 ? lwan_tmp_id7 : lwan_tmp_id6
; })
);
61 if (UNLIKELY(!aligned_size)__builtin_expect(((!aligned_size)), (0)))
62 return false0;
63
64 char *buffer = malloc(aligned_size);
65 if (UNLIKELY(!buffer)__builtin_expect(((!buffer)), (0)))
66 return false0;
67
68 memcpy(buffer, s->buffer, s->used);
69 buffer[s->used + 1] = '\0';
70
71 s->flags |= BUFFER_MALLOCD;
72 s->buffer = buffer;
73 s->capacity = aligned_size;
74
75 return true1;
76 }
77
78 if (UNLIKELY(s->capacity < size)__builtin_expect(((s->capacity < size)), (0))) {
79 char *buffer;
80 const size_t aligned_size = align_size(size + 1);
81
82 if (UNLIKELY(!aligned_size)__builtin_expect(((!aligned_size)), (0)))
83 return false0;
84
85 if (s->used == 0) {
86 /* Avoid memcpy() inside realloc() if we were not using the
87 * allocated buffer at this point. */
88 buffer = malloc(aligned_size);
89
90 if (UNLIKELY(!buffer)__builtin_expect(((!buffer)), (0)))
91 return false0;
92
93 free(s->buffer);
94 buffer[0] = '\0';
95 } else {
96 buffer = realloc(s->buffer, aligned_size);
97
98 if (UNLIKELY(!buffer)__builtin_expect(((!buffer)), (0)))
99 return false0;
100 }
101
102 s->buffer = buffer;
103 s->capacity = aligned_size;
104 }
105
106 return true1;
107}
108
109static bool_Bool grow_buffer_if_needed(struct lwan_strbuf *s, size_t size)
110{
111 if (UNLIKELY(!grow_buffer_if_needed_internal(s, size))__builtin_expect(((!grow_buffer_if_needed_internal(s, size)))
, (0))
) {
112 s->flags |= GROW_BUFFER_FAILED;
113 return false0;
114 }
115
116 return true1;
117}
118
119bool_Bool lwan_strbuf_init_with_size(struct lwan_strbuf *s, size_t size)
120{
121 if (UNLIKELY(!s)__builtin_expect(((!s)), (0)))
122 return false0;
123
124 *s = LWAN_STRBUF_STATIC_INIT(struct lwan_strbuf) { .buffer = "" };
125
126 if (size) {
127 if (UNLIKELY(!grow_buffer_if_needed(s, size))__builtin_expect(((!grow_buffer_if_needed(s, size))), (0)))
128 return false0;
129
130 s->buffer[0] = '\0';
131 }
132
133 return true1;
134}
135
136bool_Bool lwan_strbuf_init_with_fixed_buffer(struct lwan_strbuf *s,
137 void *buffer,
138 size_t size)
139{
140 if (UNLIKELY(!s)__builtin_expect(((!s)), (0)))
141 return false0;
142
143 *s = (struct lwan_strbuf) {
144 .capacity = size,
145 .used = 0,
146 .buffer = buffer,
147 .flags = BUFFER_FIXED,
148 };
149
150 return true1;
151}
152
153ALWAYS_INLINEinline __attribute__((always_inline)) bool_Bool lwan_strbuf_init(struct lwan_strbuf *s)
154{
155 return lwan_strbuf_init_with_size(s, 0);
156}
157
158struct lwan_strbuf *lwan_strbuf_new_with_size(size_t size)
159{
160 struct lwan_strbuf *s = malloc(sizeof(*s));
161
162 if (UNLIKELY(!lwan_strbuf_init_with_size(s, size))__builtin_expect(((!lwan_strbuf_init_with_size(s, size))), (0
))
) {
163 free(s);
164
165 return NULL((void*)0);
166 }
167
168 s->flags |= STRBUF_MALLOCD;
169
170 return s;
171}
172
173struct lwan_strbuf *lwan_strbuf_new_with_fixed_buffer(size_t size)
174{
175 struct lwan_strbuf *s = malloc(sizeof(*s) + size + 1);
176
177 if (UNLIKELY(!lwan_strbuf_init_with_fixed_buffer(s, s + 1, size))__builtin_expect(((!lwan_strbuf_init_with_fixed_buffer(s, s +
1, size))), (0))
) {
178 free(s);
179
180 return NULL((void*)0);
181 }
182
183 s->flags |= STRBUF_MALLOCD;
184
185 return s;
186}
187
188ALWAYS_INLINEinline __attribute__((always_inline)) struct lwan_strbuf *lwan_strbuf_new(void)
189{
190 return lwan_strbuf_new_with_size(0);
191}
192
193ALWAYS_INLINEinline __attribute__((always_inline)) struct lwan_strbuf *lwan_strbuf_new_static(const char *str,
194 size_t size)
195{
196 struct lwan_strbuf *s = malloc(sizeof(*s));
197
198 if (UNLIKELY(!s)__builtin_expect(((!s)), (0)))
199 return NULL((void*)0);
200
201 *s = (struct lwan_strbuf) {
202 .flags = STRBUF_MALLOCD,
203 .buffer = (char *)str,
204 .used = size,
205 .capacity = size,
206 };
207
208 return s;
209}
210
211void lwan_strbuf_free(struct lwan_strbuf *s)
212{
213 if (UNLIKELY(!s)__builtin_expect(((!s)), (0)))
214 return;
215 if (s->flags & BUFFER_MALLOCD) {
216 assert(!(s->flags & BUFFER_FIXED))((void) sizeof ((!(s->flags & BUFFER_FIXED)) ? 1 : 0),
__extension__ ({ if (!(s->flags & BUFFER_FIXED)) ; else
__assert_fail ("!(s->flags & BUFFER_FIXED)", "/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-strbuf.c"
, 216, __extension__ __PRETTY_FUNCTION__); }))
;
217 free(s->buffer);
218 }
219 if (s->flags & STRBUF_MALLOCD)
220 free(s);
221}
222
223bool_Bool lwan_strbuf_append_char(struct lwan_strbuf *s, const char c)
224{
225 if (UNLIKELY(!grow_buffer_if_needed(s, s->used + 2))__builtin_expect(((!grow_buffer_if_needed(s, s->used + 2))
), (0))
)
226 return false0;
227
228 s->buffer[s->used++] = c;
229 s->buffer[s->used] = '\0';
230
231 return true1;
232}
233
234bool_Bool lwan_strbuf_append_str(struct lwan_strbuf *s1, const char *s2, size_t sz)
235{
236 if (UNLIKELY(!grow_buffer_if_needed(s1, s1->used + sz + 2))__builtin_expect(((!grow_buffer_if_needed(s1, s1->used + sz
+ 2))), (0))
)
237 return false0;
238
239 memcpy(s1->buffer + s1->used, s2, sz);
240 s1->used += sz;
241 s1->buffer[s1->used] = '\0';
242
243 return true1;
244}
245
246bool_Bool lwan_strbuf_set_static(struct lwan_strbuf *s1, const char *s2, size_t sz)
247{
248 if (s1->flags & BUFFER_MALLOCD)
249 free(s1->buffer);
250
251 s1->buffer = (char *)s2;
252 s1->used = s1->capacity = sz;
253 s1->flags &= ~(BUFFER_MALLOCD | BUFFER_FIXED);
254
255 return true1;
256}
257
258bool_Bool lwan_strbuf_set(struct lwan_strbuf *s1, const char *s2, size_t sz)
259{
260 if (UNLIKELY(!grow_buffer_if_needed(s1, sz + 1))__builtin_expect(((!grow_buffer_if_needed(s1, sz + 1))), (0)))
261 return false0;
262
263 memcpy(s1->buffer, s2, sz);
264 s1->used = sz;
265 s1->buffer[sz] = '\0';
266
267 return true1;
268}
269
270static ALWAYS_INLINEinline __attribute__((always_inline)) bool_Bool
271internal_printf(struct lwan_strbuf *s1,
272 bool_Bool (*save_str)(struct lwan_strbuf *, const char *, size_t),
273 const char *fmt,
274 va_list values)
275{
276 char *s2;
277 int len;
278
279 if (UNLIKELY((len = vasprintf(&s2, fmt, values)) < 0)__builtin_expect((((len = vasprintf(&s2, fmt, values)) <
0)), (0))
)
280 return false0;
281
282 bool_Bool success = save_str(s1, s2, (size_t)len);
283 free(s2);
284
285 return success;
286}
287
288bool_Bool lwan_strbuf_vprintf(struct lwan_strbuf *s, const char *fmt, va_list ap)
289{
290 return internal_printf(s, lwan_strbuf_set, fmt, ap);
291}
292
293bool_Bool lwan_strbuf_printf(struct lwan_strbuf *s, const char *fmt, ...)
294{
295 bool_Bool could_printf;
296 va_list values;
297
298 va_start(values, fmt)__builtin_va_start(values, fmt);
299 could_printf = lwan_strbuf_vprintf(s, fmt, values);
300 va_end(values)__builtin_va_end(values);
301
302 return could_printf;
303}
304
305bool_Bool lwan_strbuf_append_vprintf(struct lwan_strbuf *s, const char *fmt, va_list ap)
306{
307 return internal_printf(s, lwan_strbuf_append_str, fmt, ap);
308}
309
310bool_Bool lwan_strbuf_append_printf(struct lwan_strbuf *s, const char *fmt, ...)
311{
312 bool_Bool could_printf;
313 va_list values;
314
315 va_start(values, fmt)__builtin_va_start(values, fmt);
316 could_printf = lwan_strbuf_append_vprintf(s, fmt, values);
317 va_end(values)__builtin_va_end(values);
318
319 return could_printf;
320}
321
322bool_Bool lwan_strbuf_grow_to(struct lwan_strbuf *s, size_t new_size)
323{
324 return grow_buffer_if_needed(s, new_size + 1);
325}
326
327bool_Bool lwan_strbuf_grow_by(struct lwan_strbuf *s, size_t offset)
328{
329 size_t new_size;
330
331 if (__builtin_add_overflow(offset, s->used, &new_size))
332 return false0;
333
334 return lwan_strbuf_grow_to(s, new_size);
335}
336
337void lwan_strbuf_reset(struct lwan_strbuf *s)
338{
339 if (s->flags & BUFFER_MALLOCD) {
6
Assuming the condition is true
7
Taking true branch
340 s->buffer[0] = '\0';
8
Use of memory after it is freed
341 } else {
342 s->buffer = "";
343 s->capacity = 0;
344 }
345
346 s->used = 0;
347}
348
349void lwan_strbuf_reset_trim(struct lwan_strbuf *s, size_t trim_thresh)
350{
351 if (s->flags & BUFFER_MALLOCD && s->capacity > trim_thresh) {
1
Assuming the condition is true
2
Assuming 'trim_thresh' is < field 'capacity'
3
Taking true branch
352 free(s->buffer);
4
Memory is released
353 s->flags &= ~BUFFER_MALLOCD;
354 }
355
356 return lwan_strbuf_reset(s);
5
Calling 'lwan_strbuf_reset'
357}
358
359/* This function is quite dangerous, so the prototype is only in lwan-private.h */
360char *lwan_strbuf_extend_unsafe(struct lwan_strbuf *s, size_t by)
361{
362 if (!lwan_strbuf_grow_by(s, by))
363 return NULL((void*)0);
364
365 size_t prev_used = s->used;
366 s->used += by;
367
368 return s->buffer + prev_used;
369}
370
371bool_Bool lwan_strbuf_init_from_file(struct lwan_strbuf *s, const char *path)
372{
373 int fd = open(path, O_RDONLY00 | O_CLOEXEC02000000);
374 struct stat st;
375
376 if (UNLIKELY(fd < 0)__builtin_expect(((fd < 0)), (0)))
377 return false0;
378
379 if (UNLIKELY(fstat(fd, &st) < 0)__builtin_expect(((fstat(fd, &st) < 0)), (0)))
380 goto error;
381
382 size_t min_buf_size;
383 if (UNLIKELY(__builtin_add_overflow(st.st_size, 1, &min_buf_size))__builtin_expect(((__builtin_add_overflow(st.st_size, 1, &
min_buf_size))), (0))
)
384 goto error;
385 if (UNLIKELY(!lwan_strbuf_init_with_size(s, min_buf_size))__builtin_expect(((!lwan_strbuf_init_with_size(s, min_buf_size
))), (0))
)
386 goto error;
387
388 s->used = (size_t)st.st_size;
389
390 for (char *buffer = s->buffer; st.st_size; ) {
391 ssize_t n_read = read(fd, buffer, (size_t)st.st_size);
392
393 if (UNLIKELY(n_read < 0)__builtin_expect(((n_read < 0)), (0))) {
394 if (errno(*__errno_location ()) == EINTR4)
395 continue;
396 goto error;
397 }
398
399 buffer += n_read;
400 *buffer = '\0';
401 st.st_size -= (off_t)n_read;
402 }
403
404 close(fd);
405 return true1;
406
407error:
408 lwan_strbuf_free(s);
409 close(fd);
410 return false0;
411}
412
413struct lwan_strbuf *lwan_strbuf_new_from_file(const char *path)
414{
415 struct lwan_strbuf *strbuf = malloc(sizeof(*strbuf));
416
417 if (!strbuf)
418 return NULL((void*)0);
419
420 if (lwan_strbuf_init_from_file(strbuf, path)) {
421 strbuf->flags |= STRBUF_MALLOCD;
422 return strbuf;
423 }
424
425 free(strbuf);
426 return NULL((void*)0);
427}