Bug Summary

File:lib/lwan-strbuf.c
Warning:line 323, 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/15.0.7 -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.1 -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/lib/clang/15.0.7/include -internal-isystem /usr/local/include -internal-isystem /usr/bin/../lib64/gcc/x86_64-pc-linux-gnu/12.2.1/../../../../x86_64-pc-linux-gnu/include -internal-externc-isystem /include -internal-externc-isystem /usr/include -Wno-unused-parameter -Wno-override-init -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/2023-01-27-043450-1480122-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;
36
37static inline size_t align_size(size_t unaligned_size)
38{
39 const size_t aligned_size = lwan_nextpow2(unaligned_size);
40
41 if (UNLIKELY(unaligned_size >= aligned_size)__builtin_expect(((unaligned_size >= aligned_size)), (0)))
42 return 0;
43
44 return aligned_size;
45}
46
47static bool_Bool grow_buffer_if_needed(struct lwan_strbuf *s, size_t size)
48{
49 if (s->flags & BUFFER_FIXED)
50 return size < s->capacity;
51
52 if (!(s->flags & BUFFER_MALLOCD)) {
53 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
; })
);
54 if (UNLIKELY(!aligned_size)__builtin_expect(((!aligned_size)), (0)))
55 return false0;
56
57 char *buffer = malloc(aligned_size);
58 if (UNLIKELY(!buffer)__builtin_expect(((!buffer)), (0)))
59 return false0;
60
61 memcpy(buffer, s->buffer, s->used);
62 buffer[s->used + 1] = '\0';
63
64 s->flags |= BUFFER_MALLOCD;
65 s->buffer = buffer;
66 s->capacity = aligned_size;
67
68 return true1;
69 }
70
71 if (UNLIKELY(s->capacity < size)__builtin_expect(((s->capacity < size)), (0))) {
72 char *buffer;
73 const size_t aligned_size = align_size(size + 1);
74
75 if (UNLIKELY(!aligned_size)__builtin_expect(((!aligned_size)), (0)))
76 return false0;
77
78 if (s->used == 0) {
79 /* Avoid memcpy() inside realloc() if we were not using the
80 * allocated buffer at this point. */
81 buffer = malloc(aligned_size);
82
83 if (UNLIKELY(!buffer)__builtin_expect(((!buffer)), (0)))
84 return false0;
85
86 free(s->buffer);
87 buffer[0] = '\0';
88 } else {
89 buffer = realloc(s->buffer, aligned_size);
90
91 if (UNLIKELY(!buffer)__builtin_expect(((!buffer)), (0)))
92 return false0;
93 }
94
95 s->buffer = buffer;
96 s->capacity = aligned_size;
97 }
98
99 return true1;
100}
101
102bool_Bool lwan_strbuf_init_with_size(struct lwan_strbuf *s, size_t size)
103{
104 if (UNLIKELY(!s)__builtin_expect(((!s)), (0)))
105 return false0;
106
107 *s = LWAN_STRBUF_STATIC_INIT(struct lwan_strbuf) { .buffer = "" };
108
109 if (size) {
110 if (UNLIKELY(!grow_buffer_if_needed(s, size))__builtin_expect(((!grow_buffer_if_needed(s, size))), (0)))
111 return false0;
112
113 s->buffer[0] = '\0';
114 }
115
116 return true1;
117}
118
119bool_Bool lwan_strbuf_init_with_fixed_buffer(struct lwan_strbuf *s,
120 void *buffer,
121 size_t size)
122{
123 if (UNLIKELY(!s)__builtin_expect(((!s)), (0)))
124 return false0;
125
126 *s = (struct lwan_strbuf) {
127 .capacity = size,
128 .used = 0,
129 .buffer = buffer,
130 .flags = BUFFER_FIXED,
131 };
132
133 return true1;
134}
135
136ALWAYS_INLINEinline __attribute__((always_inline)) bool_Bool lwan_strbuf_init(struct lwan_strbuf *s)
137{
138 return lwan_strbuf_init_with_size(s, 0);
139}
140
141struct lwan_strbuf *lwan_strbuf_new_with_size(size_t size)
142{
143 struct lwan_strbuf *s = malloc(sizeof(*s));
144
145 if (UNLIKELY(!lwan_strbuf_init_with_size(s, size))__builtin_expect(((!lwan_strbuf_init_with_size(s, size))), (0
))
) {
146 free(s);
147
148 return NULL((void*)0);
149 }
150
151 s->flags |= STRBUF_MALLOCD;
152
153 return s;
154}
155
156struct lwan_strbuf *lwan_strbuf_new_with_fixed_buffer(size_t size)
157{
158 struct lwan_strbuf *s = malloc(sizeof(*s) + size + 1);
159
160 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))
) {
161 free(s);
162
163 return NULL((void*)0);
164 }
165
166 s->flags |= STRBUF_MALLOCD;
167
168 return s;
169}
170
171ALWAYS_INLINEinline __attribute__((always_inline)) struct lwan_strbuf *lwan_strbuf_new(void)
172{
173 return lwan_strbuf_new_with_size(0);
174}
175
176ALWAYS_INLINEinline __attribute__((always_inline)) struct lwan_strbuf *lwan_strbuf_new_static(const char *str,
177 size_t size)
178{
179 struct lwan_strbuf *s = malloc(sizeof(*s));
180
181 if (UNLIKELY(!s)__builtin_expect(((!s)), (0)))
182 return NULL((void*)0);
183
184 *s = (struct lwan_strbuf) {
185 .flags = STRBUF_MALLOCD,
186 .buffer = (char *)str,
187 .used = size,
188 .capacity = size,
189 };
190
191 return s;
192}
193
194void lwan_strbuf_free(struct lwan_strbuf *s)
195{
196 if (UNLIKELY(!s)__builtin_expect(((!s)), (0)))
197 return;
198 if (s->flags & BUFFER_MALLOCD) {
199 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"
, 199, __extension__ __PRETTY_FUNCTION__); }))
;
200 free(s->buffer);
201 }
202 if (s->flags & STRBUF_MALLOCD)
203 free(s);
204}
205
206bool_Bool lwan_strbuf_append_char(struct lwan_strbuf *s, const char c)
207{
208 if (UNLIKELY(!grow_buffer_if_needed(s, s->used + 2))__builtin_expect(((!grow_buffer_if_needed(s, s->used + 2))
), (0))
)
209 return false0;
210
211 s->buffer[s->used++] = c;
212 s->buffer[s->used] = '\0';
213
214 return true1;
215}
216
217bool_Bool lwan_strbuf_append_str(struct lwan_strbuf *s1, const char *s2, size_t sz)
218{
219 if (UNLIKELY(!grow_buffer_if_needed(s1, s1->used + sz + 2))__builtin_expect(((!grow_buffer_if_needed(s1, s1->used + sz
+ 2))), (0))
)
220 return false0;
221
222 memcpy(s1->buffer + s1->used, s2, sz);
223 s1->used += sz;
224 s1->buffer[s1->used] = '\0';
225
226 return true1;
227}
228
229bool_Bool lwan_strbuf_set_static(struct lwan_strbuf *s1, const char *s2, size_t sz)
230{
231 if (s1->flags & BUFFER_MALLOCD)
232 free(s1->buffer);
233
234 s1->buffer = (char *)s2;
235 s1->used = s1->capacity = sz;
236 s1->flags &= ~(BUFFER_MALLOCD | BUFFER_FIXED);
237
238 return true1;
239}
240
241bool_Bool lwan_strbuf_set(struct lwan_strbuf *s1, const char *s2, size_t sz)
242{
243 if (UNLIKELY(!grow_buffer_if_needed(s1, sz + 1))__builtin_expect(((!grow_buffer_if_needed(s1, sz + 1))), (0)))
244 return false0;
245
246 memcpy(s1->buffer, s2, sz);
247 s1->used = sz;
248 s1->buffer[sz] = '\0';
249
250 return true1;
251}
252
253static ALWAYS_INLINEinline __attribute__((always_inline)) bool_Bool
254internal_printf(struct lwan_strbuf *s1,
255 bool_Bool (*save_str)(struct lwan_strbuf *, const char *, size_t),
256 const char *fmt,
257 va_list values)
258{
259 char *s2;
260 int len;
261
262 if (UNLIKELY((len = vasprintf(&s2, fmt, values)) < 0)__builtin_expect((((len = vasprintf(&s2, fmt, values)) <
0)), (0))
)
263 return false0;
264
265 bool_Bool success = save_str(s1, s2, (size_t)len);
266 free(s2);
267
268 return success;
269}
270
271bool_Bool lwan_strbuf_vprintf(struct lwan_strbuf *s, const char *fmt, va_list ap)
272{
273 return internal_printf(s, lwan_strbuf_set, fmt, ap);
274}
275
276bool_Bool lwan_strbuf_printf(struct lwan_strbuf *s, const char *fmt, ...)
277{
278 bool_Bool could_printf;
279 va_list values;
280
281 va_start(values, fmt)__builtin_va_start(values, fmt);
282 could_printf = lwan_strbuf_vprintf(s, fmt, values);
283 va_end(values)__builtin_va_end(values);
284
285 return could_printf;
286}
287
288bool_Bool lwan_strbuf_append_vprintf(struct lwan_strbuf *s, const char *fmt, va_list ap)
289{
290 return internal_printf(s, lwan_strbuf_append_str, fmt, ap);
291}
292
293bool_Bool lwan_strbuf_append_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_append_vprintf(s, fmt, values);
300 va_end(values)__builtin_va_end(values);
301
302 return could_printf;
303}
304
305bool_Bool lwan_strbuf_grow_to(struct lwan_strbuf *s, size_t new_size)
306{
307 return grow_buffer_if_needed(s, new_size + 1);
308}
309
310bool_Bool lwan_strbuf_grow_by(struct lwan_strbuf *s, size_t offset)
311{
312 size_t new_size;
313
314 if (__builtin_add_overflow(offset, s->used, &new_size))
315 return false0;
316
317 return lwan_strbuf_grow_to(s, new_size);
318}
319
320void lwan_strbuf_reset(struct lwan_strbuf *s)
321{
322 if (s->flags & BUFFER_MALLOCD) {
6
Assuming the condition is true
7
Taking true branch
323 s->buffer[0] = '\0';
8
Use of memory after it is freed
324 } else {
325 s->buffer = "";
326 s->capacity = 0;
327 }
328
329 s->used = 0;
330}
331
332void lwan_strbuf_reset_trim(struct lwan_strbuf *s, size_t trim_thresh)
333{
334 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
335 free(s->buffer);
4
Memory is released
336 s->flags &= ~BUFFER_MALLOCD;
337 }
338
339 return lwan_strbuf_reset(s);
5
Calling 'lwan_strbuf_reset'
340}
341
342/* This function is quite dangerous, so the prototype is only in lwan-private.h */
343char *lwan_strbuf_extend_unsafe(struct lwan_strbuf *s, size_t by)
344{
345 if (!lwan_strbuf_grow_by(s, by))
346 return NULL((void*)0);
347
348 size_t prev_used = s->used;
349 s->used += by;
350
351 return s->buffer + prev_used;
352}
353
354bool_Bool lwan_strbuf_init_from_file(struct lwan_strbuf *s, const char *path)
355{
356 int fd = open(path, O_RDONLY00 | O_CLOEXEC02000000);
357 struct stat st;
358
359 if (UNLIKELY(fd < 0)__builtin_expect(((fd < 0)), (0)))
360 return false0;
361
362 if (UNLIKELY(fstat(fd, &st) < 0)__builtin_expect(((fstat(fd, &st) < 0)), (0)))
363 goto error;
364
365 if (UNLIKELY(!lwan_strbuf_init_with_size(s, (size_t)st.st_size))__builtin_expect(((!lwan_strbuf_init_with_size(s, (size_t)st.
st_size))), (0))
)
366 goto error;
367
368 s->used = (size_t)st.st_size;
369
370 for (char *buffer = s->buffer; st.st_size; ) {
371 ssize_t n_read = read(fd, buffer, (size_t)st.st_size);
372
373 if (UNLIKELY(n_read < 0)__builtin_expect(((n_read < 0)), (0))) {
374 if (errno(*__errno_location ()) == EINTR4)
375 continue;
376 goto error;
377 }
378
379 buffer += n_read;
380 st.st_size -= (off_t)n_read;
381 }
382
383 close(fd);
384 return true1;
385
386error:
387 lwan_strbuf_free(s);
388 close(fd);
389 return false0;
390}
391
392struct lwan_strbuf *lwan_strbuf_new_from_file(const char *path)
393{
394 struct lwan_strbuf *strbuf = malloc(sizeof(*strbuf));
395
396 if (!strbuf)
397 return NULL((void*)0);
398
399 if (lwan_strbuf_init_from_file(strbuf, path)) {
400 strbuf->flags |= STRBUF_MALLOCD;
401 return strbuf;
402 }
403
404 free(strbuf);
405 return NULL((void*)0);
406}