Bug Summary

File:3rdparty/vboot/host/lib/subprocess.c
Warning:line 328, column 3
Null pointer passed to 1st parameter expecting 'nonnull'

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-unknown-linux-gnu -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name subprocess.c -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=none -fmath-errno -ffp-contract=on -fno-rounding-math -mconstructor-aliases -funwind-tables=2 -target-cpu x86-64 -tune-cpu generic -debugger-tuning=gdb -ffunction-sections -fdata-sections -fcoverage-compilation-dir=/home/coreboot/node-root/workspace/coreboot_scanbuild/3rdparty/vboot -resource-dir /opt/xgcc/lib/clang/17 -D CHROMEOS_ENVIRONMENT -D EC_EFS=0 -D EXTERNAL_TPM_CLEAR_REQUEST=0 -D _GNU_SOURCE -D _FILE_OFFSET_BITS=64 -D HAVE_EXECINFO_H -D HAVE_CROSID -D HAVE_NSS -I firmware/include -I firmware/lib/include -I firmware/lib/cgptlib/include -I firmware/lib/tpm_lite/include -I firmware/2lib/include -I host/include -I host/lib/include -I host/lib21/include -internal-isystem /opt/xgcc/lib/clang/17/include -internal-isystem /usr/local/include -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/13/../../../../x86_64-linux-gnu/include -internal-externc-isystem /usr/include/x86_64-linux-gnu -internal-externc-isystem /include -internal-externc-isystem /usr/include -source-date-epoch 1714465709 -Os -Wno-trigraphs -Wwrite-strings -Wno-format-security -Wno-address-of-packed-member -Wno-unknown-warning -std=gnu11 -fconst-strings -fdebug-compilation-dir=/home/coreboot/node-root/workspace/coreboot_scanbuild/3rdparty/vboot -ferror-limit 19 -fgnuc-version=4.2.1 -vectorize-loops -vectorize-slp -analyzer-opt-analyze-headers -analyzer-output=html -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /cb-build/coreboot_scanbuild.0/sharedutils-scanbuildtmp/2024-05-02-073004-2299942-1 -x c host/lib/subprocess.c
1/* Copyright 2019 The ChromiumOS Authors
2 * Use of this source code is governed by a BSD-style license that can be
3 * found in the LICENSE file.
4 */
5
6#include <errno(*__errno_location ()).h>
7#include <fcntl.h>
8#include <stdbool.h>
9#include <stdlib.h>
10#include <string.h>
11#include <sys/wait.h>
12#include <unistd.h>
13
14#include "2common.h"
15#include "subprocess.h"
16
17#define MAX_CB_BUF_SIZE2048 2048
18
19static int init_target_private(struct subprocess_target *target)
20{
21 switch (target->type) {
22 case TARGET_BUFFER:
23 case TARGET_BUFFER_NULL_TERMINATED:
24 case TARGET_CALLBACK:
25 return pipe(target->priv.pipefd);
26 default:
27 return 0;
28 }
29}
30
31static int flags_for_fd(int fd)
32{
33 switch (fd) {
34 case STDIN_FILENO0:
35 return O_RDONLY00;
36 case STDOUT_FILENO1:
37 case STDERR_FILENO2:
38 return O_WRONLY01;
39 default:
40 return -1;
41 }
42}
43
44static int connect_process_target(struct subprocess_target *target, int fd)
45{
46 int target_fd;
47
48 switch (target->type) {
49 case TARGET_NULL:
50 target_fd = open("/dev/null", flags_for_fd(fd));
51 break;
52 case TARGET_FD:
53 target_fd = target->fd;
54 break;
55 case TARGET_FILE:
56 target_fd = fileno(target->file);
57 break;
58 case TARGET_BUFFER:
59 case TARGET_BUFFER_NULL_TERMINATED:
60 case TARGET_CALLBACK:
61 switch (fd) {
62 case STDIN_FILENO0:
63 target_fd = target->priv.pipefd[0];
64 close(target->priv.pipefd[1]);
65 break;
66 case STDOUT_FILENO1:
67 case STDERR_FILENO2:
68 target_fd = target->priv.pipefd[1];
69 close(target->priv.pipefd[0]);
70 break;
71 default:
72 return -1;
73 }
74 break;
75 default:
76 return -1;
77 }
78
79 return dup2(target_fd, fd);
80}
81
82static int process_target_input_buffer(struct subprocess_target *target)
83{
84 ssize_t write_rv;
85 size_t bytes_to_write;
86 char *buf;
87
88 switch (target->type) {
89 case TARGET_BUFFER:
90 bytes_to_write = target->buffer.size;
91 break;
92 case TARGET_BUFFER_NULL_TERMINATED:
93 bytes_to_write = strlen(target->buffer.buf);
94 break;
95 default:
96 return -1;
97 }
98
99 buf = target->buffer.buf;
100 while (bytes_to_write) {
101 write_rv = write(target->priv.pipefd[1], buf, bytes_to_write);
102 if (write_rv <= 0)
103 return -1;
104 buf += write_rv;
105 bytes_to_write -= write_rv;
106 }
107
108 return 0;
109}
110
111static int process_target_input_cb(struct subprocess_target *target)
112{
113 ssize_t write_rv, bytes_to_write;
114 char buf[MAX_CB_BUF_SIZE2048];
115 char *bufptr;
116
117 for (;;) {
118 bytes_to_write = target->callback.cb(buf, MAX_CB_BUF_SIZE2048,
119 target->callback.data);
120 if (bytes_to_write < 0 || bytes_to_write > MAX_CB_BUF_SIZE2048)
121 return -1;
122 if (bytes_to_write == 0)
123 return 0;
124
125 bufptr = buf;
126 while (bytes_to_write) {
127 write_rv = write(target->priv.pipefd[1], bufptr,
128 bytes_to_write);
129 if (write_rv <= 0)
130 return -1;
131 bufptr += write_rv;
132 bytes_to_write -= write_rv;
133 }
134 }
135}
136
137static int process_target_input(struct subprocess_target *target)
138{
139 int rv;
140
141 switch (target->type) {
142 case TARGET_BUFFER:
143 case TARGET_BUFFER_NULL_TERMINATED:
144 case TARGET_CALLBACK:
145 break;
146 default:
147 return 0;
148 }
149
150 close(target->priv.pipefd[0]);
151 switch (target->type) {
152 case TARGET_BUFFER:
153 case TARGET_BUFFER_NULL_TERMINATED:
154 rv = process_target_input_buffer(target);
155 break;
156 case TARGET_CALLBACK:
157 rv = process_target_input_cb(target);
158 break;
159 default:
160 return -1;
161 }
162
163 close(target->priv.pipefd[1]);
164 return rv;
165}
166
167static int process_target_output_buffer(struct subprocess_target *target)
168{
169 ssize_t read_rv;
170 size_t bytes_remaining;
171
172 switch (target->type) {
173 case TARGET_BUFFER:
174 bytes_remaining = target->buffer.size;
175 break;
176 case TARGET_BUFFER_NULL_TERMINATED:
177 if (target->buffer.size == 0)
178 return -1;
179 bytes_remaining = target->buffer.size - 1;
180 break;
181 default:
182 return 0;
183 }
184
185 target->buffer.bytes_consumed = 0;
186 while (bytes_remaining) {
187 read_rv = read(
188 target->priv.pipefd[0],
189 target->buffer.buf + target->buffer.bytes_consumed,
190 bytes_remaining);
191 if (read_rv < 0)
192 return -1;
193 if (read_rv == 0)
194 break;
195 target->buffer.bytes_consumed += read_rv;
196 bytes_remaining -= read_rv;
197 }
198
199 if (target->type == TARGET_BUFFER_NULL_TERMINATED)
200 target->buffer.buf[target->buffer.bytes_consumed] = '\0';
201 return 0;
202}
203
204static int process_target_output_cb(struct subprocess_target *target)
205{
206 char buf[MAX_CB_BUF_SIZE2048];
207 ssize_t rv;
208
209 for (;;) {
210 rv = read(target->priv.pipefd[0], buf, MAX_CB_BUF_SIZE2048);
211 if (rv < 0)
212 return -1;
213 if (rv == 0)
214 break;
215 if (target->callback.cb(buf, rv, target->callback.data) < 0)
216 return -1;
217 }
218
219 return 0;
220}
221
222static int process_target_output(struct subprocess_target *target)
223{
224 int rv;
225
226 switch (target->type) {
227 case TARGET_BUFFER:
228 case TARGET_BUFFER_NULL_TERMINATED:
229 case TARGET_CALLBACK:
230 break;
231 default:
232 return 0;
233 }
234
235 close(target->priv.pipefd[1]);
236 switch (target->type) {
237 case TARGET_BUFFER:
238 case TARGET_BUFFER_NULL_TERMINATED:
239 rv = process_target_output_buffer(target);
240 break;
241 case TARGET_CALLBACK:
242 rv = process_target_output_cb(target);
243 break;
244 default:
245 return -1;
246 }
247
248 close(target->priv.pipefd[0]);
249 return rv;
250}
251
252static bool_Bool contains_spaces(const char *s)
253{
254 for (size_t i = 0; s[i]; i++) {
255 if (isspace(s[i])((*__ctype_b_loc ())[(int) ((s[i]))] & (unsigned short int
) _ISspace)
)
256 return true1;
257 }
258 return false0;
259}
260
261static void subprocess_log_call(const char *const argv[])
262{
263 VB2_DEBUG("Run:")vb2ex_printf(__func__, "Run:");
264
265 for (size_t i = 0; argv[i]; i++) {
2
Assuming pointer value is null
3
Loop condition is false. Execution continues on line 271
266 if (contains_spaces(argv[i]))
267 VB2_DEBUG_RAW(" '%s'", argv[i])vb2ex_printf(((void*)0), " '%s'", argv[i]);
268 else
269 VB2_DEBUG_RAW(" %s", argv[i])vb2ex_printf(((void*)0), " %s", argv[i]);
270 }
271 VB2_DEBUG_RAW("\n")vb2ex_printf(((void*)0), "\n");
272}
273
274struct subprocess_target subprocess_null = {
275 .type = TARGET_NULL,
276};
277
278struct subprocess_target subprocess_stdin = {
279 .type = TARGET_FD,
280 .fd = STDIN_FILENO0,
281};
282
283struct subprocess_target subprocess_stdout = {
284 .type = TARGET_FD,
285 .fd = STDOUT_FILENO1,
286};
287
288struct subprocess_target subprocess_stderr = {
289 .type = TARGET_FD,
290 .fd = STDERR_FILENO2,
291};
292
293test_mockable__attribute__((weak))
294int subprocess_run(const char *const argv[],
295 struct subprocess_target *input,
296 struct subprocess_target *output,
297 struct subprocess_target *error)
298{
299 int status;
300 pid_t pid = -1;
301
302 subprocess_log_call(argv);
1
Calling 'subprocess_log_call'
4
Returning from 'subprocess_log_call'
303
304 if (!input)
5
Assuming 'input' is null
6
Taking true branch
305 input = &subprocess_stdin;
306 if (!output)
7
Assuming 'output' is non-null
8
Taking false branch
307 output = &subprocess_stdout;
308 if (!error)
9
Assuming 'error' is non-null
10
Taking false branch
309 error = &subprocess_stderr;
310
311 if (init_target_private(input) < 0)
11
Taking false branch
312 goto fail;
313 if (init_target_private(output) < 0)
12
Taking false branch
314 goto fail;
315 if (init_target_private(error) < 0)
13
Taking false branch
316 goto fail;
317
318 if ((pid = fork()) < 0)
14
Assuming the condition is false
15
Taking false branch
319 goto fail;
320 if (pid == 0) {
16
Assuming 'pid' is equal to 0
17
Taking true branch
321 /* Child process */
322 if (connect_process_target(input, STDIN_FILENO0) < 0)
18
Assuming the condition is false
19
Taking false branch
323 goto fail;
324 if (connect_process_target(output, STDOUT_FILENO1) < 0)
20
Assuming the condition is false
21
Taking false branch
325 goto fail;
326 if (connect_process_target(error, STDERR_FILENO2) < 0)
22
Assuming the condition is false
23
Taking false branch
327 goto fail;
328 execvp(*argv, (char *const *)argv);
24
Null pointer passed to 1st parameter expecting 'nonnull'
329 goto fail;
330 }
331
332 /* Parent process */
333 if (process_target_input(input) < 0)
334 goto fail;
335 if (process_target_output(output) < 0)
336 goto fail;
337 if (process_target_output(error) < 0)
338 goto fail;
339
340 if (waitpid(pid, &status, 0) < 0)
341 goto fail;
342
343 if (WIFEXITED(status)(((status) & 0x7f) == 0))
344 return WEXITSTATUS(status)(((status) & 0xff00) >> 8);
345
346 fail:
347 VB2_DEBUG("Failed to execute external command: %s\n", strerror(errno))vb2ex_printf(__func__, "Failed to execute external command: %s\n"
, strerror((*__errno_location ())))
;
348 if (pid == 0)
349 exit(127);
350 return -1;
351}