Bug Summary

File:exec.c
Warning:line 419, column 33
Null pointer passed as an argument to a 'nonnull' parameter

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -triple x86_64-unknown-linux-gnu -analyze -disable-free -disable-llvm-verifier -discard-value-names -main-file-name exec.c -analyzer-store=region -analyzer-opt-analyze-nested-blocks -analyzer-eagerly-assume -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 -mrelocation-model pic -pic-level 2 -mthread-model posix -mdisable-fp-elim -fmath-errno -masm-verbose -mconstructor-aliases -munwind-tables -fuse-init-array -target-cpu x86-64 -dwarf-column-info -debugger-tuning=gdb -resource-dir /usr/local/clang-7.0.0/lib/clang/7.0.0 -D HAVE_CONFIG_H -I . -I /home/travis/build/blogc/blogc -D PIC -internal-isystem /usr/local/include -internal-isystem /usr/local/clang-7.0.0/lib/clang/7.0.0/include -internal-externc-isystem /usr/include/x86_64-linux-gnu -internal-externc-isystem /include -internal-externc-isystem /usr/include -O0 -fdebug-compilation-dir /home/travis/build/blogc/blogc/build -ferror-limit 19 -fmessage-length 0 -fobjc-runtime=gcc -fdiagnostics-show-option -analyzer-output=html -o /home/travis/build/blogc/blogc/build/reports/2019-07-15-202426-7050-1 -x c /home/travis/build/blogc/blogc/src/blogc-make/exec.c -faddrsig
1/*
2 * blogc: A blog compiler.
3 * Copyright (C) 2014-2019 Rafael G. Martins <rafael@rafaelmartins.eng.br>
4 *
5 * This program can be distributed under the terms of the BSD License.
6 * See the file LICENSE.
7 */
8
9#ifdef HAVE_CONFIG_H1
10#include <config.h>
11#endif /* HAVE_CONFIG_H */
12
13#include <stdbool.h>
14#include <stdlib.h>
15#include <stdio.h>
16#include <string.h>
17#include <unistd.h>
18#include <sys/wait.h>
19#include <errno(*__errno_location ()).h>
20#include <libgen.h>
21#include "../common/compat.h"
22#include "../common/error.h"
23#include "../common/file.h"
24#include "../common/utils.h"
25#include "ctx.h"
26#include "exec.h"
27#include "settings.h"
28
29
30char*
31bm_exec_find_binary(const char *argv0, const char *bin, const char *env)
32{
33#ifdef MAKE_EMBEDDED
34 // for embedded blogc-make, if we are looking for blogc, we just return
35 // argv0, because the static binary may not be named `blogc`, and we
36 // prefer to use our own `blogc` instead of some other version around.
37 if (argv0 != NULL((void*)0) && bin != NULL((void*)0) && (0 == strcmp(bin, "blogc"))) {
38 return bc_shell_quote(argv0);
39 }
40#endif
41
42 // first try: env var
43 const char *env_bin = getenv(env);
44 if (env_bin != NULL((void*)0)) {
45 return bc_shell_quote(env_bin);
46 }
47
48 // second try: same dir as current exec
49 // we rely on some assumptions here:
50 //
51 // - if binary is called without a directory, that means location will
52 // be resolved from $PATH, we don't care about doing a dir lookup.
53 // - we *never* call chdir anywhere in the code, so we can assume
54 // that relative paths will work as expected.
55 // - windows path sep is not supported
56 if (argv0 != NULL((void*)0) && (NULL((void*)0) != strchr(argv0, '/'))) {
57 char *path = bc_strdup(argv0);
58 char *dir = bc_strdup(dirname(path));
59 free(path);
60 char *tmp = bc_strdup_printf("%s/%s", dir, bin);
61 free(dir);
62 if (0 == access(tmp, X_OK1)) {
63 char *rv = bc_shell_quote(tmp);
64 free(tmp);
65 return rv;
66 }
67 free(tmp);
68 }
69
70 // last try: $PATH
71 return bc_strdup(bin);
72}
73
74
75int
76bm_exec_command(const char *cmd, const char *input, char **output,
77 char **error, bc_error_t **err)
78{
79 if (err == NULL((void*)0) || *err != NULL((void*)0))
10
Taking false branch
80 return 1;
81
82 int fd_in[2];
83 if (-1 == pipe(fd_in)) {
11
Assuming the condition is false
12
Taking false branch
84 *err = bc_error_new_printf(BLOGC_MAKE_ERROR_EXEC,
85 "Failed to create stdin pipe: %s", strerror(errno(*__errno_location ())));
86 return 1;
87 }
88
89 int fd_out[2];
90 if (-1 == pipe(fd_out)) {
13
Assuming the condition is false
14
Taking false branch
91 *err = bc_error_new_printf(BLOGC_MAKE_ERROR_EXEC,
92 "Failed to create stdout pipe: %s", strerror(errno(*__errno_location ())));
93 close(fd_in[0]);
94 close(fd_in[1]);
95 return 1;
96 }
97
98 int fd_err[2];
99 if (-1 == pipe(fd_err)) {
15
Assuming the condition is false
16
Taking false branch
100 *err = bc_error_new_printf(BLOGC_MAKE_ERROR_EXEC,
101 "Failed to create stderr pipe: %s", strerror(errno(*__errno_location ())));
102 close(fd_in[0]);
103 close(fd_in[1]);
104 close(fd_out[0]);
105 close(fd_out[1]);
106 return 1;
107 }
108
109 pid_t pid = fork();
110 if (pid == -1) {
17
Assuming the condition is false
18
Taking false branch
111 *err = bc_error_new_printf(BLOGC_MAKE_ERROR_EXEC,
112 "Failed to fork: %s", strerror(errno(*__errno_location ())));
113 close(fd_in[0]);
114 close(fd_in[1]);
115 close(fd_out[0]);
116 close(fd_out[1]);
117 close(fd_err[0]);
118 close(fd_err[1]);
119 return 1;
120 }
121
122 // child
123 if (pid == 0) {
19
Assuming 'pid' is not equal to 0
20
Taking false branch
124 close(fd_in[1]);
125 close(fd_out[0]);
126 close(fd_err[0]);
127
128 dup2(fd_in[0], STDIN_FILENO0);
129 dup2(fd_out[1], STDOUT_FILENO1);
130 dup2(fd_err[1], STDERR_FILENO2);
131
132 char *const argv[] = {
133 "/bin/sh",
134 "-c",
135 (char*) cmd,
136 NULL((void*)0),
137 };
138
139 execv(argv[0], argv);
140
141 exit(1);
142 }
143
144 // parent
145 close(fd_in[0]);
146 close(fd_out[1]);
147 close(fd_err[1]);
148
149 if (input != NULL((void*)0)) {
21
Assuming 'input' is equal to NULL
22
Taking false branch
150 if (-1 == write(fd_in[1], input, strlen(input))) {
151 *err = bc_error_new_printf(BLOGC_MAKE_ERROR_EXEC,
152 "Failed to write to stdin pipe: %s", strerror(errno(*__errno_location ())));
153 close(fd_in[1]);
154 close(fd_out[0]);
155 close(fd_err[0]);
156 return 1;
157 }
158 }
159
160 close(fd_in[1]);
161
162 char buffer[BC_FILE_CHUNK_SIZE1024];
163 ssize_t s;
164
165 bc_string_t *out = NULL((void*)0);
166 while(0 != (s = read(fd_out[0], buffer, BC_FILE_CHUNK_SIZE1024))) {
23
Assuming the condition is false
24
Loop condition is false. Execution continues on line 180
167 if (s == -1) {
168 *err = bc_error_new_printf(BLOGC_MAKE_ERROR_EXEC,
169 "Failed to read from stdout pipe: %s", strerror(errno(*__errno_location ())));
170 close(fd_out[0]);
171 close(fd_err[0]);
172 bc_string_free(out, true1);
173 return 1;
174 }
175 if (out == NULL((void*)0)) {
176 out = bc_string_new();
177 }
178 bc_string_append_len(out, buffer, s);
179 }
180 if (out != NULL((void*)0)) {
25
Taking false branch
181 *output = bc_string_free(out, false0);
182 }
183 close(fd_out[0]);
184
185 out = NULL((void*)0);
186 while(0 != (s = read(fd_err[0], buffer, BC_FILE_CHUNK_SIZE1024))) {
26
Assuming the condition is false
27
Loop condition is false. Execution continues on line 198
187 if (s == -1) {
188 *err = bc_error_new_printf(BLOGC_MAKE_ERROR_EXEC,
189 "Failed to read from stderr pipe: %s", strerror(errno(*__errno_location ())));
190 close(fd_err[0]);
191 bc_string_free(out, true1);
192 return 1;
193 }
194 if (out == NULL((void*)0))
195 out = bc_string_new();
196 bc_string_append_len(out, buffer, s);
197 }
198 if (out != NULL((void*)0)) {
28
Taking false branch
199 *error = bc_string_free(out, false0);
200 }
201 close(fd_err[0]);
202
203 int status;
204 waitpid(pid, &status, 0);
205
206 return bc_compat_status_code(status);
29
Returning without writing to '*output'
207}
208
209
210static void
211list_variables(const char *key, const char *value, bc_string_t *str)
212{
213 char *tmp = bc_shell_quote(value);
214 bc_string_append_printf(str, " -D %s=%s", key, tmp);
215 free(tmp);
216}
217
218
219char*
220bm_exec_build_blogc_cmd(const char *blogc_bin, bm_settings_t *settings,
221 bc_trie_t *global_variables, bc_trie_t *local_variables, const char *print,
222 bool_Bool listing, const char *listing_entry, const char *template,
223 const char *output, bool_Bool dev, bool_Bool sources_stdin)
224{
225 bc_string_t *rv = bc_string_new();
226
227 const char *locale = NULL((void*)0);
228 if (settings != NULL((void*)0)) {
229 locale = bc_trie_lookup(settings->settings, "locale");
230 }
231 if (locale != NULL((void*)0)) {
232 char *tmp = bc_shell_quote(locale);
233 bc_string_append_printf(rv, "LC_ALL=%s ", tmp);
234 free(tmp);
235 }
236
237 bc_string_append(rv, blogc_bin);
238
239 if (settings != NULL((void*)0)) {
240 if (settings->tags != NULL((void*)0)) {
241 char *tags = bc_strv_join(settings->tags, " ");
242 bc_string_append_printf(rv, " -D MAKE_TAGS='%s'", tags);
243 free(tags);
244 }
245
246 bc_trie_foreach(settings->global,
247 (bc_trie_foreach_func_t) list_variables, rv);
248 }
249
250 bc_trie_foreach(global_variables, (bc_trie_foreach_func_t) list_variables, rv);
251 bc_trie_foreach(local_variables, (bc_trie_foreach_func_t) list_variables, rv);
252
253 if (dev) {
254 bc_string_append(rv, " -D MAKE_ENV_DEV=1 -D MAKE_ENV='dev'");
255 }
256
257 if (print != NULL((void*)0)) {
258 bc_string_append_printf(rv, " -p %s", print);
259 }
260
261 if (listing) {
262 bc_string_append(rv, " -l");
263 if (listing_entry != NULL((void*)0)) {
264 char *tmp = bc_shell_quote(listing_entry);
265 bc_string_append_printf(rv, " -e %s", tmp);
266 free(tmp);
267 }
268 }
269
270 if (template != NULL((void*)0)) {
271 char *tmp = bc_shell_quote(template);
272 bc_string_append_printf(rv, " -t %s", tmp);
273 free(tmp);
274 }
275
276 if (output != NULL((void*)0)) {
277 char *tmp = bc_shell_quote(output);
278 bc_string_append_printf(rv, " -o %s", tmp);
279 free(tmp);
280 }
281
282 if (sources_stdin) {
283 bc_string_append(rv, " -i");
284 }
285
286 return bc_string_free(rv, false0);
287}
288
289
290int
291bm_exec_blogc(bm_ctx_t *ctx, bc_trie_t *global_variables, bc_trie_t *local_variables,
292 bool_Bool listing, bm_filectx_t *listing_entry, bm_filectx_t *template,
293 bm_filectx_t *output, bc_slist_t *sources, bool_Bool only_first_source)
294{
295 if (ctx == NULL((void*)0))
296 return 1;
297
298 bc_string_t *input = bc_string_new();
299 for (bc_slist_t *l = sources; l != NULL((void*)0); l = l->next) {
300 bc_string_append_printf(input, "%s\n", ((bm_filectx_t*) l->data)->path);
301 if (only_first_source)
302 break;
303 }
304
305 char *cmd = bm_exec_build_blogc_cmd(ctx->blogc, ctx->settings, global_variables,
306 local_variables, NULL((void*)0), listing, listing_entry == NULL((void*)0) ? NULL((void*)0) : listing_entry->path,
307 template->path, output->path, ctx->dev, input->len > 0);
308
309 if (ctx->verbose)
310 printf("%s\n", cmd);
311 else
312 printf(" BLOGC %s\n", output->short_path);
313 fflush(stdoutstdout);
314
315 char *out = NULL((void*)0);
316 char *err = NULL((void*)0);
317 bc_error_t *error = NULL((void*)0);
318
319 int rv = bm_exec_command(cmd, input->str, &out, &err, &error);
320
321 if (error != NULL((void*)0)) {
322 bc_error_print(error, "blogc-make");
323 free(cmd);
324 free(out);
325 free(err);
326 bc_string_free(input, true1);
327 bc_error_free(error);
328 return 1;
329 }
330
331 if (rv != 0 && ctx->verbose) {
332 fprintf(stderrstderr,
333 "blogc-make: error: Failed to execute command.\n"
334 "\n"
335 "STATUS CODE: %d\n", rv);
336 if (input->len > 0) {
337 fprintf(stderrstderr, "\nSTDIN:\n"
338 "----------------------------->8-----------------------------\n"
339 "%s\n"
340 "----------------------------->8-----------------------------\n",
341 bc_str_strip(input->str));
342 }
343 if (out != NULL((void*)0)) {
344 fprintf(stderrstderr, "\nSTDOUT:\n"
345 "----------------------------->8-----------------------------\n"
346 "%s\n"
347 "----------------------------->8-----------------------------\n",
348 bc_str_strip(out));
349 }
350 if (err != NULL((void*)0)) {
351 fprintf(stderrstderr, "\nSTDERR:\n"
352 "----------------------------->8-----------------------------\n"
353 "%s\n"
354 "----------------------------->8-----------------------------\n",
355 bc_str_strip(err));
356 }
357 fprintf(stderrstderr, "\n");
358 }
359 else if (err != NULL((void*)0)) {
360 fprintf(stderrstderr, "%s\n", err);
361 }
362
363 bc_string_free(input, true1);
364 free(cmd);
365 free(out);
366 free(err);
367
368 return rv == 127 ? 1 : rv;
369}
370
371
372char*
373bm_exec_blogc_get_variable(bm_ctx_t *ctx, bc_trie_t *global_variables,
374 bc_trie_t *local_variables, const char *variable, bool_Bool listing,
375 bc_slist_t *sources, bool_Bool only_first_source)
376{
377 if (ctx == NULL((void*)0))
1
Assuming 'ctx' is not equal to NULL
2
Taking false branch
378 return NULL((void*)0);
379
380 bc_string_t *input = bc_string_new();
381 for (bc_slist_t *l = sources; l != NULL((void*)0); l = l->next) {
3
Assuming 'l' is equal to NULL
4
Loop condition is false. Execution continues on line 387
382 bc_string_append_printf(input, "%s\n", ((bm_filectx_t*) l->data)->path);
383 if (only_first_source)
384 break;
385 }
386
387 char *cmd = bm_exec_build_blogc_cmd(ctx->blogc, ctx->settings, global_variables,
388 local_variables, variable, listing, NULL((void*)0), NULL((void*)0), NULL((void*)0), ctx->dev, input->len > 0);
5
Assuming the condition is false
389
390 if (ctx->verbose)
6
Assuming the condition is false
7
Taking false branch
391 printf("%s\n", cmd);
392 fflush(stdoutstdout);
393
394 char *out = NULL((void*)0);
8
'out' initialized to a null pointer value
395 char *err = NULL((void*)0);
396 bc_error_t *error = NULL((void*)0);
397
398 int rv = bm_exec_command(cmd, input->str, &out, &err, &error);
9
Calling 'bm_exec_command'
30
Returning from 'bm_exec_command'
399
400 if (error != NULL((void*)0)) {
31
Taking false branch
401 bc_error_print(error, "blogc-make");
402 bc_error_free(error);
403 bc_string_free(input, true1);
404 free(cmd);
405 free(out);
406 free(err);
407 return NULL((void*)0);
408 }
409
410 if (rv != 0) {
32
Assuming 'rv' is equal to 0
33
Taking false branch
411 fprintf(stderrstderr, "blogc-make: error: %s\n", bc_str_strip(err));
412 bc_string_free(input, true1);
413 free(cmd);
414 free(out);
415 free(err);
416 return NULL((void*)0);
417 }
418
419 char *val = bc_strndup(out, strlen(out) - 1);
34
Null pointer passed as an argument to a 'nonnull' parameter
420
421 bc_string_free(input, true1);
422 free(cmd);
423 free(out);
424 free(err);
425
426 return val;
427}
428
429
430int
431bm_exec_blogc_runserver(bm_ctx_t *ctx, const char *host, const char *port,
432 const char *threads)
433{
434 if (ctx == NULL((void*)0))
435 return 1;
436
437 bc_string_t *cmd = bc_string_new();
438
439 bc_string_append(cmd, ctx->blogc_runserver);
440
441 if (host != NULL((void*)0)) {
442 char *tmp = bc_shell_quote(host);
443 bc_string_append_printf(cmd, " -t %s", tmp);
444 free(tmp);
445 }
446
447 if (port != NULL((void*)0)) {
448 char *tmp = bc_shell_quote(port);
449 bc_string_append_printf(cmd, " -p %s", tmp);
450 free(tmp);
451 }
452
453 if (threads != NULL((void*)0)) {
454 char *tmp = bc_shell_quote(threads);
455 bc_string_append_printf(cmd, " -m %s", tmp);
456 free(tmp);
457 }
458
459 char *tmp = bc_shell_quote(ctx->output_dir);
460 bc_string_append_printf(cmd, " %s", tmp);
461 free(tmp);
462
463 if (ctx->verbose)
464 printf("%s\n\n", cmd->str);
465 else
466 printf("\n");
467 fflush(stdoutstdout);
468
469 // we don't need pipes to run blogc-runserver, because it is "interactive"
470 int status = system(cmd->str);
471 int rv = bc_compat_status_code(status);
472 bc_string_free(cmd, true1);
473
474 if (rv != 0 && rv != 130) {
475 if (rv == 127) {
476 fprintf(stderrstderr,
477 "blogc-make: error: blogc-runserver command not found. Maybe "
478 "it is not installed?\n");
479 rv = 1; // blogc-make exists, so we should not return 127
480 }
481 else {
482 fprintf(stderrstderr,
483 "blogc-make: error: Failed to execute command, returned "
484 "status code: %d\n", rv);
485 }
486 }
487
488 return rv;
489}