Bug Summary

File:bin/tools/mimegen.c
Warning:line 329, column 9
Potential leak of memory pointed to by 'output.ptr'

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 mimegen.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 -pic-is-pie -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/bin/tools -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 -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/bin/tools -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-11-03-202149-596009-1 -x c /home/buildbot/lwan-worker/clang-analyze/build/src/bin/tools/mimegen.c
1/*
2 * lwan - web server
3 * Copyright (c) 2016 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, USA.
18 */
19
20#include <assert.h>
21#include <ctype.h>
22#include <errno(*__errno_location ()).h>
23#include <fcntl.h>
24#include <stdint.h>
25#include <stdio.h>
26#include <stdlib.h>
27#include <string.h>
28
29#if defined(LWAN_HAVE_BROTLI)
30#include <brotli/encode.h>
31#elif defined(LWAN_HAVE_ZSTD)
32#include <zstd.h>
33#elif defined(LWAN_HAVE_ZOPFLI)
34#include <zopfli/zopfli.h>
35#else
36#include <zlib.h>
37#endif
38
39#include "../../lib/hash.h"
40
41struct output {
42 char *ptr;
43 size_t used, capacity;
44};
45
46static int
47output_append_full(struct output *output, const char *str, size_t str_len)
48{
49 size_t total_size = output->used + str_len;
50
51 if (total_size >= output->capacity) {
22
Assuming 'total_size' is >= field 'capacity'
23
Taking true branch
52 char *tmp;
53
54 while (total_size
23.1
'total_size' is >= field 'capacity'
>= output->capacity
)
24
Loop condition is true. Entering loop body
25
Assuming 'total_size' is < field 'capacity'
26
Loop condition is false. Execution continues on line 57
55 output->capacity *= 2;
56
57 tmp = realloc(output->ptr, output->capacity);
27
Memory is allocated
58 if (!tmp)
28
Assuming 'tmp' is non-null
29
Taking false branch
59 return -errno(*__errno_location ());
60
61 output->ptr = tmp;
62 }
63
64 memcpy(output->ptr + output->used, str, str_len);
65 output->used = total_size;
66
67 return 0;
68}
69
70static int output_append_padded(struct output *output, const char *str)
71{
72 size_t str_len = strlen(str);
73
74 assert(str_len <= 8)__builtin_assume(str_len <= 8);
17
Assuming 'str_len' is <= 8
75
76 int r = output_append_full(output, str, str_len);
77 if (r
17.1
'r' is >= 0
< 0)
18
Taking false branch
78 return r;
79
80 if (str_len != 8)
19
Assuming 'str_len' is not equal to 8
20
Taking true branch
81 return output_append_full(output, "\0\0\0\0\0\0\0\0", 8 - str_len);
21
Calling 'output_append_full'
30
Returned allocated memory
82
83 return 0;
84}
85
86static int output_append(struct output *output, const char *str)
87{
88 return output_append_full(output, str, strlen(str) + 1);
89}
90
91static int compare_ext(const void *a, const void *b)
92{
93 const char **exta = (const char **)a;
94 const char **extb = (const char **)b;
95
96 return strcasecmp(*exta, *extb);
97}
98
99static char *strend(char *str, char ch)
100{
101 str = strchr(str, ch);
102 if (str) {
103 *str = '\0';
104 return str + 1;
105 }
106 return NULL((void*)0);
107}
108
109static char *compress_output(const struct output *output, size_t *outlen)
110{
111 char *compressed;
112
113#if defined(LWAN_HAVE_BROTLI)
114 *outlen = BrotliEncoderMaxCompressedSize(output->used);
115
116 compressed = malloc(*outlen);
117 if (!compressed) {
118 fprintf(stderrstderr, "Could not allocate memory for compressed data\n");
119 exit(1);
120 }
121
122 if (BrotliEncoderCompress(BROTLI_MAX_QUALITY11, BROTLI_MAX_WINDOW_BITS24,
123 BROTLI_MODE_TEXT, output->used,
124 (const unsigned char *)output->ptr, outlen,
125 (unsigned char *)compressed) != BROTLI_TRUE1) {
126 fprintf(stderrstderr, "Could not compress mime type table with Brotli\n");
127 exit(1);
128 }
129#elif defined(LWAN_HAVE_ZSTD)
130 *outlen = ZSTD_compressBound(output->used);
131
132 compressed = malloc(*outlen);
133 if (!compressed) {
134 fprintf(stderrstderr, "Could not allocate memory for compressed data\n");
135 exit(1);
136 }
137
138 *outlen = ZSTD_compress(compressed, *outlen, output->ptr, output->used,
139 ZSTD_maxCLevel());
140 if (ZSTD_isError(*outlen)) {
141 fprintf(stderrstderr, "Could not compress mime type table with ZSTD\n");
142 exit(1);
143 }
144#elif defined(LWAN_HAVE_ZOPFLI)
145 ZopfliOptions opts;
146
147 *outlen = 0;
148
149 ZopfliInitOptions(&opts);
150 ZopfliCompress(&opts, ZOPFLI_FORMAT_ZLIB,
151 (const unsigned char *)output->ptr, output->used,
152 (unsigned char **)&compressed, outlen);
153#else
154 *outlen = compressBound((uLong)output->used);
155 compressed = malloc(*outlen);
156 if (!compressed) {
157 fprintf(stderrstderr, "Could not allocate memory for compressed data\n");
158 exit(1);
159 }
160 if (compress2((Bytef *)compressed, outlen, (const Bytef *)output->ptr,
161 output->used, 9) != Z_OK) {
162 fprintf(stderrstderr, "Could not compress data with zlib\n");
163 exit(1);
164 }
165#endif
166 if (!*outlen) {
167 free(compressed);
168 return NULL((void*)0);
169 }
170
171 return compressed;
172}
173
174static bool_Bool is_builtin_mime_type(const char *mime)
175{
176 /* These are the mime types supported by Lwan without having to perform
177 * a bsearch(). application/octet-stream is the fallback. */
178 if (streq(mime, "application/octet-stream"))
179 return true1;
180 if (streq(mime, "text/javascript"))
181 return true1;
182 if (streq(mime, "image/jpeg"))
183 return true1;
184 if (streq(mime, "image/gif"))
185 return true1;
186 if (streq(mime, "image/png"))
187 return true1;
188 if (streq(mime, "text/html"))
189 return true1;
190 if (streq(mime, "text/css"))
191 return true1;
192 if (streq(mime, "text/plain"))
193 return true1;
194 return false0;
195}
196
197int main(int argc, char *argv[])
198{
199 FILE *fp;
200 char buffer[256];
201 struct output output = { .capacity = 1024 };
202 size_t compressed_size;
203 char *compressed, *ext;
204 struct hash *ext_mime;
205 struct hash_iter iter;
206 const char **exts, *key;
207 size_t i;
208
209 if (argc < 2) {
1
Assuming 'argc' is >= 2
2
Taking false branch
210 fprintf(stderrstderr, "Usage: %s /path/to/mime.types\n", argv[0]);
211 return 1;
212 }
213
214 fp = fopen(argv[1], "re");
215 if (!fp) {
3
Assuming 'fp' is non-null
4
Taking false branch
216 fprintf(stderrstderr, "Could not open %s: %s\n", argv[1], strerror(errno(*__errno_location ())));
217 return 1;
218 }
219
220 ext_mime = hash_str_new(free, free);
221 if (!ext_mime) {
5
Assuming 'ext_mime' is non-null
6
Taking false branch
222 fprintf(stderrstderr, "Could not allocate hash table\n");
223 fclose(fp);
224 return 1;
225 }
226
227 while (fgets(buffer, sizeof(buffer), fp)) {
7
Loop condition is false. Execution continues on line 286
228 char *start = buffer, *end, *tab, *mime_type;
229
230 while (*start && isspace(*start)((*__ctype_b_loc ())[(int) ((*start))] & (unsigned short int
) _ISspace)
) /* Strip spaces at the start. */
231 start++;
232 if (*start == '#') /* Ignore commented-out lines. */
233 continue;
234
235 strend(start, '\n'); /* Strip line endings. */
236 strend(start, '#'); /* Strip comments from the middle. */
237 tab = strend(start, '\t');
238 if (!tab) /* Find mime-type/extension separator. */
239 continue;
240
241 mime_type = start;
242 if (is_builtin_mime_type(mime_type))
243 continue;
244
245 while (*tab && *tab == '\t') /* Find first extension. */
246 tab++;
247
248 for (ext = tab; *ext; ext += end - ext + 1) {
249 char *k, *v;
250 int r;
251
252 end = strchr(ext, ' '); /* Stop at next extension. */
253 if (!end)
254 end = strchr(ext, '\0'); /* If not found, find last extension. */
255 *end = '\0';
256
257 if (end - ext > 8) {
258 /* Truncate extensions over 8 characters. See commit 2050759297. */
259 ext[8] = '\0';
260 }
261
262 k = strdup(ext);
263 v = strdup(mime_type);
264
265 if (!k || !v) {
266 fprintf(stderrstderr, "Could not allocate memory\n");
267 fclose(fp);
268 return 1;
269 }
270
271 r = hash_add_unique(ext_mime, k, v);
272 if (r < 0) {
273 free(k);
274 free(v);
275
276 if (r != -EEXIST17) {
277 fprintf(stderrstderr, "Could not add extension to hash table\n");
278 fclose(fp);
279 return 1;
280 }
281 }
282 }
283 }
284
285 /* Get sorted list of extensions. */
286 exts = calloc(hash_get_count(ext_mime), sizeof(char *));
287 if (!exts) {
8
Assuming 'exts' is non-null
9
Taking false branch
288 fprintf(stderrstderr, "Could not allocate extension array\n");
289 fclose(fp);
290 return 1;
291 }
292 hash_iter_init(ext_mime, &iter);
293 for (i = 0; hash_iter_next(&iter, (const void **)&key, NULL((void*)0)); i++)
10
Loop condition is false. Execution continues on line 295
294 exts[i] = key;
295 qsort(exts, hash_get_count(ext_mime), sizeof(char *), compare_ext);
296
297 /* Generate uncompressed blob. */
298 output.ptr = malloc(output.capacity);
299 if (!output.ptr) {
11
Assuming field 'ptr' is non-null
12
Taking false branch
300 fprintf(stderrstderr, "Could not allocate temporary memory\n");
301 fclose(fp);
302 return 1;
303 }
304 for (i = 0; i < hash_get_count(ext_mime); i++) {
13
Assuming the condition is true
14
Loop condition is true. Entering loop body
33
Assuming the condition is false
34
Loop condition is false. Execution continues on line 318
305 char ext_lower[9] = {0};
306
307 strncpy(ext_lower, exts[i], 8);
308
309 for (char *p = ext_lower; *p; p++)
15
Loop condition is false. Execution continues on line 312
310 *p &= ~0x20;
311
312 if (output_append_padded(&output, ext_lower) < 0) {
16
Calling 'output_append_padded'
31
Returned allocated memory
32
Taking false branch
313 fprintf(stderrstderr, "Could not append to output\n");
314 fclose(fp);
315 return 1;
316 }
317 }
318 for (i = 0; i < hash_get_count(ext_mime); i++) {
35
Assuming the condition is false
36
Loop condition is false. Execution continues on line 327
319 if (output_append(&output, hash_find(ext_mime, exts[i])) < 0) {
320 fprintf(stderrstderr, "Could not append to output\n");
321 fclose(fp);
322 return 1;
323 }
324 }
325
326 /* Compress blob. */
327 compressed = compress_output(&output, &compressed_size);
328 if (!compressed
36.1
'compressed' is null
) {
37
Taking true branch
329 fprintf(stderrstderr, "Could not compress data\n");
38
Potential leak of memory pointed to by 'output.ptr'
330 fclose(fp);
331 return 1;
332 }
333
334 /* Print output. */
335#if defined(LWAN_HAVE_BROTLI)
336 printf("/* Compressed with brotli */\n");
337#elif defined(LWAN_HAVE_ZSTD)
338 printf("/* Compressed with zstd */\n");
339#elif defined(LWAN_HAVE_ZOPFLI)
340 printf("/* Compressed with zopfli (deflate) */\n");
341#else
342 printf("/* Compressed with zlib (deflate) */\n");
343#endif
344 printf("#pragma once\n");
345 printf("#define MIME_UNCOMPRESSED_LEN %zu\n", output.used);
346 printf("#define MIME_COMPRESSED_LEN %lu\n", compressed_size);
347 printf("#define MIME_ENTRIES %d\n", hash_get_count(ext_mime));
348 printf("static const unsigned char mime_entries_compressed[] = {\n");
349 for (i = 1; compressed_size; compressed_size--, i++)
350 printf("0x%02x,%c", compressed[i - 1] & 0xff, " \n"[i % 13 == 0]);
351 printf("};\n");
352
353 free(compressed);
354 free(output.ptr);
355 free(exts);
356 hash_free(ext_mime);
357 fclose(fp);
358
359 return 0;
360}