Bug Summary

File:src/usr.sbin/sa/main.c
Warning:line 328, 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.4 -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name main.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 1 -pic-is-pie -mframe-pointer=all -relaxed-aliasing -ffp-contract=on -fno-rounding-math -mconstructor-aliases -funwind-tables=2 -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/llvm16/lib/clang/16 -internal-isystem /usr/local/llvm16/lib/clang/16/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 -fcf-protection=branch -fno-jump-tables -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/scan/2024-01-11-140451-98009-1 -x c /usr/src/usr.sbin/sa/main.c
1/* $OpenBSD: main.c,v 1.19 2022/12/04 23:50:51 cheloha 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 174
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_MAX0x7fffffff, &errstr);
164 if (errstr)
165 errx(1, "-v %s: %s", optarg, errstr);
166 break;
167 default:
168 (void)fprintf(stderr(&__sF[2]),
169 "usage: %s [-abcDdfijKklmnqrstu] [-v cutoff]"
170 " [file ...]\n", __progname);
171 exit(1);
172 }
173
174 argc -= optind;
175 argv += optind;
176
177 /* various argument checking */
178 if (fflag && !vflag)
5
Assuming 'fflag' is 0
179 errx(1, "only one of -f requires -v");
180 if (fflag
5.1
'fflag' is 0
&& aflag)
181 errx(1, "only one of -a and -v may be specified");
182 /* XXX need more argument checking */
183
184 if (!uflag) {
6
Assuming 'uflag' is not equal to 0
7
Taking false branch
185 /* initialize tables */
186 if ((sflag || (!mflag && !qflag)) && pacct_init() != 0)
187 errx(1, "process accounting initialization failed");
188 if ((sflag || (mflag && !qflag)) && usracct_init() != 0)
189 errx(1, "user accounting initialization failed");
190 }
191
192 if (argc == 0) {
8
Assuming 'argc' is not equal to 0
9
Taking false branch
193 argc = dfltargc;
194 argv = dfltargv;
195 }
196
197 /* for each file specified */
198 for (; argc > 0; argc--, argv++) {
10
Assuming 'argc' is > 0
11
Loop condition is true. Entering loop body
199 int fd;
200
201 /*
202 * load the accounting data from the file.
203 * if it fails, go on to the next file.
204 */
205 fd = acct_load(argv[0], sflag);
12
Calling 'acct_load'
206 if (fd < 0)
207 continue;
208
209 if (!uflag && sflag) {
210#ifndef DEBUG
211 sigset_t nmask, omask;
212 int unmask = 1;
213
214 /*
215 * block most signals so we aren't interrupted during
216 * the update.
217 */
218 if (sigfillset(&nmask) == -1) {
219 warn("sigfillset");
220 unmask = 0;
221 error = 1;
222 }
223 if (unmask &&
224 (sigprocmask(SIG_BLOCK1, &nmask, &omask) == -1)) {
225 warn("couldn't set signal mask ");
226 unmask = 0;
227 error = 1;
228 }
229#endif /* DEBUG */
230
231 /*
232 * truncate the accounting data file ASAP, to avoid
233 * losing data. don't worry about errors in updating
234 * the saved stats; better to underbill than overbill,
235 * but we want every accounting record intact.
236 */
237 if (ftruncate(fd, 0) == -1) {
238 warn("couldn't truncate %s", *argv);
239 error = 1;
240 }
241
242 /*
243 * update saved user and process accounting data.
244 * note errors for later.
245 */
246 if (pacct_update() != 0 || usracct_update() != 0)
247 error = 1;
248
249#ifndef DEBUG
250 /*
251 * restore signals
252 */
253 if (unmask &&
254 (sigprocmask(SIG_SETMASK3, &omask, NULL((void *)0)) == -1)) {
255 warn("couldn't restore signal mask");
256 error = 1;
257 }
258#endif /* DEBUG */
259 }
260
261 /*
262 * close the opened accounting file
263 */
264 if (close(fd) == -1) {
265 warn("close %s", *argv);
266 error = 1;
267 }
268 }
269
270 if (!uflag && !qflag) {
271 /* print any results we may have obtained. */
272 if (!mflag)
273 pacct_print();
274 else
275 usracct_print();
276 }
277
278 if (!uflag) {
279 /* finally, deallocate databases */
280 if (sflag || (!mflag && !qflag))
281 pacct_destroy();
282 if (sflag || (mflag && !qflag))
283 usracct_destroy();
284 }
285
286 exit(error);
287}
288
289static int
290acct_load(char *pn, int wr)
291{
292 struct acct ac;
293 struct cmdinfo ci;
294 ssize_t rv;
295 int fd, i;
296
297 /*
298 * open the file
299 */
300 fd = open(pn, wr ? O_RDWR0x0002 : O_RDONLY0x0000);
13
Assuming 'wr' is 0
14
'?' condition is false
301 if (fd == -1) {
15
Assuming the condition is false
16
Taking false branch
302 warn("open %s %s", pn, wr ? "for read/write" : "read-only");
303 return (-1);
304 }
305
306 /*
307 * read all we can; don't stat and open because more processes
308 * could exit, and we'd miss them
309 */
310 while (1) {
17
Loop condition is true. Entering loop body
311 /* get one accounting entry and punt if there's an error */
312 rv = read(fd, &ac, sizeof(struct acct));
313 if (rv == -1)
18
Assuming the condition is false
314 warn("error reading %s", pn);
315 else if (rv > 0 && rv < sizeof(struct acct))
19
Assuming 'rv' is > 0
20
Assuming the condition is false
21
Taking false branch
316 warnx("short read of accounting data in %s", pn);
317 if (rv != sizeof(struct acct))
22
Taking false branch
318 break;
319
320 /* decode it */
321 ci.ci_calls = 1;
322 for (i = 0; i < sizeof(ac.ac_comm) && ac.ac_comm[i] != '\0';
23
Assuming the condition is true
24
Loop condition is true. Entering loop body
323 i++) {
324 unsigned char c = ac.ac_comm[i];
325
326 if (!isascii(c) || iscntrl(c)) {
25
Assuming the character is not an ASCII character
327 ci.ci_comm[i] = '?';
328 ci.ci_flags |= CI_UNPRINTABLE0x0001;
26
The left expression of the compound assignment is an uninitialized value. The computed value will also be garbage
329 } else
330 ci.ci_comm[i] = c;
331 }
332 if (ac.ac_flag & AFORK0x00000001)
333 ci.ci_comm[i++] = '*';
334 ci.ci_comm[i++] = '\0';
335 ci.ci_etime = decode_comp_t(ac.ac_etime);
336 ci.ci_utime = decode_comp_t(ac.ac_utime);
337 ci.ci_stime = decode_comp_t(ac.ac_stime);
338 ci.ci_uid = ac.ac_uid;
339 ci.ci_mem = ac.ac_mem;
340 ci.ci_io = decode_comp_t(ac.ac_io) / AHZ64;
341 ci.ci_pid = ac.ac_pid;
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 pid %u %s\n",
351 ci.ci_uid,
352 (ci.ci_utime + ci.ci_stime) / (double) AHZ64,
353 ci.ci_mem, ci.ci_io, ci.ci_pid, 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}