Bug Summary

File:lib/lwan-strbuf.c
Warning:line 319, 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-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 -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/14.0.6 -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/14.0.6/include -internal-isystem /usr/local/include -internal-isystem /usr/bin/../lib64/gcc/x86_64-pc-linux-gnu/12.2.0/../../../../x86_64-pc-linux-gnu/include -internal-externc-isystem /include -internal-externc-isystem /usr/include -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/2022-10-12-052144-3323220-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 <limits.h>
23#include <stdarg.h>
24#include <stdlib.h>
25#include <string.h>
26
27#include "lwan-private.h"
28
29static const unsigned int BUFFER_MALLOCD = 1 << 0;
30static const unsigned int STRBUF_MALLOCD = 1 << 1;
31static const unsigned int BUFFER_FIXED = 1 << 2;
32
33static inline size_t align_size(size_t unaligned_size)
34{
35 const size_t aligned_size = lwan_nextpow2(unaligned_size);
36
37 if (UNLIKELY(unaligned_size >= aligned_size)__builtin_expect(((unaligned_size >= aligned_size)), (0)))
38 return 0;
39
40 return aligned_size;
41}
42
43static bool_Bool grow_buffer_if_needed(struct lwan_strbuf *s, size_t size)
44{
45 if (s->flags & BUFFER_FIXED)
46 return size < s->capacity;
47
48 if (!(s->flags & BUFFER_MALLOCD)) {
49 const size_t aligned_size = align_size(LWAN_MAX(size + 1, s->used)({ const __typeof__((size + 1) + 0) lwan_tmp_id4 = (size + 1)
; const __typeof__((s->used) + 0) lwan_tmp_id5 = (s->used
); lwan_tmp_id4 < lwan_tmp_id5 ? lwan_tmp_id5 : lwan_tmp_id4
; })
);
50 if (UNLIKELY(!aligned_size)__builtin_expect(((!aligned_size)), (0)))
51 return false0;
52
53 char *buffer = malloc(aligned_size);
54 if (UNLIKELY(!buffer)__builtin_expect(((!buffer)), (0)))
55 return false0;
56
57 memcpy(buffer, s->buffer, s->used);
58 buffer[s->used + 1] = '\0';
59
60 s->flags |= BUFFER_MALLOCD;
61 s->buffer = buffer;
62 s->capacity = aligned_size;
63
64 return true1;
65 }
66
67 if (UNLIKELY(s->capacity < size)__builtin_expect(((s->capacity < size)), (0))) {
68 char *buffer;
69 const size_t aligned_size = align_size(size + 1);
70
71 if (UNLIKELY(!aligned_size)__builtin_expect(((!aligned_size)), (0)))
72 return false0;
73
74 if (s->used == 0) {
75 /* Avoid memcpy() inside realloc() if we were not using the
76 * allocated buffer at this point. */
77 buffer = malloc(aligned_size);
78
79 if (UNLIKELY(!buffer)__builtin_expect(((!buffer)), (0)))
80 return false0;
81
82 free(s->buffer);
83 buffer[0] = '\0';
84 } else {
85 buffer = realloc(s->buffer, aligned_size);
86
87 if (UNLIKELY(!buffer)__builtin_expect(((!buffer)), (0)))
88 return false0;
89 }
90
91 s->buffer = buffer;
92 s->capacity = aligned_size;
93 }
94
95 return true1;
96}
97
98bool_Bool lwan_strbuf_init_with_size(struct lwan_strbuf *s, size_t size)
99{
100 if (UNLIKELY(!s)__builtin_expect(((!s)), (0)))
101 return false0;
102
103 *s = LWAN_STRBUF_STATIC_INIT(struct lwan_strbuf) { .buffer = "" };
104
105 if (size) {
106 if (UNLIKELY(!grow_buffer_if_needed(s, size))__builtin_expect(((!grow_buffer_if_needed(s, size))), (0)))
107 return false0;
108
109 s->buffer[0] = '\0';
110 }
111
112 return true1;
113}
114
115bool_Bool lwan_strbuf_init_with_fixed_buffer(struct lwan_strbuf *s,
116 void *buffer,
117 size_t size)
118{
119 if (UNLIKELY(!s)__builtin_expect(((!s)), (0)))
120 return false0;
121
122 *s = (struct lwan_strbuf) {
123 .capacity = size,
124 .used = 0,
125 .buffer = buffer,
126 .flags = BUFFER_FIXED,
127 };
128
129 return true1;
130}
131
132ALWAYS_INLINEinline __attribute__((always_inline)) bool_Bool lwan_strbuf_init(struct lwan_strbuf *s)
133{
134 return lwan_strbuf_init_with_size(s, 0);
135}
136
137struct lwan_strbuf *lwan_strbuf_new_with_size(size_t size)
138{
139 struct lwan_strbuf *s = malloc(sizeof(*s));
140
141 if (UNLIKELY(!lwan_strbuf_init_with_size(s, size))__builtin_expect(((!lwan_strbuf_init_with_size(s, size))), (0
))
) {
142 free(s);
143
144 return NULL((void*)0);
145 }
146
147 s->flags |= STRBUF_MALLOCD;
148
149 return s;
150}
151
152struct lwan_strbuf *lwan_strbuf_new_with_fixed_buffer(size_t size)
153{
154 struct lwan_strbuf *s = malloc(sizeof(*s) + size + 1);
155
156 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))
) {
157 free(s);
158
159 return NULL((void*)0);
160 }
161
162 s->flags |= STRBUF_MALLOCD;
163
164 return s;
165}
166
167ALWAYS_INLINEinline __attribute__((always_inline)) struct lwan_strbuf *lwan_strbuf_new(void)
168{
169 return lwan_strbuf_new_with_size(0);
170}
171
172ALWAYS_INLINEinline __attribute__((always_inline)) struct lwan_strbuf *lwan_strbuf_new_static(const char *str,
173 size_t size)
174{
175 struct lwan_strbuf *s = malloc(sizeof(*s));
176
177 if (UNLIKELY(!s)__builtin_expect(((!s)), (0)))
178 return NULL((void*)0);
179
180 *s = (struct lwan_strbuf) {
181 .flags = STRBUF_MALLOCD,
182 .buffer = (char *)str,
183 .used = size,
184 .capacity = size,
185 };
186
187 return s;
188}
189
190void lwan_strbuf_free(struct lwan_strbuf *s)
191{
192 if (UNLIKELY(!s)__builtin_expect(((!s)), (0)))
193 return;
194 if (s->flags & BUFFER_MALLOCD) {
195 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"
, 195, __extension__ __PRETTY_FUNCTION__); }))
;
196 free(s->buffer);
197 }
198 if (s->flags & STRBUF_MALLOCD)
199 free(s);
200}
201
202bool_Bool lwan_strbuf_append_char(struct lwan_strbuf *s, const char c)
203{
204 if (UNLIKELY(!grow_buffer_if_needed(s, s->used + 2))__builtin_expect(((!grow_buffer_if_needed(s, s->used + 2))
), (0))
)
205 return false0;
206
207 s->buffer[s->used++] = c;
208 s->buffer[s->used] = '\0';
209
210 return true1;
211}
212
213bool_Bool lwan_strbuf_append_str(struct lwan_strbuf *s1, const char *s2, size_t sz)
214{
215 if (UNLIKELY(!grow_buffer_if_needed(s1, s1->used + sz + 2))__builtin_expect(((!grow_buffer_if_needed(s1, s1->used + sz
+ 2))), (0))
)
216 return false0;
217
218 memcpy(s1->buffer + s1->used, s2, sz);
219 s1->used += sz;
220 s1->buffer[s1->used] = '\0';
221
222 return true1;
223}
224
225bool_Bool lwan_strbuf_set_static(struct lwan_strbuf *s1, const char *s2, size_t sz)
226{
227 if (s1->flags & BUFFER_MALLOCD)
228 free(s1->buffer);
229
230 s1->buffer = (char *)s2;
231 s1->used = s1->capacity = sz;
232 s1->flags &= ~(BUFFER_MALLOCD | BUFFER_FIXED);
233
234 return true1;
235}
236
237bool_Bool lwan_strbuf_set(struct lwan_strbuf *s1, const char *s2, size_t sz)
238{
239 if (UNLIKELY(!grow_buffer_if_needed(s1, sz + 1))__builtin_expect(((!grow_buffer_if_needed(s1, sz + 1))), (0)))
240 return false0;
241
242 memcpy(s1->buffer, s2, sz);
243 s1->used = sz;
244 s1->buffer[sz] = '\0';
245
246 return true1;
247}
248
249static ALWAYS_INLINEinline __attribute__((always_inline)) bool_Bool
250internal_printf(struct lwan_strbuf *s1,
251 bool_Bool (*save_str)(struct lwan_strbuf *, const char *, size_t),
252 const char *fmt,
253 va_list values)
254{
255 char *s2;
256 int len;
257
258 if (UNLIKELY((len = vasprintf(&s2, fmt, values)) < 0)__builtin_expect((((len = vasprintf(&s2, fmt, values)) <
0)), (0))
)
259 return false0;
260
261 bool_Bool success = save_str(s1, s2, (size_t)len);
262 free(s2);
263
264 return success;
265}
266
267bool_Bool lwan_strbuf_vprintf(struct lwan_strbuf *s, const char *fmt, va_list ap)
268{
269 return internal_printf(s, lwan_strbuf_set, fmt, ap);
270}
271
272bool_Bool lwan_strbuf_printf(struct lwan_strbuf *s, const char *fmt, ...)
273{
274 bool_Bool could_printf;
275 va_list values;
276
277 va_start(values, fmt)__builtin_va_start(values, fmt);
278 could_printf = lwan_strbuf_vprintf(s, fmt, values);
279 va_end(values)__builtin_va_end(values);
280
281 return could_printf;
282}
283
284bool_Bool lwan_strbuf_append_vprintf(struct lwan_strbuf *s, const char *fmt, va_list ap)
285{
286 return internal_printf(s, lwan_strbuf_append_str, fmt, ap);
287}
288
289bool_Bool lwan_strbuf_append_printf(struct lwan_strbuf *s, const char *fmt, ...)
290{
291 bool_Bool could_printf;
292 va_list values;
293
294 va_start(values, fmt)__builtin_va_start(values, fmt);
295 could_printf = lwan_strbuf_append_vprintf(s, fmt, values);
296 va_end(values)__builtin_va_end(values);
297
298 return could_printf;
299}
300
301bool_Bool lwan_strbuf_grow_to(struct lwan_strbuf *s, size_t new_size)
302{
303 return grow_buffer_if_needed(s, new_size + 1);
304}
305
306bool_Bool lwan_strbuf_grow_by(struct lwan_strbuf *s, size_t offset)
307{
308 size_t new_size;
309
310 if (__builtin_add_overflow(offset, s->used, &new_size))
311 return false0;
312
313 return lwan_strbuf_grow_to(s, new_size);
314}
315
316void lwan_strbuf_reset(struct lwan_strbuf *s)
317{
318 if (s->flags & BUFFER_MALLOCD) {
6
Assuming the condition is true
7
Taking true branch
319 s->buffer[0] = '\0';
8
Use of memory after it is freed
320 } else {
321 s->buffer = "";
322 s->capacity = 0;
323 }
324
325 s->used = 0;
326}
327
328void lwan_strbuf_reset_trim(struct lwan_strbuf *s, size_t trim_thresh)
329{
330 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
331 free(s->buffer);
4
Memory is released
332 s->flags &= ~BUFFER_MALLOCD;
333 }
334
335 return lwan_strbuf_reset(s);
5
Calling 'lwan_strbuf_reset'
336}
337
338/* This function is quite dangerous, so the prototype is only in lwan-private.h */
339char *lwan_strbuf_extend_unsafe(struct lwan_strbuf *s, size_t by)
340{
341 if (!lwan_strbuf_grow_by(s, by))
342 return NULL((void*)0);
343
344 size_t prev_used = s->used;
345 s->used += by;
346
347 return s->buffer + prev_used;
348}