Bug Summary

File:src/usr.sbin/sa/main.c
Warning:line 329, column 17
The left expression of the compound assignment is an uninitialized value. The computed value will also be garbage

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 main.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.sbin/sa/obj -resource-dir /usr/local/lib/clang/13.0.0 -internal-isystem /usr/local/lib/clang/13.0.0/include -internal-externc-isystem /usr/include -O2 -fdebug-compilation-dir=/usr/src/usr.sbin/sa/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.sbin/sa/main.c
1/* $OpenBSD: main.c,v 1.17 2021/10/24 21:24:19 deraadt Exp $ */
2/*
3 * Copyright (c) 1994 Christopher G. Demetriou
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * 3. All advertising materials mentioning features or use of this software
15 * must display the following acknowledgement:
16 * This product includes software developed by Christopher G. Demetriou.
17 * 4. The name of the author may not be used to endorse or promote products
18 * derived from this software without specific prior written permission
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
21 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
22 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
23 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
24 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
25 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
29 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 */
31
32/*
33 * sa: system accounting
34 */
35
36#include <sys/types.h>
37#include <sys/acct.h>
38#include <ctype.h>
39#include <err.h>
40#include <fcntl.h>
41#include <signal.h>
42#include <stdio.h>
43#include <stdlib.h>
44#include <string.h>
45#include <unistd.h>
46#include "extern.h"
47#include "pathnames.h"
48
49static int acct_load(char *, int);
50static uint64_t decode_comp_t(comp_t);
51static int cmp_comm(const char *, const char *);
52static int cmp_usrsys(const DBT *, const DBT *);
53static int cmp_avgusrsys(const DBT *, const DBT *);
54static int cmp_dkio(const DBT *, const DBT *);
55static int cmp_avgdkio(const DBT *, const DBT *);
56static int cmp_cpumem(const DBT *, const DBT *);
57static int cmp_avgcpumem(const DBT *, const DBT *);
58static int cmp_calls(const DBT *, const DBT *);
59
60int aflag, bflag, cflag, dflag, Dflag, fflag, iflag, jflag, kflag;
61int Kflag, lflag, mflag, qflag, rflag, sflag, tflag, uflag, vflag;
62int cutoff = 1;
63
64static char *dfltargv[] = { _PATH_ACCT"/var/account/acct" };
65static int dfltargc = (sizeof(dfltargv)/sizeof(char *));
66
67/* default to comparing by sum of user + system time */
68cmpf_t sa_cmp = cmp_usrsys;
69
70int
71main(int argc, char **argv)
72{
73 int ch;
74 int error = 0;
75 const char *errstr;
76 extern char *__progname;
77
78 if (pledge("stdio rpath wpath cpath getpw flock", NULL((void *)0)) == -1)
1
Assuming the condition is false
2
Taking false branch
79 err(1, "pledge");
80
81 while ((ch = getopt(argc, argv, "abcdDfijkKlmnqrstuv:")) != -1)
3
Assuming the condition is false
4
Loop condition is false. Execution continues on line 175
82 switch (ch) {
83 case 'a':
84 /* print all commands */
85 aflag = 1;
86 break;
87 case 'b':
88 /* sort by per-call user/system time average */
89 bflag = 1;
90 sa_cmp = cmp_avgusrsys;
91 break;
92 case 'c':
93 /* print percentage total time */
94 cflag = 1;
95 break;
96 case 'd':
97 /* sort by averge number of disk I/O ops */
98 dflag = 1;
99 sa_cmp = cmp_avgdkio;
100 break;
101 case 'D':
102 /* print and sort by total disk I/O ops */
103 Dflag = 1;
104 sa_cmp = cmp_dkio;
105 break;
106 case 'f':
107 /* force no interactive threshold comprison */
108 fflag = 1;
109 break;
110 case 'i':
111 /* do not read in summary file */
112 iflag = 1;
113 break;
114 case 'j':
115 /* instead of total minutes, give sec/call */
116 jflag = 1;
117 break;
118 case 'k':
119 /* sort by cpu-time average memory usage */
120 kflag = 1;
121 sa_cmp = cmp_avgcpumem;
122 break;
123 case 'K':
124 /* print and sort by cpu-storage integral */
125 sa_cmp = cmp_cpumem;
126 Kflag = 1;
127 break;
128 case 'l':
129 /* separate system and user time */
130 lflag = 1;
131 break;
132 case 'm':
133 /* print procs and time per-user */
134 mflag = 1;
135 break;
136 case 'n':
137 /* sort by number of calls */
138 sa_cmp = cmp_calls;
139 break;
140 case 'q':
141 /* quiet; error messages only */
142 qflag = 1;
143 break;
144 case 'r':
145 /* reverse order of sort */
146 rflag = 1;
147 break;
148 case 's':
149 /* merge accounting file into summaries */
150 sflag = 1;
151 break;
152 case 't':
153 /* report ratio of user and system times */
154 tflag = 1;
155 break;
156 case 'u':
157 /* first, print uid and command name */
158 uflag = 1;
159 break;
160 case 'v':
161 /* cull junk */
162 vflag = 1;
163 cutoff = strtonum(optarg, 1, INT_MAX2147483647, &errstr);
164 if (errstr)
165 errx(1, "-v %s: %s", optarg, errstr);
166 break;
167 case '?':
168 default:
169 (void)fprintf(stderr(&__sF[2]),
170 "usage: %s [-abcDdfijKklmnqrstu] [-v cutoff]"
171 " [file ...]\n", __progname);
172 exit(1);
173 }
174
175 argc -= optind;
176 argv += optind;
177
178 /* various argument checking */
179 if (fflag && !vflag)
5
Assuming 'fflag' is 0
180 errx(1, "only one of -f requires -v");
181 if (fflag
5.1
'fflag' is 0
&& aflag)
182 errx(1, "only one of -a and -v may be specified");
183 /* XXX need more argument checking */
184
185 if (!uflag) {
6
Assuming 'uflag' is not equal to 0
7
Taking false branch
186 /* initialize tables */
187 if ((sflag || (!mflag && !qflag)) && pacct_init() != 0)
188 errx(1, "process accounting initialization failed");
189 if ((sflag || (mflag && !qflag)) && usracct_init() != 0)
190 errx(1, "user accounting initialization failed");
191 }
192
193 if (argc == 0) {
8
Assuming 'argc' is not equal to 0
9
Taking false branch
194 argc = dfltargc;
195 argv = dfltargv;
196 }
197
198 /* for each file specified */
199 for (; argc > 0; argc--, argv++) {
10
Assuming 'argc' is > 0
11
Loop condition is true. Entering loop body
200 int fd;
201
202 /*
203 * load the accounting data from the file.
204 * if it fails, go on to the next file.
205 */
206 fd = acct_load(argv[0], sflag);
12
Calling 'acct_load'
207 if (fd < 0)
208 continue;
209
210 if (!uflag && sflag) {
211#ifndef DEBUG
212 sigset_t nmask, omask;
213 int unmask = 1;
214
215 /*
216 * block most signals so we aren't interrupted during
217 * the update.
218 */
219 if (sigfillset(&nmask) == -1) {
220 warn("sigfillset");
221 unmask = 0;
222 error = 1;
223 }
224 if (unmask &&
225 (sigprocmask(SIG_BLOCK1, &nmask, &omask) == -1)) {
226 warn("couldn't set signal mask ");
227 unmask = 0;
228 error = 1;
229 }
230#endif /* DEBUG */
231
232 /*
233 * truncate the accounting data file ASAP, to avoid
234 * losing data. don't worry about errors in updating
235 * the saved stats; better to underbill than overbill,
236 * but we want every accounting record intact.
237 */
238 if (ftruncate(fd, 0) == -1) {
239 warn("couldn't truncate %s", *argv);
240 error = 1;
241 }
242
243 /*
244 * update saved user and process accounting data.
245 * note errors for later.
246 */
247 if (pacct_update() != 0 || usracct_update() != 0)
248 error = 1;
249
250#ifndef DEBUG
251 /*
252 * restore signals
253 */
254 if (unmask &&
255 (sigprocmask(SIG_SETMASK3, &omask, NULL((void *)0)) == -1)) {
256 warn("couldn't restore signal mask");
257 error = 1;
258 }
259#endif /* DEBUG */
260 }
261
262 /*
263 * close the opened accounting file
264 */
265 if (close(fd) == -1) {
266 warn("close %s", *argv);
267 error = 1;
268 }
269 }
270
271 if (!uflag && !qflag) {
272 /* print any results we may have obtained. */
273 if (!mflag)
274 pacct_print();
275 else
276 usracct_print();
277 }
278
279 if (!uflag) {
280 /* finally, deallocate databases */
281 if (sflag || (!mflag && !qflag))
282 pacct_destroy();
283 if (sflag || (mflag && !qflag))
284 usracct_destroy();
285 }
286
287 exit(error);
288}
289
290static int
291acct_load(char *pn, int wr)
292{
293 struct acct ac;
294 struct cmdinfo ci;
295 ssize_t rv;
296 int fd, i;
297
298 /*
299 * open the file
300 */
301 fd = open(pn, wr ? O_RDWR0x0002 : O_RDONLY0x0000);
13
Assuming 'wr' is 0
14
'?' condition is false
302 if (fd == -1) {
15
Assuming the condition is false
16
Taking false branch
303 warn("open %s %s", pn, wr ? "for read/write" : "read-only");
304 return (-1);
305 }
306
307 /*
308 * read all we can; don't stat and open because more processes
309 * could exit, and we'd miss them
310 */
311 while (1) {
17
Loop condition is true. Entering loop body
312 /* get one accounting entry and punt if there's an error */
313 rv = read(fd, &ac, sizeof(struct acct));
314 if (rv == -1)
18
Assuming the condition is false
19
Taking false branch
315 warn("error reading %s", pn);
316 else if (rv > 0 && rv < sizeof(struct acct))
20
Assuming 'rv' is > 0
21
Assuming the condition is false
22
Taking false branch
317 warnx("short read of accounting data in %s", pn);
318 if (rv != sizeof(struct acct))
23
Taking false branch
319 break;
320
321 /* decode it */
322 ci.ci_calls = 1;
323 for (i = 0; i < sizeof(ac.ac_comm) && ac.ac_comm[i] != '\0';
24
Assuming the condition is true
25
Loop condition is true. Entering loop body
324 i++) {
325 unsigned char c = ac.ac_comm[i];
326
327 if (!isascii(c) || iscntrl(c)) {
328 ci.ci_comm[i] = '?';
329 ci.ci_flags |= CI_UNPRINTABLE0x0001;
26
The left expression of the compound assignment is an uninitialized value. The computed value will also be garbage
330 } else
331 ci.ci_comm[i] = c;
332 }
333 if (ac.ac_flag & AFORK0x01)
334 ci.ci_comm[i++] = '*';
335 ci.ci_comm[i++] = '\0';
336 ci.ci_etime = decode_comp_t(ac.ac_etime);
337 ci.ci_utime = decode_comp_t(ac.ac_utime);
338 ci.ci_stime = decode_comp_t(ac.ac_stime);
339 ci.ci_uid = ac.ac_uid;
340 ci.ci_mem = ac.ac_mem;
341 ci.ci_io = decode_comp_t(ac.ac_io) / AHZ64;
342
343 if (!uflag) {
344 /* and enter it into the usracct and pacct databases */
345 if (sflag || (!mflag && !qflag))
346 pacct_add(&ci);
347 if (sflag || (mflag && !qflag))
348 usracct_add(&ci);
349 } else if (!qflag)
350 printf("%6u %12.2f cpu %12lluk mem %12llu io %s\n",
351 ci.ci_uid,
352 (ci.ci_utime + ci.ci_stime) / (double) AHZ64,
353 ci.ci_mem, ci.ci_io, ci.ci_comm);
354 }
355
356 /* finally, return the file descriptor for possible truncation */
357 return (fd);
358}
359
360static uint64_t
361decode_comp_t(comp_t comp)
362{
363 uint64_t rv;
364
365 /*
366 * for more info on the comp_t format, see:
367 * /usr/src/sys/kern/kern_acct.c
368 * /usr/src/sys/sys/acct.h
369 * /usr/src/usr.bin/lastcomm/lastcomm.c
370 */
371 rv = comp & 0x1fff; /* 13 bit fraction */
372 comp >>= 13; /* 3 bit base-8 exponent */
373 while (comp--)
374 rv <<= 3;
375
376 return (rv);
377}
378
379/* sort commands, doing the right thing in terms of reversals */
380static int
381cmp_comm(const char *s1, const char *s2)
382{
383 int rv;
384
385 rv = strcmp(s1, s2);
386 if (rv == 0)
387 rv = -1;
388 return (rflag ? rv : -rv);
389}
390
391/* sort by total user and system time */
392static int
393cmp_usrsys(const DBT *d1, const DBT *d2)
394{
395 struct cmdinfo c1, c2;
396 uint64_t t1, t2;
397
398 memcpy(&c1, d1->data, sizeof(c1));
399 memcpy(&c2, d2->data, sizeof(c2));
400
401 t1 = c1.ci_utime + c1.ci_stime;
402 t2 = c2.ci_utime + c2.ci_stime;
403
404 if (t1 < t2)
405 return -1;
406 else if (t1 == t2)
407 return (cmp_comm(c1.ci_comm, c2.ci_comm));
408 else
409 return 1;
410}
411
412/* sort by average user and system time */
413static int
414cmp_avgusrsys(const DBT *d1, const DBT *d2)
415{
416 struct cmdinfo c1, c2;
417 double t1, t2;
418
419 memcpy(&c1, d1->data, sizeof(c1));
420 memcpy(&c2, d2->data, sizeof(c2));
421
422 t1 = c1.ci_utime + c1.ci_stime;
423 t1 /= (double) (c1.ci_calls ? c1.ci_calls : 1);
424
425 t2 = c2.ci_utime + c2.ci_stime;
426 t2 /= (double) (c2.ci_calls ? c2.ci_calls : 1);
427
428 if (t1 < t2)
429 return -1;
430 else if (t1 == t2)
431 return (cmp_comm(c1.ci_comm, c2.ci_comm));
432 else
433 return 1;
434}
435
436/* sort by total number of disk I/O operations */
437static int
438cmp_dkio(const DBT *d1, const DBT *d2)
439{
440 struct cmdinfo c1, c2;
441
442 memcpy(&c1, d1->data, sizeof(c1));
443 memcpy(&c2, d2->data, sizeof(c2));
444
445 if (c1.ci_io < c2.ci_io)
446 return -1;
447 else if (c1.ci_io == c2.ci_io)
448 return (cmp_comm(c1.ci_comm, c2.ci_comm));
449 else
450 return 1;
451}
452
453/* sort by average number of disk I/O operations */
454static int
455cmp_avgdkio(const DBT *d1, const DBT *d2)
456{
457 struct cmdinfo c1, c2;
458 double n1, n2;
459
460 memcpy(&c1, d1->data, sizeof(c1));
461 memcpy(&c2, d2->data, sizeof(c2));
462
463 n1 = (double) c1.ci_io / (double) (c1.ci_calls ? c1.ci_calls : 1);
464 n2 = (double) c2.ci_io / (double) (c2.ci_calls ? c2.ci_calls : 1);
465
466 if (n1 < n2)
467 return -1;
468 else if (n1 == n2)
469 return (cmp_comm(c1.ci_comm, c2.ci_comm));
470 else
471 return 1;
472}
473
474/* sort by the cpu-storage integral */
475static int
476cmp_cpumem(const DBT *d1, const DBT *d2)
477{
478 struct cmdinfo c1, c2;
479
480 memcpy(&c1, d1->data, sizeof(c1));
481 memcpy(&c2, d2->data, sizeof(c2));
482
483 if (c1.ci_mem < c2.ci_mem)
484 return -1;
485 else if (c1.ci_mem == c2.ci_mem)
486 return (cmp_comm(c1.ci_comm, c2.ci_comm));
487 else
488 return 1;
489}
490
491/* sort by the cpu-time average memory usage */
492static int
493cmp_avgcpumem(const DBT *d1, const DBT *d2)
494{
495 struct cmdinfo c1, c2;
496 uint64_t t1, t2;
497 double n1, n2;
498
499 memcpy(&c1, d1->data, sizeof(c1));
500 memcpy(&c2, d2->data, sizeof(c2));
501
502 t1 = c1.ci_utime + c1.ci_stime;
503 t2 = c2.ci_utime + c2.ci_stime;
504
505 n1 = (double) c1.ci_mem / (double) (t1 ? t1 : 1);
506 n2 = (double) c2.ci_mem / (double) (t2 ? t2 : 1);
507
508 if (n1 < n2)
509 return -1;
510 else if (n1 == n2)
511 return (cmp_comm(c1.ci_comm, c2.ci_comm));
512 else
513 return 1;
514}
515
516/* sort by the number of invocations */
517static int
518cmp_calls(const DBT *d1, const DBT *d2)
519{
520 struct cmdinfo c1, c2;
521
522 memcpy(&c1, d1->data, sizeof(c1));
523 memcpy(&c2, d2->data, sizeof(c2));
524
525 if (c1.ci_calls < c2.ci_calls)
526 return -1;
527 else if (c1.ci_calls == c2.ci_calls)
528 return (cmp_comm(c1.ci_comm, c2.ci_comm));
529 else
530 return 1;
531}