Bug Summary

File:src/usr.bin/top/commands.c
Warning:line 370, column 8
Assigned value is garbage or undefined

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 commands.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/top/obj -resource-dir /usr/local/lib/clang/13.0.0 -I . -internal-isystem /usr/local/lib/clang/13.0.0/include -internal-externc-isystem /usr/include -O2 -fdebug-compilation-dir=/usr/src/usr.bin/top/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/top/commands.c
1/* $OpenBSD: commands.c,v 1.33 2019/10/08 07:26:59 kn Exp $ */
2
3/*
4 * Top users/processes display for Unix
5 * Version 3
6 *
7 * Copyright (c) 1984, 1989, William LeFebvre, Rice University
8 * Copyright (c) 1989, 1990, 1992, William LeFebvre, Northwestern University
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
20 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
21 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
22 * IN NO EVENT SHALL THE AUTHOR OR HIS EMPLOYER BE LIABLE FOR ANY DIRECT, INDIRECT,
23 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
24 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
28 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */
30
31/*
32 * This file contains the routines that implement some of the interactive
33 * mode commands. Note that some of the commands are implemented in-line
34 * in "main". This is necessary because they change the global state of
35 * "top" (i.e.: changing the number of processes to display).
36 */
37
38#include <sys/types.h>
39#include <stdio.h>
40#include <err.h>
41#include <ctype.h>
42#include <errno(*__errno()).h>
43#include <stdlib.h>
44#include <string.h>
45#include <signal.h>
46#include <unistd.h>
47#include <sys/time.h>
48#include <sys/resource.h>
49#include <stdbool.h>
50
51#include "top.h"
52
53#include "utils.h"
54#include "machine.h"
55
56static char *next_field(char *);
57static int scan_arg(char *, int *, char *);
58static char *err_string(void);
59static size_t str_adderr(char *, size_t, int);
60static size_t str_addarg(char *, size_t, char *, int);
61static int err_compar(const void *, const void *);
62
63/*
64 * Utility routines that help with some of the commands.
65 */
66static char *
67next_field(char *str)
68{
69 char *spaces = " \t";
70 size_t span;
71
72 span = strcspn(str, spaces);
73 if (span == strlen(str))
74 return (NULL((void *)0));
75
76 str += span;
77 *str++ = '\0';
78
79 while (strcspn(str, spaces) == 0)
80 str++;
81
82 if (*str == '\0')
83 return (NULL((void *)0));
84
85 return(str);
86}
87
88/*
89 * Scan the renice or kill interactive arguments for data and/or errors.
90 */
91static int
92scan_arg(char *str, int *intp, char *nptr)
93{
94 int val = 0, bad_flag = 0;
95 char ch;
96
97 *nptr = '\0';
98
99 if (*str == '\0')
100 return (-1);
101
102 while ((ch = *str++) != '\0') {
103 if (isspace((unsigned char)ch))
104 break;
105 else if (!isdigit((unsigned char)ch))
106 bad_flag = 1;
107 else
108 val = val * 10 + (ch - '0');
109
110 *(nptr++) = ch;
111 }
112 *nptr = '\0';
113
114 if (bad_flag == 1)
115 return(-1);
116
117 *intp = val;
118 return (0);
119}
120
121/*
122 * Some of the commands make system calls that could generate errors.
123 * These errors are collected up in an array of structures for later
124 * contemplation and display. Such routines return a string containing an
125 * error message, or NULL if no errors occurred. The next few routines are
126 * for manipulating and displaying these errors. We need an upper limit on
127 * the number of errors, so we arbitrarily choose 20.
128 */
129
130#define ERRMAX20 20
131
132struct errs errs[ERRMAX20];
133int errcnt;
134static char *err_toomany = " too many errors occurred";
135static char *err_listem =
136 " Many errors occurred. Press `e' to display the list of errors.";
137
138/* These macros get used to reset and log the errors */
139#define ERR_RESETerrcnt = 0 errcnt = 0
140#define ERROR(p, e)if (errcnt >= 20) { return(err_toomany); } else { free(errs
[errcnt].arg); if ((errs[errcnt].arg = strdup(p)) == ((void *
)0)) err(1, "strdup"); errs[errcnt++].err = (e); }
\
141 if (errcnt >= ERRMAX20) { \
142 return(err_toomany); \
143 } else { \
144 free(errs[errcnt].arg); \
145 if ((errs[errcnt].arg = strdup(p)) == NULL((void *)0)) \
146 err(1, "strdup"); \
147 errs[errcnt++].err = (e); \
148 }
149
150#define STRMAX80 80
151
152/*
153 * err_string() - return an appropriate error string. This is what the
154 * command will return for displaying. If no errors were logged, then
155 * return NULL. The maximum length of the error string is defined by
156 * "STRMAX".
157 */
158static char *
159err_string(void)
160{
161 int cnt = 0, first = true1, currerr = -1;
162 static char string[STRMAX80];
163 struct errs *errp;
164
165 /* if there are no errors, return NULL */
166 if (errcnt == 0)
167 return (NULL((void *)0));
168
169 /* sort the errors */
170 qsort(errs, errcnt, sizeof(struct errs), err_compar);
171
172 /* need a space at the front of the error string */
173 string[0] = ' ';
174 string[1] = '\0';
175
176 /* loop thru the sorted list, building an error string */
177 while (cnt < errcnt) {
178 errp = &(errs[cnt++]);
179 if (errp->err != currerr) {
180 if (currerr != -1) {
181 if (str_adderr(string, sizeof string, currerr) >
182 sizeof string - 2)
183 return (err_listem);
184
185 /* we know there's more */
186 (void) strlcat(string, "; ", sizeof string);
187 }
188 currerr = errp->err;
189 first = true1;
190 }
191 if (str_addarg(string, sizeof string, errp->arg, first) >=
192 sizeof string)
193 return (err_listem);
194
195 first = false0;
196 }
197
198 /* add final message */
199 if (str_adderr(string, sizeof string, currerr) >= sizeof string)
200 return (err_listem);
201
202 /* return the error string */
203 return (string);
204}
205
206/*
207 * str_adderr(str, len, err) - add an explanation of error "err" to
208 * the string "str".
209 */
210static size_t
211str_adderr(char *str, size_t len, int err)
212{
213 size_t msglen;
214 char *msg;
215
216 msg = err == 0 ? "Not a number" : strerror(err);
217
218 if ((msglen = strlcat(str, ": ", len)) >= len)
219 return (msglen);
220
221 return (strlcat(str, msg, len));
222}
223
224/*
225 * str_addarg(str, len, arg, first) - add the string argument "arg" to
226 * the string "str". This is the first in the group when "first"
227 * is set (indicating that a comma should NOT be added to the front).
228 */
229static size_t
230str_addarg(char *str, size_t len, char *arg, int first)
231{
232 size_t msglen;
233
234 if (!first) {
235 if ((msglen = strlcat(str, ", ", len)) >= len)
236 return (msglen);
237 }
238 return (strlcat(str, arg, len));
239}
240
241/*
242 * err_compar(p1, p2) - comparison routine used by "qsort"
243 * for sorting errors.
244 */
245static int
246err_compar(const void *e1, const void *e2)
247{
248 const struct errs *p1 = (const struct errs *) e1;
249 const struct errs *p2 = (const struct errs *) e2;
250 int result;
251
252 if ((result = p1->err - p2->err) == 0)
253 return (strcmp(p1->arg, p2->arg));
254 return (result);
255}
256
257/*
258 * error_count() - return the number of errors currently logged.
259 */
260int
261error_count(void)
262{
263 return (errcnt);
264}
265
266/*
267 * kill_procs(str) - send signals to processes, much like the "kill"
268 * command does; invoked in response to 'k'.
269 */
270char *
271kill_procs(char *str)
272{
273 int signum = SIGTERM15, procnum;
274 uid_t uid, puid;
275 char tempbuf[TEMPBUFSIZE50];
276 char *nptr, *tmp;
277
278 tmp = tempbuf;
279
280 /* reset error array */
281 ERR_RESETerrcnt = 0;
282
283 /* remember our uid */
284 uid = getuid();
285
286 /* skip over leading white space */
287 while (isspace((unsigned char)*str))
288 str++;
289
290 if (*str == '-') {
291 str++;
292
293 /* explicit signal specified */
294 if ((nptr = next_field(str)) == NULL((void *)0))
295 return (" kill: no processes specified");
296
297 if (isdigit((unsigned char)*str)) {
298 (void) scan_arg(str, &signum, tmp);
299 if (signum <= 0 || signum >= NSIG33)
300 return (" invalid signal number");
301 } else {
302 /* translate the name into a number */
303 for (signum = 0; signum < NSIG33; signum++) {
304 if (strcasecmp(sys_signame[signum],
305 str) == 0)
306 break;
307 }
308
309 /* was it ever found */
310 if (signum == NSIG33)
311 return (" bad signal name");
312 }
313 /* put the new pointer in place */
314 str = nptr;
315 }
316 nptr = tempbuf;
317 /* loop thru the string, killing processes */
318 do {
319 if (scan_arg(str, &procnum, nptr) == -1) {
320 ERROR(nptr, 0)if (errcnt >= 20) { return(err_toomany); } else { free(errs
[errcnt].arg); if ((errs[errcnt].arg = strdup(nptr)) == ((void
*)0)) err(1, "strdup"); errs[errcnt++].err = (0); }
;
321 } else {
322 /* check process owner if we're not root */
323 puid = proc_owner(procnum);
324 if (puid == (uid_t)(-1)) {
325 ERROR(nptr, ESRCH)if (errcnt >= 20) { return(err_toomany); } else { free(errs
[errcnt].arg); if ((errs[errcnt].arg = strdup(nptr)) == ((void
*)0)) err(1, "strdup"); errs[errcnt++].err = (3); }
;
326 } else if (uid && (uid != puid)) {
327 ERROR(nptr, EACCES)if (errcnt >= 20) { return(err_toomany); } else { free(errs
[errcnt].arg); if ((errs[errcnt].arg = strdup(nptr)) == ((void
*)0)) err(1, "strdup"); errs[errcnt++].err = (13); }
;
328 } else if (kill(procnum, signum) == -1) {
329 ERROR(nptr, errno)if (errcnt >= 20) { return(err_toomany); } else { free(errs
[errcnt].arg); if ((errs[errcnt].arg = strdup(nptr)) == ((void
*)0)) err(1, "strdup"); errs[errcnt++].err = ((*__errno()));
}
;
330 }
331 }
332 } while ((str = next_field(str)) != NULL((void *)0));
333
334 /* return appropriate error string */
335 return (err_string());
336}
337
338/*
339 * renice_procs(str) - change the "nice" of processes, much like the
340 * "renice" command does; invoked in response to 'r'.
341 */
342char *
343renice_procs(char *str)
344{
345 uid_t uid;
346 char negate;
347 int prio, procnum;
348 char tempbuf[TEMPBUFSIZE50];
349 char *nptr;
350
351 ERR_RESETerrcnt = 0;
352 uid = getuid();
353
354 /* skip over leading white space */
355 while (isspace((unsigned char)*str))
1
Loop condition is false. Execution continues on line 359
356 str++;
357
358 /* allow for negative priority values */
359 if ((negate = (*str == '-')) != 0) {
2
Assuming the condition is true
3
Taking true branch
360 /* move past the minus sign */
361 str++;
362 }
363
364 nptr = tempbuf;
365 /* use procnum as a temporary holding place and get the number */
366 procnum = scan_arg(str, &prio, nptr);
367
368 /* negate if necessary */
369 if (negate
3.1
'negate' is 1
)
4
Taking true branch
370 prio = -prio;
5
Assigned value is garbage or undefined
371
372#if defined(PRIO_MIN(-20)) && defined(PRIO_MAX20)
373 /* check for validity */
374 if (procnum == -1 || prio < PRIO_MIN(-20) || prio > PRIO_MAX20)
375 return (" bad priority value");
376#endif
377
378 /* move to the first process number */
379 if ((str = next_field(str)) == NULL((void *)0))
380 return (" no processes specified");
381
382 /* loop thru the process numbers, renicing each one */
383 do {
384 if (scan_arg(str, &procnum, nptr) == -1) {
385 ERROR(nptr, 0)if (errcnt >= 20) { return(err_toomany); } else { free(errs
[errcnt].arg); if ((errs[errcnt].arg = strdup(nptr)) == ((void
*)0)) err(1, "strdup"); errs[errcnt++].err = (0); }
;
386 }
387 /* check process owner if we're not root */
388 else if (uid && (uid != proc_owner(procnum))) {
389 ERROR(nptr, EACCES)if (errcnt >= 20) { return(err_toomany); } else { free(errs
[errcnt].arg); if ((errs[errcnt].arg = strdup(nptr)) == ((void
*)0)) err(1, "strdup"); errs[errcnt++].err = (13); }
;
390 } else if (setpriority(PRIO_PROCESS0, procnum, prio) == -1) {
391 ERROR(nptr, errno)if (errcnt >= 20) { return(err_toomany); } else { free(errs
[errcnt].arg); if ((errs[errcnt].arg = strdup(nptr)) == ((void
*)0)) err(1, "strdup"); errs[errcnt++].err = ((*__errno()));
}
;
392 }
393 } while ((str = next_field(str)) != NULL((void *)0));
394
395 /* return appropriate error string */
396 return (err_string());
397}