Bug Summary

File:src/usr.bin/bgplg/bgplg/../bgplg.c
Warning:line 140, column 7
Array access (from variable 'arg') results in a null pointer dereference

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple amd64-unknown-openbsd7.0 -analyze -disable-free -disable-llvm-verifier -discard-value-names -main-file-name bgplg.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 1 -pic-is-pie -mframe-pointer=all -relaxed-aliasing -fno-rounding-math -mconstructor-aliases -munwind-tables -target-cpu x86-64 -target-feature +retpoline-indirect-calls -target-feature +retpoline-indirect-branches -tune-cpu generic -debugger-tuning=gdb -fcoverage-compilation-dir=/usr/src/usr.bin/bgplg/bgplg/obj -resource-dir /usr/local/lib/clang/13.0.0 -I /usr/src/usr.bin/bgplg/bgplg -internal-isystem /usr/local/lib/clang/13.0.0/include -internal-externc-isystem /usr/include -O2 -fdebug-compilation-dir=/usr/src/usr.bin/bgplg/bgplg/obj -ferror-limit 19 -fwrapv -D_RET_PROTECTOR -ret-protector -fgnuc-version=4.2.1 -vectorize-loops -vectorize-slp -fno-builtin-malloc -fno-builtin-calloc -fno-builtin-realloc -fno-builtin-valloc -fno-builtin-free -fno-builtin-strdup -fno-builtin-strndup -analyzer-output=html -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /home/ben/Projects/vmm/scan-build/2022-01-12-194120-40624-1 -x c /usr/src/usr.bin/bgplg/bgplg/../bgplg.c
1/* $OpenBSD: bgplg.c,v 1.19 2018/03/05 10:53:37 denis Exp $ */
2
3/*
4 * Copyright (c) 2005, 2006 Reyk Floeter <reyk@openbsd.org>
5 *
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18
19#include <sys/types.h>
20#include <sys/stat.h>
21
22#include <stdio.h>
23#include <stdlib.h>
24#include <signal.h>
25#include <string.h>
26#include <unistd.h>
27#include <limits.h>
28#include <ctype.h>
29#include <errno(*__errno()).h>
30#include <fcntl.h>
31#include <err.h>
32
33#include "bgplg.h"
34
35#define INC_STYLE"/conf/bgplg.css" "/conf/bgplg.css"
36#define INC_HEAD"/conf/bgplg.head" "/conf/bgplg.head"
37#define INC_FOOT"/conf/bgplg.foot" "/conf/bgplg.foot"
38
39#define BGPDSOCK"/run/bgpd.rsock" "/run/bgpd.rsock"
40#define BGPCTL"/bin/bgpctl", "-s", "/run/bgpd.rsock" "/bin/bgpctl", "-s", BGPDSOCK"/run/bgpd.rsock"
41#define PING"/bin/ping" "/bin/ping"
42#define TRACEROUTE"/bin/traceroute" "/bin/traceroute"
43#define PING6"/bin/ping6" "/bin/ping6"
44#define TRACEROUTE6"/bin/traceroute6" "/bin/traceroute6"
45#define CONTENT_TYPE"text/html" "text/html"
46
47static struct cmd cmds[] = CMDS{ { "show ip bgp", 1, 1, "&lt;prefix&gt;", { "/bin/bgpctl"
, "-s", "/run/bgpd.rsock", "show", "ip", "bgp", ((void *)0) }
}, { "show ip bgp as", 1, 1, "&lt;asnum&gt;", { "/bin/bgpctl"
, "-s", "/run/bgpd.rsock", "show", "ip", "bgp", "as", ((void *
)0) } }, { "show ip bgp source-as", 1, 1, "&lt;asnum&gt;"
, { "/bin/bgpctl", "-s", "/run/bgpd.rsock", "show", "ip", "bgp"
, "source-as", ((void *)0) } }, { "show ip bgp transit-as", 1
, 1, "&lt;asnum&gt;", { "/bin/bgpctl", "-s", "/run/bgpd.rsock"
, "show", "ip", "bgp", "transit-as", ((void *)0) } }, { "show ip bgp peer-as"
, 1, 1, "&lt;asnum&gt;", { "/bin/bgpctl", "-s", "/run/bgpd.rsock"
, "show", "ip", "bgp", "peer-as", ((void *)0) } }, { "show ip bgp empty-as"
, 0, 0, ((void *)0), { "/bin/bgpctl", "-s", "/run/bgpd.rsock"
, "show", "ip", "bgp", "empty-as", ((void *)0) } }, { "show ip bgp summary"
, 0, 0, ((void *)0), { "/bin/bgpctl", "-s", "/run/bgpd.rsock"
, "show", "ip", "bgp", "summary", ((void *)0) } }, { "show ip bgp community"
, 1, 1, "&lt;community&gt;", { "/bin/bgpctl", "-s", "/run/bgpd.rsock"
, "show","ip", "bgp", "community", ((void *)0) } }, { "show ip bgp detail community"
, 1, 1, "&lt;community&gt;", { "/bin/bgpctl", "-s", "/run/bgpd.rsock"
, "show","ip", "bgp", "detail", "community", ((void *)0) } },
{ "show ip bgp ext-community", 2, 2, "&lt;ext-community&gt;"
, { "/bin/bgpctl", "-s", "/run/bgpd.rsock", "show","ip", "bgp"
, "ext-community", ((void *)0) } }, { "show ip bgp detail ext-community"
, 2, 2, "&lt;ext-community&gt;", { "/bin/bgpctl", "-s"
, "/run/bgpd.rsock", "show","ip", "bgp", "detail", "ext-community"
, ((void *)0) } }, { "show ip bgp large-community", 1, 1, "&lt;large-community&gt;"
, { "/bin/bgpctl", "-s", "/run/bgpd.rsock", "show","ip", "bgp"
, "large-community", ((void *)0) } }, { "show ip bgp detail large-community"
, 1, 1, "&lt;large-community&gt;", { "/bin/bgpctl", "-s"
, "/run/bgpd.rsock", "show","ip", "bgp", "detail", "large-community"
, ((void *)0) } }, { "show ip bgp detail", 1, 1, "&lt;prefix&gt;"
, { "/bin/bgpctl", "-s", "/run/bgpd.rsock", "show","ip", "bgp"
, "detail", ((void *)0) } }, { "show ip bgp detail as", 1, 1,
"&lt;asnum&gt;", { "/bin/bgpctl", "-s", "/run/bgpd.rsock"
, "show","ip", "bgp", "detail", "as", ((void *)0) } }, { "show ip bgp in"
, 1, 1, "&lt;neighbor&gt;", { "/bin/bgpctl", "-s", "/run/bgpd.rsock"
, "show","ip", "bgp", "in", "neighbor", ((void *)0) } }, { "show ip bgp out"
, 1, 1, "&lt;neighbor&gt;", { "/bin/bgpctl", "-s", "/run/bgpd.rsock"
, "show","ip", "bgp", "out", "neighbor", ((void *)0) } }, { "show ip bgp ovs"
, 1, 1, "&lt;state&gt;", { "/bin/bgpctl", "-s", "/run/bgpd.rsock"
, "show","ip", "bgp", "ovs", ((void *)0) } }, { "show ip bgp memory"
, 0, 0, ((void *)0), { "/bin/bgpctl", "-s", "/run/bgpd.rsock"
, "show", "ip", "bgp", "memory", ((void *)0) } }, { "show neighbor"
, 0, 1, ((void *)0), { "/bin/bgpctl", "-s", "/run/bgpd.rsock"
, "show", "neighbor", ((void *)0) } }, { "show nexthop", 0, 0
, ((void *)0), { "/bin/bgpctl", "-s", "/run/bgpd.rsock", "show"
, "nexthop", ((void *)0) } }, { "traceroute", 1, 1, "&lt;address&gt;"
, { "/bin/traceroute", "-ASl", ((void *)0) } }, { "ping", 1, 1
, "&lt;address&gt;", { "/bin/ping", "-c4", "-w2", ((void
*)0) } }, { "traceroute6", 1, 1, "&lt;address&gt;", {
"/bin/traceroute6", "-Al", ((void *)0) } }, { "ping6", 1, 1,
"&lt;address&gt;", { "/bin/ping6", "-c4", "-i2", ((void
*)0) } }, { "help", 0, 0, ((void *)0), { ((void *)0) }, lg_help
}, { ((void *)0) } }
;
48
49char *lg_getenv(const char *, int *);
50void lg_urldecode(char *);
51char **lg_arg2argv(char *, int *);
52char **lg_argextra(char **, int, struct cmd *);
53char *lg_getarg(const char *, char *, int);
54int lg_incl(const char *);
55
56void
57lg_urldecode(char *str)
58{
59 size_t i, c, len;
60 char code[3];
61 long result;
62
63 if (str && *str) {
64 len = strlen(str);
65 i = c = 0;
66 while (i < len) {
67 if (str[i] == '%' && i <= (len - 2)) {
68 if (isxdigit((unsigned char)str[i + 1]) &&
69 isxdigit((unsigned char)str[i + 2])) {
70 code[0] = str[i + 1];
71 code[1] = str[i + 2];
72 code[2] = 0;
73 result = strtol(code, NULL((void *)0), 16);
74 /* Replace NUL chars with a space */
75 if (result == 0)
76 result = ' ';
77 str[c++] = result;
78 i += 3;
79 } else {
80 str[c++] = '%';
81 i++;
82 }
83 } else if (str[i] == '+') {
84 str[i] = ' ';
85 } else {
86 if (c != i)
87 str[c] = str[i];
88 c++;
89 i++;
90 }
91 }
92 str[c] = 0x0;
93 }
94}
95
96char *
97lg_getenv(const char *name, int *lenp)
98{
99 size_t len;
100 u_int i;
101 char *ptr;
102
103 if ((ptr = getenv(name)) == NULL((void *)0))
104 return (NULL((void *)0));
105
106 lg_urldecode(ptr);
107
108 if (!(len = strlen(ptr)))
109 return (NULL((void *)0));
110
111 if (lenp != NULL((void *)0))
112 *lenp = len;
113
114#define allowed_in_string(_x) \
115 (isalnum((unsigned char)_x) || strchr("-_.:/= ", _x))
116
117 for (i = 0; i < len; i++) {
118 if (ptr[i] == '&')
119 ptr[i] = '\0';
120 if (!allowed_in_string(ptr[i])) {
121 printf("invalid character in input\n");
122 return (NULL((void *)0));
123 }
124 }
125
126 return (ptr);
127#undef allowed_in_string
128}
129
130char *
131lg_getarg(const char *name, char *arg, int len)
132{
133 char *ptr = arg;
134 size_t namelen, ptrlen;
135 int i;
136
137 namelen = strlen(name);
138
139 for (i = 0; i < len; i++) {
17
Assuming 'i' is < 'len'
18
Loop condition is true. Entering loop body
140 if (arg[i] == '\0')
19
Array access (from variable 'arg') results in a null pointer dereference
141 continue;
142 ptr = arg + i;
143 ptrlen = strlen(ptr);
144 if (namelen >= ptrlen)
145 continue;
146 if (strncmp(name, ptr, namelen) == 0)
147 return (ptr + namelen);
148 }
149
150 return (NULL((void *)0));
151}
152
153char **
154lg_arg2argv(char *arg, int *argc)
155{
156 char **argv, *ptr = arg;
157 size_t len;
158 u_int i, c = 1;
159
160 len = strlen(arg);
161
162 /* Count elements */
163 for (i = 0; i < len; i++) {
164 if (isspace((unsigned char)arg[i])) {
165 /* filter out additional options */
166 if (arg[i + 1] == '-') {
167 printf("invalid input\n");
168 return (NULL((void *)0));
169 }
170 arg[i] = '\0';
171 c++;
172 }
173 }
174
175 /* Generate array */
176 if ((argv = calloc(c + 1, sizeof(char *))) == NULL((void *)0)) {
177 printf("fatal error: %s\n", strerror(errno(*__errno())));
178 return (NULL((void *)0));
179 }
180
181 argv[c] = NULL((void *)0);
182 *argc = c;
183
184 /* Fill array */
185 for (i = c = 0; i < len; i++) {
186 if (arg[i] == '\0' || i == 0) {
187 if (i != 0)
188 ptr = &arg[i + 1];
189 argv[c++] = ptr;
190 }
191 }
192
193 return (argv);
194}
195
196char **
197lg_argextra(char **argv, int argc, struct cmd *cmdp)
198{
199 char **new_argv;
200 int i, c = 0;
201
202 /* Count elements */
203 for (i = 0; cmdp->earg[i] != NULL((void *)0); i++)
204 c++;
205
206 /* Generate array */
207 if ((new_argv = calloc(c + argc + 1, sizeof(char *))) == NULL((void *)0)) {
208 printf("fatal error: %s\n", strerror(errno(*__errno())));
209 return (NULL((void *)0));
210 }
211
212 /* Fill array */
213 for (i = c = 0; cmdp->earg[i] != NULL((void *)0); i++)
214 new_argv[c++] = cmdp->earg[i];
215
216 /* Append old array */
217 for (i = 0; i < argc; i++)
218 new_argv[c++] = argv[i];
219
220 new_argv[c] = NULL((void *)0);
221
222 free(argv);
223
224 return (new_argv);
225}
226
227int
228lg_incl(const char *file)
229{
230 char buf[BUFSIZ1024];
231 int fd, len;
232
233 if ((fd = open(file, O_RDONLY0x0000)) == -1)
234 return (errno(*__errno()));
235
236 do {
237 len = read(fd, buf, sizeof(buf));
238 fwrite(buf, len, 1, stdout(&__sF[1]));
239 } while(len == BUFSIZ1024);
240
241 close(fd);
242 return (0);
243}
244
245int
246main(void)
247{
248 char *query, *myname, *self, *cmd = NULL((void *)0), *req;
249 char **argv = NULL((void *)0);
250 int ret = 1, argc = 0, query_length = 0;
251 struct stat st;
252 u_int i;
253 struct cmd *cmdp = NULL((void *)0);
254
255 if (pledge("stdio rpath proc exec", NULL((void *)0)) == -1)
1
Assuming the condition is false
2
Taking false branch
256 err(1, "pledge");
257
258 if ((myname = lg_getenv("SERVER_NAME", NULL((void *)0))) == NULL((void *)0))
3
Assuming the condition is false
4
Taking false branch
259 return (1);
260
261 printf("Content-Type: %s\n"
262 "Cache-Control: no-cache\n\n"
263 "<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>\n"
264 "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.1//EN\" "
265 "\"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd\">\n"
266 "<html xmlns=\"http://www.w3.org/1999/xhtml\">\n"
267 "<head>\n"
268 "<title>%s</title>\n",
269 CONTENT_TYPE"text/html", myname);
270 if (stat(INC_STYLE"/conf/bgplg.css", &st) == 0) {
5
Assuming the condition is false
6
Taking false branch
271 printf("<style type='text/css'><!--\n");
272 lg_incl(INC_STYLE"/conf/bgplg.css");
273 printf("--></style>\n");
274 }
275 if (stat(INC_HEAD"/conf/bgplg.head", &st) != 0 || lg_incl(INC_HEAD"/conf/bgplg.head") != 0) {
7
Assuming the condition is true
276 printf("</head>\n"
277 "<body>\n");
278 }
279
280 /* print a form with possible options */
281 if ((self = lg_getenv("SCRIPT_NAME", NULL((void *)0))) == NULL((void *)0)) {
8
Assuming the condition is false
9
Taking false branch
282 printf("fatal error: invalid request\n");
283 goto err;
284 }
285 if ((query = lg_getenv("QUERY_STRING", &query_length)) != NULL((void *)0))
10
Value assigned to 'query'
11
Assuming pointer value is null
12
Taking false branch
286 cmd = lg_getarg("cmd=", query, query_length);
287 printf(
288 "<form action='%s'>\n"
289 "<div class=\"command\">\n"
290 "<select name='cmd'>\n",
291 self);
292 for (i = 0; cmds[i].name != NULL((void *)0); i++) {
13
Assuming field 'name' is equal to NULL
14
Loop condition is false. Execution continues on line 305
293 if (!lg_checkperm(&cmds[i]))
294 continue;
295
296 if (cmd != NULL((void *)0) && strcmp(cmd, cmds[i].name) == 0)
297 printf("<option value='%s' selected='selected'>%s"
298 "</option>\n",
299 cmds[i].name, cmds[i].name);
300 else
301 printf("<option value='%s'>%s</option>\n",
302 cmds[i].name, cmds[i].name);
303 }
304
305 if ((req = lg_getarg("req=", query, query_length)) != NULL((void *)0)) {
15
Passing null pointer value via 2nd parameter 'arg'
16
Calling 'lg_getarg'
306 /* Could be NULL */
307 argv = lg_arg2argv(req, &argc);
308 }
309
310 printf("</select>\n"
311 "<input type='text' value='%s' name='req'/>\n"
312 "<input type='submit' value='submit'/>\n"
313 "</div>\n"
314 "</form>\n"
315 "<pre>\n", req ? req : "");
316 fflush(stdout(&__sF[1]));
317
318#ifdef DEBUG
319 if (close(2) == -1 || dup2(1, 2) == -1)
320#else
321 if (close(2) == -1)
322#endif
323 {
324 printf("fatal error: %s\n", strerror(errno(*__errno())));
325 goto err;
326 }
327
328 if (query == NULL((void *)0))
329 goto err;
330 if (cmd == NULL((void *)0)) {
331 printf("unspecified command\n");
332 goto err;
333 }
334
335 for (i = 0; cmds[i].name != NULL((void *)0); i++) {
336 if (strcmp(cmd, cmds[i].name) == 0) {
337 cmdp = &cmds[i];
338 break;
339 }
340 }
341
342 if (cmdp == NULL((void *)0)) {
343 printf("invalid command: %s\n", cmd);
344 goto err;
345 }
346 if (argc > cmdp->maxargs) {
347 printf("superfluous argument(s): %s %s\n",
348 cmd, cmdp->args ? cmdp->args : "");
349 goto err;
350 }
351 if (argc < cmdp->minargs) {
352 printf("missing argument(s): %s %s\n", cmd, cmdp->args);
353 goto err;
354 }
355
356 if (cmdp->func != NULL((void *)0)) {
357 ret = cmdp->func(cmds, argv);
358 } else {
359 if ((argv = lg_argextra(argv, argc, cmdp)) == NULL((void *)0))
360 goto err;
361 ret = lg_exec(cmdp->earg[0], argv);
362 }
363 if (ret != 0)
364 printf("\nfailed%s\n", ret == 127 ? ": file not found" : ".");
365 else
366 printf("\nsuccess.\n");
367
368 err:
369 fflush(stdout(&__sF[1]));
370
371 free(argv);
372
373 printf("</pre>\n");
374
375 if (stat(INC_FOOT"/conf/bgplg.foot", &st) != 0 || lg_incl(INC_FOOT"/conf/bgplg.foot") != 0)
376 printf("<hr/>\n");
377
378 printf("<div class='footer'>\n"
379 "</div>\n"
380 "</body>\n"
381 "</html>\n");
382
383 return (ret);
384}