Bug Summary

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