Bug Summary

File:lib/lwan-strbuf.c
Warning:line 304, 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 -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 -fno-rounding-math -mconstructor-aliases -fno-plt -munwind-tables -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/13.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/python3.10 -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/13.0.1/include -internal-isystem /usr/local/include -internal-isystem /usr/bin/../lib64/gcc/x86_64-pc-linux-gnu/12.1.0/../../../../x86_64-pc-linux-gnu/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 -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /home/buildbot/lwan-worker/clang-analyze/CLANG/2022-06-05-005737-3677223-1 -x c /home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-strbuf.c
1/*
2 * lwan - simple 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 const size_t aligned_size = align_size(size + 1);
69 if (UNLIKELY(!aligned_size)__builtin_expect(((!aligned_size)), (0)))
70 return false0;
71
72 char *buffer = realloc(s->buffer, aligned_size);
73 if (UNLIKELY(!buffer)__builtin_expect(((!buffer)), (0)))
74 return false0;
75
76 s->buffer = buffer;
77 s->capacity = aligned_size;
78 }
79
80 return true1;
81}
82
83bool_Bool lwan_strbuf_init_with_size(struct lwan_strbuf *s, size_t size)
84{
85 if (UNLIKELY(!s)__builtin_expect(((!s)), (0)))
86 return false0;
87
88 *s = LWAN_STRBUF_STATIC_INIT(struct lwan_strbuf) { .buffer = "" };
89
90 if (size) {
91 if (UNLIKELY(!grow_buffer_if_needed(s, size))__builtin_expect(((!grow_buffer_if_needed(s, size))), (0)))
92 return false0;
93
94 s->buffer[0] = '\0';
95 }
96
97 return true1;
98}
99
100bool_Bool lwan_strbuf_init_with_fixed_buffer(struct lwan_strbuf *s,
101 void *buffer,
102 size_t size)
103{
104 if (UNLIKELY(!s)__builtin_expect(((!s)), (0)))
105 return false0;
106
107 *s = (struct lwan_strbuf) {
108 .capacity = size,
109 .used = 0,
110 .buffer = buffer,
111 .flags = BUFFER_FIXED,
112 };
113
114 return true1;
115}
116
117ALWAYS_INLINEinline __attribute__((always_inline)) bool_Bool lwan_strbuf_init(struct lwan_strbuf *s)
118{
119 return lwan_strbuf_init_with_size(s, 0);
120}
121
122struct lwan_strbuf *lwan_strbuf_new_with_size(size_t size)
123{
124 struct lwan_strbuf *s = malloc(sizeof(*s));
125
126 if (UNLIKELY(!lwan_strbuf_init_with_size(s, size))__builtin_expect(((!lwan_strbuf_init_with_size(s, size))), (0
))
) {
127 free(s);
128
129 return NULL((void*)0);
130 }
131
132 s->flags |= STRBUF_MALLOCD;
133
134 return s;
135}
136
137struct lwan_strbuf *lwan_strbuf_new_with_fixed_buffer(size_t size)
138{
139 struct lwan_strbuf *s = malloc(sizeof(*s) + size + 1);
140
141 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))
) {
142 free(s);
143
144 return NULL((void*)0);
145 }
146
147 s->flags |= STRBUF_MALLOCD;
148
149 return s;
150}
151
152ALWAYS_INLINEinline __attribute__((always_inline)) struct lwan_strbuf *lwan_strbuf_new(void)
153{
154 return lwan_strbuf_new_with_size(0);
155}
156
157ALWAYS_INLINEinline __attribute__((always_inline)) struct lwan_strbuf *lwan_strbuf_new_static(const char *str,
158 size_t size)
159{
160 struct lwan_strbuf *s = malloc(sizeof(*s));
161
162 if (UNLIKELY(!s)__builtin_expect(((!s)), (0)))
163 return NULL((void*)0);
164
165 *s = (struct lwan_strbuf) {
166 .flags = STRBUF_MALLOCD,
167 .buffer = (char *)str,
168 .used = size,
169 .capacity = size,
170 };
171
172 return s;
173}
174
175void lwan_strbuf_free(struct lwan_strbuf *s)
176{
177 if (UNLIKELY(!s)__builtin_expect(((!s)), (0)))
178 return;
179 if (s->flags & BUFFER_MALLOCD) {
180 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"
, 180, __extension__ __PRETTY_FUNCTION__); }))
;
181 free(s->buffer);
182 }
183 if (s->flags & STRBUF_MALLOCD)
184 free(s);
185}
186
187bool_Bool lwan_strbuf_append_char(struct lwan_strbuf *s, const char c)
188{
189 if (UNLIKELY(!grow_buffer_if_needed(s, s->used + 2))__builtin_expect(((!grow_buffer_if_needed(s, s->used + 2))
), (0))
)
190 return false0;
191
192 s->buffer[s->used++] = c;
193 s->buffer[s->used] = '\0';
194
195 return true1;
196}
197
198bool_Bool lwan_strbuf_append_str(struct lwan_strbuf *s1, const char *s2, size_t sz)
199{
200 if (UNLIKELY(!grow_buffer_if_needed(s1, s1->used + sz + 2))__builtin_expect(((!grow_buffer_if_needed(s1, s1->used + sz
+ 2))), (0))
)
201 return false0;
202
203 memcpy(s1->buffer + s1->used, s2, sz);
204 s1->used += sz;
205 s1->buffer[s1->used] = '\0';
206
207 return true1;
208}
209
210bool_Bool lwan_strbuf_set_static(struct lwan_strbuf *s1, const char *s2, size_t sz)
211{
212 if (s1->flags & BUFFER_MALLOCD)
213 free(s1->buffer);
214
215 s1->buffer = (char *)s2;
216 s1->used = s1->capacity = sz;
217 s1->flags &= ~(BUFFER_MALLOCD | BUFFER_FIXED);
218
219 return true1;
220}
221
222bool_Bool lwan_strbuf_set(struct lwan_strbuf *s1, const char *s2, size_t sz)
223{
224 if (UNLIKELY(!grow_buffer_if_needed(s1, sz + 1))__builtin_expect(((!grow_buffer_if_needed(s1, sz + 1))), (0)))
225 return false0;
226
227 memcpy(s1->buffer, s2, sz);
228 s1->used = sz;
229 s1->buffer[sz] = '\0';
230
231 return true1;
232}
233
234static ALWAYS_INLINEinline __attribute__((always_inline)) bool_Bool
235internal_printf(struct lwan_strbuf *s1,
236 bool_Bool (*save_str)(struct lwan_strbuf *, const char *, size_t),
237 const char *fmt,
238 va_list values)
239{
240 char *s2;
241 int len;
242
243 if (UNLIKELY((len = vasprintf(&s2, fmt, values)) < 0)__builtin_expect((((len = vasprintf(&s2, fmt, values)) <
0)), (0))
)
244 return false0;
245
246 bool_Bool success = save_str(s1, s2, (size_t)len);
247 free(s2);
248
249 return success;
250}
251
252bool_Bool lwan_strbuf_vprintf(struct lwan_strbuf *s, const char *fmt, va_list ap)
253{
254 return internal_printf(s, lwan_strbuf_set, fmt, ap);
255}
256
257bool_Bool lwan_strbuf_printf(struct lwan_strbuf *s, const char *fmt, ...)
258{
259 bool_Bool could_printf;
260 va_list values;
261
262 va_start(values, fmt)__builtin_va_start(values, fmt);
263 could_printf = lwan_strbuf_vprintf(s, fmt, values);
264 va_end(values)__builtin_va_end(values);
265
266 return could_printf;
267}
268
269bool_Bool lwan_strbuf_append_vprintf(struct lwan_strbuf *s, const char *fmt, va_list ap)
270{
271 return internal_printf(s, lwan_strbuf_append_str, fmt, ap);
272}
273
274bool_Bool lwan_strbuf_append_printf(struct lwan_strbuf *s, const char *fmt, ...)
275{
276 bool_Bool could_printf;
277 va_list values;
278
279 va_start(values, fmt)__builtin_va_start(values, fmt);
280 could_printf = lwan_strbuf_append_vprintf(s, fmt, values);
281 va_end(values)__builtin_va_end(values);
282
283 return could_printf;
284}
285
286bool_Bool lwan_strbuf_grow_to(struct lwan_strbuf *s, size_t new_size)
287{
288 return grow_buffer_if_needed(s, new_size + 1);
289}
290
291bool_Bool lwan_strbuf_grow_by(struct lwan_strbuf *s, size_t offset)
292{
293 size_t new_size;
294
295 if (__builtin_add_overflow(offset, s->used, &new_size))
296 return false0;
297
298 return lwan_strbuf_grow_to(s, new_size);
299}
300
301void lwan_strbuf_reset(struct lwan_strbuf *s)
302{
303 if (s->flags & BUFFER_MALLOCD) {
6
Assuming the condition is true
7
Taking true branch
304 s->buffer[0] = '\0';
8
Use of memory after it is freed
305 } else {
306 s->buffer = "";
307 s->capacity = 0;
308 }
309
310 s->used = 0;
311}
312
313void lwan_strbuf_reset_trim(struct lwan_strbuf *s, size_t trim_thresh)
314{
315 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
316 free(s->buffer);
4
Memory is released
317 s->flags &= ~BUFFER_MALLOCD;
318 }
319
320 return lwan_strbuf_reset(s);
5
Calling 'lwan_strbuf_reset'
321}
322
323/* This function is quite dangerous, so the prototype is only in lwan-private.h */
324char *lwan_strbuf_extend_unsafe(struct lwan_strbuf *s, size_t by)
325{
326 if (!lwan_strbuf_grow_by(s, by))
327 return NULL((void*)0);
328
329 size_t prev_used = s->used;
330 s->used += by;
331
332 return s->buffer + prev_used;
333}