Bug Summary

File:src/usr.bin/radioctl/radioctl.c
Warning:line 211, column 6
Potential leak of memory pointed to by 'opt.string'

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 radioctl.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/radioctl/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.bin/radioctl/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/radioctl/radioctl.c
1/* $OpenBSD: radioctl.c,v 1.20 2019/06/28 13:35:03 deraadt Exp $ */
2/* $RuOBSD: radioctl.c,v 1.4 2001/10/20 18:09:10 pva Exp $ */
3
4/*
5 * Copyright (c) 2001 Vladimir Popov <jumbo@narod.ru>
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
22 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
23 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
24 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
25 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
26 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29#include <sys/ioctl.h>
30#include <sys/radioio.h>
31
32#include <dev/ic/bt8xx.h>
33
34#include <err.h>
35#include <fcntl.h>
36#include <stdio.h>
37#include <stdlib.h>
38#include <string.h>
39#include <unistd.h>
40#include <ctype.h>
41
42#define RADIO_ENV"RADIODEVICE" "RADIODEVICE"
43#define RADIODEVICE"/dev/radio" "/dev/radio"
44
45const char *varname[] = {
46 "search",
47#define OPTION_SEARCH0x00 0x00
48 "volume",
49#define OPTION_VOLUME0x01 0x01
50 "frequency",
51#define OPTION_FREQUENCY0x02 0x02
52 "mute",
53#define OPTION_MUTE0x03 0x03
54 "reference",
55#define OPTION_REFERENCE0x04 0x04
56 "mono",
57#define OPTION_MONO0x05 0x05
58 "stereo",
59#define OPTION_STEREO0x06 0x06
60 "sensitivity",
61#define OPTION_SENSITIVITY0x07 0x07
62 "channel",
63#define OPTION_CHANNEL0x08 0x08
64 "chnlset"
65#define OPTION_CHNLSET0x09 0x09
66};
67
68#define OPTION_NONE~0u ~0u
69#define VALUE_NONE~0u ~0u
70
71struct opt_t {
72 char *string;
73 int option;
74 int sign;
75#define SIGN_NONE0 0
76#define SIGN_PLUS1 1
77#define SIGN_MINUS-1 -1
78 u_int32_t value;
79};
80
81struct chansets {
82 int value;
83 char *name;
84} chansets[] = {
85{ CHNLSET_NABCST1, "nabcst", },
86{ CHNLSET_CABLEIRC2, "cableirc", },
87{ CHNLSET_CABLEHRC3, "cablehrc", },
88{ CHNLSET_WEUROPE4, "weurope", },
89{ CHNLSET_JPNBCST5, "jpnbcst", },
90{ CHNLSET_JPNCABLE6, "jpncable", },
91{ CHNLSET_XUSSR7, "xussr", },
92{ CHNLSET_AUSTRALIA8, "australia", },
93{ CHNLSET_FRANCE9, "france", },
94{ 0, NULL((void *)0) }
95};
96
97extern char *__progname;
98const char *onchar = "on";
99#define ONCHAR_LEN2 2
100const char *offchar = "off";
101#define OFFCHAR_LEN3 3
102
103struct radio_info ri;
104unsigned int i = 0;
105
106int parse_opt(char *, struct opt_t *);
107
108void print_vars(int, int);
109void do_ioctls(int, struct opt_t *, int);
110
111void print_value(int, int);
112void change_value(const struct opt_t);
113void update_value(int, int *, int);
114
115void warn_unsupported(int);
116void usage(void);
117
118void show_verbose(const char *, int);
119void show_int_val(int, const char *, char *, int);
120void show_float_val(float, const char *, char *, int);
121void show_char_val(const char *, const char *, int);
122int str_to_opt(const char *);
123u_int str_to_int(char *, int);
124
125/*
126 * Control behavior of a FM tuner - set frequency, volume etc
127 */
128int
129main(int argc, char **argv)
130{
131 struct opt_t opt;
132 char **avp;
133
134 char *radiodev = NULL((void *)0);
135 int rd = -1;
136 int optchar;
137 int show_vars = 0;
138 int show_choices = 0;
139 int silent = 0;
140 int mode = O_RDONLY0x0000;
141
142 radiodev = getenv(RADIO_ENV"RADIODEVICE");
143 if (radiodev == NULL((void *)0))
1
Assuming 'radiodev' is not equal to NULL
2
Taking false branch
144 radiodev = RADIODEVICE"/dev/radio";
145
146 while ((optchar = getopt(argc, argv, "af:nvw")) != -1) {
3
Assuming the condition is false
4
Loop condition is false. Execution continues on line 169
147 switch (optchar) {
148 case 'a':
149 show_vars = 1;
150 break;
151 case 'f':
152 radiodev = optarg;
153 break;
154 case 'n':
155 silent = 1;
156 break;
157 case 'v':
158 show_choices = 1;
159 break;
160 case 'w':
161 /* backwards compatibility */
162 break;
163 default:
164 usage();
165 /* NOTREACHED */
166 }
167 }
168
169 argc -= optind;
170 argv += optind;
171
172 if (argc == 0)
5
Assuming 'argc' is not equal to 0
6
Taking false branch
173 show_vars = 1;
174
175 /*
176 * Scan the options for `name=value` so the
177 * device can be opened in the proper mode.
178 */
179 for (avp = argv; *avp != NULL((void *)0); avp++)
7
Loop condition is true. Entering loop body
180 if (strchr(*avp, '=') != NULL((void *)0)) {
8
Assuming the condition is true
9
Taking true branch
181 mode = O_RDWR0x0002;
182 break;
10
Execution continues on line 185
183 }
184
185 rd = open(radiodev, mode);
186 if (rd == -1)
11
Assuming the condition is false
12
Taking false branch
187 err(1, "%s open error", radiodev);
188
189 if (ioctl(rd, RIOCGINFO((unsigned long)0x40000000 | ((sizeof(struct radio_info) &
0x1fff) << 16) | ((('R')) << 8) | ((21)))
, &ri) == -1
)
13
Assuming the condition is false
14
Taking false branch
190 err(1, "RIOCGINFO");
191
192 if (!argc
14.1
'argc' is not equal to 0
&& show_vars)
193 print_vars(silent, show_choices);
194 else if (argc > 0 && !show_vars
15.1
'show_vars' is 0
) {
15
Assuming 'argc' is > 0
16
Taking true branch
195 if (mode
16.1
'mode' is equal to O_RDWR
== O_RDWR0x0002) {
17
Taking true branch
196 for (; argc--; argv++)
18
Loop condition is true. Entering loop body
39
Loop condition is false. Execution continues on line 211
197 if (parse_opt(*argv, &opt))
19
Calling 'parse_opt'
37
Returned allocated memory
38
Taking false branch
198 do_ioctls(rd, &opt, silent);
199 } else {
200 for (; argc--; argv++)
201 if (parse_opt(*argv, &opt)) {
202 show_verbose(varname[opt.option],
203 silent);
204 print_value(opt.option, show_choices);
205 free(opt.string);
206 putchar('\n')(!__isthreaded ? __sputc('\n', (&__sF[1])) : (putc)('\n',
(&__sF[1])))
;
207 }
208 }
209 }
210
211 if (close(rd) == -1)
40
Potential leak of memory pointed to by 'opt.string'
212 warn("%s close error", radiodev);
213
214 return 0;
215}
216
217void
218usage(void)
219{
220 fprintf(stderr(&__sF[2]),
221 "usage: %s [-anv] [-f file]\n"
222 " %s [-nv] [-f file] name\n"
223 " %s [-n] [-f file] name=value\n",
224 __progname, __progname, __progname);
225 exit(1);
226}
227
228void
229show_verbose(const char *nick, int silent)
230{
231 if (!silent)
232 printf("%s=", nick);
233}
234
235void
236warn_unsupported(int optval)
237{
238 warnx("driver does not support `%s'", varname[optval]);
239}
240
241void
242do_ioctls(int fd, struct opt_t *o, int silent)
243{
244 int oval;
245
246 if (fd < 0 || o == NULL((void *)0))
247 return;
248
249 if (o->option == OPTION_SEARCH0x00 && !(ri.caps & RADIO_CAPS_HW_SEARCH(1<<3))) {
250 warn_unsupported(o->option);
251 return;
252 }
253
254 oval = o->option == OPTION_SEARCH0x00 ? OPTION_FREQUENCY0x02 : o->option;
255 if (!silent)
256 printf("%s: ", varname[oval]);
257
258 print_value(o->option, 0);
259 printf(" -> ");
260
261 if (o->option == OPTION_SEARCH0x00) {
262
263 if (ioctl(fd, RIOCSSRCH((unsigned long)0x80000000 | ((sizeof(int) & 0x1fff) <<
16) | ((('R')) << 8) | ((23)))
, &o->value) == -1) {
264 warn("RIOCSSRCH");
265 return;
266 }
267
268 } else {
269
270 change_value(*o);
271 if (ioctl(fd, RIOCSINFO(((unsigned long)0x80000000|(unsigned long)0x40000000) | ((sizeof
(struct radio_info) & 0x1fff) << 16) | ((('R')) <<
8) | ((22)))
, &ri) == -1) {
272 warn("RIOCSINFO");
273 return;
274 }
275
276 }
277
278 if (ioctl(fd, RIOCGINFO((unsigned long)0x40000000 | ((sizeof(struct radio_info) &
0x1fff) << 16) | ((('R')) << 8) | ((21)))
, &ri) == -1) {
279 warn("RIOCGINFO");
280 return;
281 }
282
283 print_value(o->option, 0);
284 putchar('\n')(!__isthreaded ? __sputc('\n', (&__sF[1])) : (putc)('\n',
(&__sF[1])))
;
285}
286
287void
288change_value(const struct opt_t o)
289{
290 int unsupported = 0;
291
292 if (o.value == VALUE_NONE~0u)
293 return;
294
295 switch (o.option) {
296 case OPTION_VOLUME0x01:
297 update_value(o.sign, &ri.volume, o.value);
298 break;
299 case OPTION_FREQUENCY0x02:
300 ri.tuner_mode = RADIO_TUNER_MODE_RADIO(1<<0);
301 update_value(o.sign, &ri.freq, o.value);
302 break;
303 case OPTION_REFERENCE0x04:
304 if (ri.caps & RADIO_CAPS_REFERENCE_FREQ(1<<5))
305 update_value(o.sign, &ri.rfreq, o.value);
306 else
307 unsupported++;
308 break;
309 case OPTION_MONO0x05:
310 /* FALLTHROUGH */
311 case OPTION_STEREO0x06:
312 if (ri.caps & RADIO_CAPS_SET_MONO(1<<2))
313 ri.stereo = o.option == OPTION_MONO0x05 ? !o.value : o.value;
314 else
315 unsupported++;
316 break;
317 case OPTION_SENSITIVITY0x07:
318 if (ri.caps & RADIO_CAPS_LOCK_SENSITIVITY(1<<6))
319 update_value(o.sign, &ri.lock, o.value);
320 else
321 unsupported++;
322 break;
323 case OPTION_MUTE0x03:
324 ri.mute = o.value;
325 break;
326 case OPTION_CHANNEL0x08:
327 ri.tuner_mode = RADIO_TUNER_MODE_TV(1<<1);
328 update_value(o.sign, &ri.chan, o.value);
329 break;
330 case OPTION_CHNLSET0x09:
331 ri.chnlset = o.value;
332 break;
333 }
334
335 if (unsupported)
336 warn_unsupported(o.option);
337}
338
339/*
340 * Convert string to integer representation of a parameter
341 */
342int
343str_to_opt(const char *topt)
344{
345 int res, toptlen, varlen, len, varsize;
346
347 if (topt == NULL((void *)0) || *topt == '\0')
348 return OPTION_NONE~0u;
349
350 varsize = sizeof(varname) / sizeof(varname[0]);
351 toptlen = strlen(topt);
352
353 for (res = 0; res < varsize; res++) {
354 varlen = strlen(varname[res]);
355 len = toptlen > varlen ? toptlen : varlen;
356 if (strncmp(topt, varname[res], len) == 0)
357 return res;
358 }
359
360 warnx("bad name `%s'", topt);
361 return OPTION_NONE~0u;
362}
363
364void
365update_value(int sign, int *value, int update)
366{
367 switch (sign) {
368 case SIGN_NONE0:
369 *value = update;
370 break;
371 case SIGN_PLUS1:
372 *value += update;
373 break;
374 case SIGN_MINUS-1:
375 *value -= update;
376 break;
377 }
378}
379
380/*
381 * Convert string to unsigned integer
382 */
383u_int
384str_to_int(char *str, int optval)
385{
386 int val;
387
388 if (str == NULL((void *)0) || *str == '\0')
389 return VALUE_NONE~0u;
390
391 if (optval == OPTION_FREQUENCY0x02)
392 val = (int)(1000 * atof(str));
393 else
394 val = (int)strtol(str, (char **)NULL((void *)0), 10);
395
396 return val;
397}
398
399/*
400 * parse string s into struct opt_t
401 * return true on success, false on failure
402 */
403int
404parse_opt(char *s, struct opt_t *o) {
405 static const char badvalue[] = "bad value `%s'";
406 char *topt = NULL((void *)0);
407 int slen, optlen;
408
409 if (s == NULL((void *)0) || *s == '\0' || o
21.1
'o' is not equal to NULL
== NULL((void *)0))
20
Assuming 's' is not equal to NULL
21
Assuming the condition is false
22
Taking false branch
410 return 0;
411
412 o->string = NULL((void *)0);
413 o->option = OPTION_NONE~0u;
414 o->value = VALUE_NONE~0u;
415 o->sign = SIGN_NONE0;
416
417 slen = strlen(s);
418 optlen = strcspn(s, "=");
419
420 /* Set only o->optval, the rest is missing */
421 if (slen == optlen) {
23
Assuming 'slen' is not equal to 'optlen'
24
Taking false branch
422 o->option = str_to_opt(s);
423 return o->option == OPTION_NONE~0u ? 0 : 1;
424 }
425
426 if (optlen > slen - 2) {
25
Assuming the condition is false
26
Taking false branch
427 warnx(badvalue, s);
428 return 0;
429 }
430
431 slen -= ++optlen;
432
433 if ((topt = malloc(optlen)) == NULL((void *)0)) {
27
Memory is allocated
28
Assuming the condition is false
29
Taking false branch
434 warn("memory allocation error");
435 return 0;
436 }
437 strlcpy(topt, s, optlen);
438
439 if ((o->option = str_to_opt(topt)) == OPTION_NONE~0u) {
30
Taking false branch
440 free(topt);
441 return 0;
442 }
443 o->string = topt;
444
445 topt = &s[optlen];
446
447 if (strcmp(o->string, "chnlset") == 0) {
31
Assuming the condition is false
32
Taking false branch
448 for (i = 0; chansets[i].name; i++)
449 if (strncmp(chansets[i].name, topt,
450 strlen(chansets[i].name)) == 0)
451 break;
452 if (chansets[i].name != NULL((void *)0)) {
453 o->value = chansets[i].value;
454 return 1;
455 } else {
456 warnx(badvalue, topt);
457 return 0;
458 }
459 }
460
461 switch (*topt) {
33
Control jumps to the 'default' case at line 483
462 case '+':
463 case '-':
464 o->sign = (*topt == '+') ? SIGN_PLUS1 : SIGN_MINUS-1;
465 o->value = str_to_int(&topt[1], o->option);
466 break;
467 case 'o':
468 if (strncmp(topt, offchar,
469 slen > OFFCHAR_LEN3 ? slen : OFFCHAR_LEN3) == 0)
470 o->value = 0;
471 else if (strncmp(topt, onchar,
472 slen > ONCHAR_LEN2 ? slen : ONCHAR_LEN2) == 0)
473 o->value = 1;
474 break;
475 case 'u':
476 if (strncmp(topt, "up", slen > 2 ? slen : 2) == 0)
477 o->value = 1;
478 break;
479 case 'd':
480 if (strncmp(topt, "down", slen > 4 ? slen : 4) == 0)
481 o->value = 0;
482 break;
483 default:
484 if (isdigit((unsigned char)*topt))
34
Taking false branch
485 o->value = str_to_int(topt, o->option);
486 break;
35
Execution continues on line 489
487 }
488
489 if (o->value == VALUE_NONE~0u) {
36
Taking true branch
490 warnx(badvalue, topt);
491 return 0;
492 }
493
494 return 1;
495}
496
497/*
498 * Print current value of the parameter.
499 */
500void
501print_value(int optval, int show_choices)
502{
503 if (optval == OPTION_NONE~0u)
504 return;
505
506 switch (optval) {
507 case OPTION_SEARCH0x00:
508 /* FALLTHROUGH */
509 case OPTION_FREQUENCY0x02:
510 printf("%.2fMHz", (float)ri.freq / 1000.);
511 break;
512 case OPTION_REFERENCE0x04:
513 printf("%ukHz", ri.rfreq);
514 break;
515 case OPTION_SENSITIVITY0x07:
516 printf("%umkV", ri.lock);
517 break;
518 case OPTION_MUTE0x03:
519 printf("%s", ri.mute ? onchar : offchar);
520 break;
521 case OPTION_MONO0x05:
522 printf("%s", ri.stereo ? offchar : onchar);
523 break;
524 case OPTION_STEREO0x06:
525 printf("%s", ri.stereo ? onchar : offchar);
526 break;
527 case OPTION_CHANNEL0x08:
528 printf("%u", ri.chan);
529 break;
530 case OPTION_CHNLSET0x09:
531 for (i = 0; chansets[i].name; i++) {
532 if (chansets[i].value == ri.chnlset)
533 printf("%s", chansets[i].name);
534 }
535 if (show_choices) {
536 printf("\n\t[");
537 for (i = 0; chansets[i].name; i++)
538 printf("%s ", chansets[i].name);
539 printf("]");
540 }
541 break;
542 case OPTION_VOLUME0x01:
543 default:
544 printf("%u", ri.volume);
545 break;
546 }
547}
548
549void
550show_int_val(int val, const char *nick, char *append, int silent)
551{
552 show_verbose(nick, silent);
553 printf("%u%s\n", val, append);
554}
555
556void
557show_float_val(float val, const char *nick, char *append, int silent)
558{
559 show_verbose(nick, silent);
560 printf("%.2f%s\n", val, append);
561}
562
563void
564show_char_val(const char *val, const char *nick, int silent)
565{
566 show_verbose(nick, silent);
567 printf("%s\n", val);
568}
569
570/*
571 * Print all available parameters
572 */
573void
574print_vars(int silent, int show_choices)
575{
576 show_int_val(ri.volume, varname[OPTION_VOLUME0x01], "", silent);
577 show_int_val(ri.chan, varname[OPTION_CHANNEL0x08], "", silent);
578 for (i = 0; chansets[i].name; i++) {
579 if (chansets[i].value == ri.chnlset)
580 show_char_val(chansets[i].name, varname[OPTION_CHNLSET0x09], silent);
581 }
582 if (show_choices) {
583 printf("\t[ ");
584 for (i = 0; chansets[i].name; i++)
585 printf("%s ", chansets[i].name);
586 printf("]\n");
587 }
588 show_float_val((float)ri.freq / 1000., varname[OPTION_FREQUENCY0x02],
589 "MHz", silent);
590 show_char_val(ri.mute ? onchar : offchar, varname[OPTION_MUTE0x03], silent);
591
592 if (ri.caps & RADIO_CAPS_REFERENCE_FREQ(1<<5))
593 show_int_val(ri.rfreq, varname[OPTION_REFERENCE0x04], "kHz", silent);
594 if (ri.caps & RADIO_CAPS_LOCK_SENSITIVITY(1<<6))
595 show_int_val(ri.lock, varname[OPTION_SENSITIVITY0x07], "mkV", silent);
596
597 if (ri.caps & RADIO_CAPS_DETECT_SIGNAL(1<<1)) {
598 show_verbose("signal", silent);
599 printf("%s\n", ri.info & RADIO_INFO_SIGNAL(1<<1) ? onchar : offchar);
600 }
601 if (ri.caps & RADIO_CAPS_DETECT_STEREO(1<<0)) {
602 show_verbose(varname[OPTION_STEREO0x06], silent);
603 printf("%s\n", ri.info & RADIO_INFO_STEREO(1<<0) ? onchar : offchar);
604 }
605
606 if (!silent) {
607 printf("mode: %s\n",
608 ri.tuner_mode == RADIO_TUNER_MODE_TV(1<<1) ? "TV" : "radio");
609
610 puts("card capabilities:");
611 }
612
613 if (ri.caps & RADIO_CAPS_SET_MONO(1<<2))
614 puts("\tmanageable mono/stereo");
615 if (ri.caps & RADIO_CAPS_HW_SEARCH(1<<3))
616 puts("\thardware search");
617 if (ri.caps & RADIO_CAPS_HW_AFC(1<<4))
618 puts("\thardware AFC");
619}