Bug Summary

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

Annotated Source Code

1/*
2 * lwan - simple web server
3 * Copyright (c) 2016 Leandro A. F. Pereira <leandro@hardinfo.org>
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 <ctype.h>
21#include <errno(*__errno_location ()).h>
22#include <fcntl.h>
23#include <stdint.h>
24#include <stdio.h>
25#include <stdlib.h>
26#include <string.h>
27
28#ifdef HAVE_ZOPFLI
29#include <zopfli/zopfli.h>
30#else
31#include <zlib.h>
32#endif
33
34#include "../../lib/hash.h"
35
36struct output {
37 char *ptr;
38 size_t used, capacity;
39};
40
41static int output_append(struct output *output, const char *str)
42{
43 size_t str_len = strlen(str) + 1;
44 size_t total_size = output->used + str_len;
45
46 if (total_size >= output->capacity) {
47 char *tmp;
48
49 while (total_size >= output->capacity)
50 output->capacity *= 2;
51
52 tmp = realloc(output->ptr, output->capacity);
53 if (!tmp)
54 return -errno(*__errno_location ());
55
56 output->ptr = tmp;
57 }
58
59 memcpy(output->ptr + output->used, str, str_len);
60 output->used = total_size;
61
62 return 0;
63}
64
65static int compare_ext(const void *a, const void *b)
66{
67 const char **exta = (const char **)a;
68 const char **extb = (const char **)b;
69
70 return strcmp(*exta, *extb);
71}
72
73static char *strend(char *str, char ch)
74{
75 str = strchr(str, ch);
76 if (str) {
77 *str = '\0';
78 return str + 1;
79 }
80 return NULL((void*)0);
81}
82
83static char *compress_output(const struct output *output, size_t *outlen)
84{
85 char *compressed;
86
87#ifdef HAVE_ZOPFLI
88 ZopfliOptions opts;
89
90 *outlen = 0;
91
92 ZopfliInitOptions(&opts);
93 ZopfliCompress(&opts, ZOPFLI_FORMAT_ZLIB,
94 (const unsigned char *)output->ptr, output->used,
95 (unsigned char **)&compressed, outlen);
96#else
97 *outlen = compressBound((uLong)output->used);
98 compressed = malloc(*outlen);
99 if (!compressed) {
100 fprintf(stderrstderr, "Could not allocate memory for compressed data\n");
101 exit(1);
102 }
103 if (compress2((Bytef *)compressed, outlen, (const Bytef *)output->ptr, output->used, 9) != Z_OK0) {
104 fprintf(stderrstderr, "Could not compress data\n");
105 exit(1);
106 }
107#endif
108 if (!*outlen)
109 return NULL((void*)0);
110
111 return compressed;
112}
113
114static bool_Bool is_builtin_mime_type(const char *mime)
115{
116 /* These are the mime types supported by Lwan without having to perform
117 * a bsearch(). application/octet-stream is the fallback. */
118 if (streq(mime, "application/octet-stream"))
119 return true1;
120 if (streq(mime, "application/javascript"))
121 return true1;
122 if (streq(mime, "image/jpeg"))
123 return true1;
124 if (streq(mime, "image/png"))
125 return true1;
126 if (streq(mime, "text/html"))
127 return true1;
128 if (streq(mime, "text/css"))
129 return true1;
130 if (streq(mime, "text/plain"))
131 return true1;
132 return false0;
133}
134
135int main(int argc, char *argv[])
136{
137 FILE *fp;
138 char buffer[256];
139 struct output output = { .capacity = 1024 };
140 size_t compressed_size;
141 char *compressed, *ext;
142 struct hash *ext_mime;
143 struct hash_iter iter;
144 const char **exts, *key;
145 size_t i;
146
147 if (argc < 2) {
1
Assuming 'argc' is >= 2
2
Taking false branch
148 fprintf(stderrstderr, "Usage: %s /path/to/mime.types\n", argv[0]);
149 return 1;
150 }
151
152 fp = fopen(argv[1], "re");
153 if (!fp) {
3
Assuming 'fp' is non-null
4
Taking false branch
154 fprintf(stderrstderr, "Could not open %s: %s\n", argv[1], strerror(errno(*__errno_location ())));
155 return 1;
156 }
157
158 ext_mime = hash_str_new(free, free);
159 if (!ext_mime) {
5
Assuming 'ext_mime' is non-null
6
Taking false branch
160 fprintf(stderrstderr, "Could not allocate hash table\n");
161 return 1;
162 }
163
164 while (fgets(buffer, sizeof(buffer), fp)) {
7
Loop condition is false. Execution continues on line 216
165 char *start = buffer, *end, *tab, *mime_type;
166
167 while (*start && isspace(*start)((*__ctype_b_loc ())[(int) ((*start))] & (unsigned short int
) _ISspace)
) /* Strip spaces at the start. */
168 start++;
169 if (*start == '#') /* Ignore commented-out lines. */
170 continue;
171
172 strend(start, '\n'); /* Strip line endings. */
173 strend(start, '#'); /* Strip comments from the middle. */
174 tab = strend(start, '\t');
175 if (!tab) /* Find mime-type/extension separator. */
176 continue;
177
178 mime_type = start;
179 if (is_builtin_mime_type(mime_type))
180 continue;
181
182 while (*tab && *tab == '\t') /* Find first extension. */
183 tab++;
184
185 for (ext = tab; *ext; ext += end - ext + 1) {
186 char *k, *v;
187 int r;
188
189 end = strchr(ext, ' '); /* Stop at next extension. */
190 if (!end)
191 end = strchr(ext, '\0'); /* If not found, find last extension. */
192 *end = '\0';
193
194 k = strdup(ext);
195 v = strdup(mime_type);
196
197 if (!k || !v) {
198 fprintf(stderrstderr, "Could not allocate memory\n");
199 return 1;
200 }
201
202 r = hash_add_unique(ext_mime, k, v);
203 if (r < 0) {
204 free(k);
205 free(v);
206
207 if (r != -EEXIST17) {
208 fprintf(stderrstderr, "Could not add extension to hash table\n");
209 return 1;
210 }
211 }
212 }
213 }
214
215 /* Get sorted list of extensions. */
216 exts = calloc(hash_get_count(ext_mime), sizeof(char *));
217 if (!exts) {
8
Assuming 'exts' is non-null
9
Taking false branch
218 fprintf(stderrstderr, "Could not allocate extension array\n");
219 return 1;
220 }
221 hash_iter_init(ext_mime, &iter);
222 for (i = 0; hash_iter_next(&iter, (const void **)&key, NULL((void*)0)); i++)
10
Loop condition is false. Execution continues on line 224
223 exts[i] = key;
224 qsort(exts, hash_get_count(ext_mime), sizeof(char *), compare_ext);
225
226 /* Generate uncompressed blob. */
227 output.ptr = malloc(output.capacity);
11
Memory is allocated
228 if (!output.ptr) {
12
Assuming the condition is false
13
Taking false branch
229 fprintf(stderrstderr, "Could not allocate temporary memory\n");
230 return 1;
231 }
232 for (i = 0; i < hash_get_count(ext_mime); i++) {
14
Assuming the condition is false
15
Loop condition is false. Execution continues on line 244
233 if (output_append(&output, exts[i]) < 0) {
234 fprintf(stderrstderr, "Could not append to output\n");
235 return 1;
236 }
237 if (output_append(&output, hash_find(ext_mime, exts[i])) < 0) {
238 fprintf(stderrstderr, "Could not append to output\n");
239 return 1;
240 }
241 }
242
243 /* Compress blob. */
244 compressed = compress_output(&output, &compressed_size);
245 if (!compressed) {
16
Taking true branch
246 fprintf(stderrstderr, "Could not compress data\n");
17
Potential leak of memory pointed to by 'output.ptr'
247 return 1;
248 }
249
250 /* Print output. */
251 printf("#pragma once\n");
252 printf("#define MIME_UNCOMPRESSED_LEN %zu\n", output.used);
253 printf("#define MIME_COMPRESSED_LEN %lu\n", compressed_size);
254 printf("#define MIME_ENTRIES %d\n", hash_get_count(ext_mime));
255 printf("struct mime_entry { const char *extension; const char *type; };\n");
256 printf("static const unsigned char mime_entries_compressed[] = {\n");
257 for (i = 1; compressed_size; compressed_size--, i++)
258 printf("0x%x,%c", compressed[i - 1] & 0xff, " \n"[i % 13 == 0]);
259 printf("};\n");
260
261 free(compressed);
262 free(output.ptr);
263 free(exts);
264 hash_free(ext_mime);
265 fclose(fp);
266
267 return 0;
268}