Bug Summary

File:src/usr.bin/usbhidctl/usbhid.c
Warning:line 715, column 20
Access to field 'status' results in a dereference of a null pointer (loaded from variable 'repptr')

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 usbhid.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.bin/usbhidctl/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.bin/usbhidctl/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.bin/usbhidctl/usbhid.c
1/* $OpenBSD: usbhid.c,v 1.20 2023/03/07 17:43:59 guenther Exp $ */
2/* $NetBSD: usbhid.c,v 1.22 2002/02/20 20:30:42 christos Exp $ */
3
4/*
5 * Copyright (c) 2001 The NetBSD Foundation, Inc.
6 * All rights reserved.
7 *
8 * This code is derived from software contributed to The NetBSD Foundation
9 * by David Sainty <David.Sainty@dtsp.co.nz>
10 *
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
21 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
22 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
24 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30 * POSSIBILITY OF SUCH DAMAGE.
31 */
32
33#include <sys/types.h>
34
35#include <dev/usb/usb.h>
36#include <dev/usb/usbhid.h>
37
38#include <ctype.h>
39#include <err.h>
40#include <errno(*__errno()).h>
41#include <fcntl.h>
42#include <limits.h>
43#include <stdio.h>
44#include <stdlib.h>
45#include <string.h>
46#include <unistd.h>
47#include <usbhid.h>
48
49/*
50 * Zero if not in a verbose mode. Greater levels of verbosity
51 * are indicated by values larger than one.
52 */
53unsigned int verbose;
54
55/* Parser tokens */
56#define DELIM_USAGE'.' '.'
57#define DELIM_PAGE':' ':'
58#define DELIM_SET'=' '='
59
60static int reportid;
61
62struct Susbvar {
63 /* Variable name, not NUL terminated */
64 char const *variable;
65 size_t varlen;
66
67 char const *value; /* Value to set variable to */
68
69#define MATCH_ALL(1 << 0) (1 << 0)
70#define MATCH_COLLECTIONS(1 << 1) (1 << 1)
71#define MATCH_NODATA(1 << 2) (1 << 2)
72#define MATCH_CONSTANTS(1 << 3) (1 << 3)
73#define MATCH_WASMATCHED(1 << 4) (1 << 4)
74#define MATCH_SHOWPAGENAME(1 << 5) (1 << 5)
75#define MATCH_SHOWNUMERIC(1 << 6) (1 << 6)
76#define MATCH_WRITABLE(1 << 7) (1 << 7)
77#define MATCH_SHOWVALUES(1 << 8) (1 << 8)
78 unsigned int mflags;
79
80 /* Workspace for hidmatch() */
81 ssize_t matchindex;
82
83 int (*opfunc)(struct hid_item *item, struct Susbvar *var,
84 u_int32_t const *collist, size_t collen, u_char *buf);
85};
86
87struct Sreport {
88 struct usb_ctl_report *buffer;
89
90 enum {srs_uninit, srs_clean, srs_dirty} status;
91 int report_id;
92 size_t size;
93};
94
95static struct {
96 int uhid_report;
97 hid_kind_t hid_kind;
98 char const *name;
99} const reptoparam[] = {
100#define REPORT_INPUT0 0
101 { UHID_INPUT_REPORT0x01, hid_input, "input" },
102#define REPORT_OUTPUT1 1
103 { UHID_OUTPUT_REPORT0x02, hid_output, "output" },
104#define REPORT_FEATURE2 2
105 { UHID_FEATURE_REPORT0x03, hid_feature, "feature" }
106#define REPORT_MAXVAL2 2
107};
108
109/*
110 * Extract 16-bit unsigned usage ID from a numeric string. Returns -1
111 * if string failed to parse correctly.
112 */
113static int
114strtousage(const char *nptr, size_t nlen)
115{
116 char *endptr;
117 long result;
118 char numstr[16];
119
120 if (nlen >= sizeof(numstr) || !isdigit((unsigned char)*nptr))
121 return -1;
122
123 /*
124 * We use strtol() here, but unfortunately strtol() requires a
125 * NUL terminated string - which we don't have - at least not
126 * officially.
127 */
128 memcpy(numstr, nptr, nlen);
129 numstr[nlen] = '\0';
130
131 result = strtol(numstr, &endptr, 0);
132
133 if (result < 0 || result > 0xffff || endptr != &numstr[nlen])
134 return -1;
135
136 return result;
137}
138
139struct usagedata {
140 char const *page_name;
141 char const *usage_name;
142 size_t page_len;
143 size_t usage_len;
144 int isfinal;
145 u_int32_t usage_id;
146};
147
148/*
149 * Test a rule against the current usage data. Returns -1 on no
150 * match, 0 on partial match and 1 on complete match.
151 */
152static int
153hidtestrule(struct Susbvar *var, struct usagedata *cache)
154{
155 char const *varname;
156 ssize_t matchindex, pagesplit;
157 size_t strind, varlen;
158 int numusage;
159 u_int32_t usage_id;
160
161 matchindex = var->matchindex;
162 varname = var->variable;
163 varlen = var->varlen;
164
165 usage_id = cache->usage_id;
166
167 /*
168 * Parse the current variable name, locating the end of the
169 * current 'usage', and possibly where the usage page name
170 * ends.
171 */
172 pagesplit = -1;
173 for (strind = matchindex; strind < varlen; strind++) {
174 if (varname[strind] == DELIM_USAGE'.')
175 break;
176 if (varname[strind] == DELIM_PAGE':')
177 pagesplit = strind;
178 }
179
180 if (cache->isfinal && strind != varlen)
181 /*
182 * Variable name is too long (hit delimiter instead of
183 * end-of-variable).
184 */
185 return -1;
186
187 if (pagesplit >= 0) {
188 /*
189 * Page name was specified, determine whether it was
190 * symbolic or numeric.
191 */
192 char const *strstart;
193 int numpage;
194
195 strstart = &varname[matchindex];
196
197 numpage = strtousage(strstart, pagesplit - matchindex);
198
199 if (numpage >= 0) {
200 /* Valid numeric */
201
202 if (numpage != HID_PAGE(usage_id)(((usage_id) >> 16) & 0xffff))
203 /* Numeric didn't match page ID */
204 return -1;
205 } else {
206 /* Not a valid numeric */
207
208 /*
209 * Load and cache the page name if and only if
210 * it hasn't already been loaded (it's a
211 * fairly expensive operation).
212 */
213 if (cache->page_name == NULL((void *)0)) {
214 cache->page_name = hid_usage_page(HID_PAGE(usage_id)(((usage_id) >> 16) & 0xffff));
215 cache->page_len = strlen(cache->page_name);
216 }
217
218 /*
219 * Compare specified page name to actual page
220 * name.
221 */
222 if (cache->page_len !=
223 (size_t)(pagesplit - matchindex) ||
224 memcmp(cache->page_name,
225 &varname[matchindex],
226 cache->page_len) != 0)
227 /* Mismatch, page name wrong */
228 return -1;
229 }
230
231 /* Page matches, discard page name */
232 matchindex = pagesplit + 1;
233 }
234
235 numusage = strtousage(&varname[matchindex], strind - matchindex);
236
237 if (numusage >= 0) {
238 /* Valid numeric */
239
240 if (numusage != HID_USAGE(usage_id)((usage_id) & 0xffff))
241 /* Numeric didn't match usage ID */
242 return -1;
243 } else {
244 /* Not a valid numeric */
245
246 /* Load and cache the usage name */
247 if (cache->usage_name == NULL((void *)0)) {
248 cache->usage_name = hid_usage_in_page(usage_id);
249 cache->usage_len = strlen(cache->usage_name);
250 }
251
252 /*
253 * Compare specified usage name to actual usage name
254 */
255 if (cache->usage_len != (size_t)(strind - matchindex) ||
256 memcmp(cache->usage_name, &varname[matchindex],
257 cache->usage_len) != 0)
258 /* Mismatch, usage name wrong */
259 return -1;
260 }
261
262 if (cache->isfinal)
263 /* Match */
264 return 1;
265
266 /*
267 * Partial match: Move index past this usage string +
268 * delimiter
269 */
270 var->matchindex = strind + 1;
271
272 return 0;
273}
274
275/*
276 * hidmatch() determines whether the item specified in 'item', and
277 * nested within a hierarchy of collections specified in 'collist'
278 * matches any of the rules in the list 'varlist'. Returns the
279 * matching rule on success, or NULL on no match.
280 */
281static struct Susbvar*
282hidmatch(u_int32_t const *collist, size_t collen, struct hid_item *item,
283 struct Susbvar *varlist, size_t vlsize)
284{
285 size_t colind, vlactive, vlind;
286 int iscollection;
287
288 /*
289 * Keep track of how many variables are still "active". When
290 * the active count reaches zero, don't bother to continue
291 * looking for matches.
292 */
293 vlactive = vlsize;
294
295 iscollection = item->kind == hid_collection ||
296 item->kind == hid_endcollection;
297
298 for (vlind = 0; vlind < vlsize; vlind++) {
299 struct Susbvar *var;
300
301 var = &varlist[vlind];
302
303 var->matchindex = 0;
304
305 if (!(var->mflags & MATCH_COLLECTIONS(1 << 1)) && iscollection) {
306 /* Don't match collections for this variable */
307 var->matchindex = -1;
308 vlactive--;
309 } else if (!iscollection && !(var->mflags & MATCH_CONSTANTS(1 << 3)) &&
310 (item->flags & HIO_CONST0x001)) {
311 /*
312 * Don't match constants for this variable,
313 * but ignore the constant bit on collections.
314 */
315 var->matchindex = -1;
316 vlactive--;
317 } else if ((var->mflags & MATCH_WRITABLE(1 << 7)) &&
318 ((item->kind != hid_output &&
319 item->kind != hid_feature) ||
320 (item->flags & HIO_CONST0x001))) {
321 /*
322 * If we are only matching writable items, if
323 * this is not an output or feature kind, or
324 * it is a constant, reject it.
325 */
326 var->matchindex = -1;
327 vlactive--;
328 } else if (var->mflags & MATCH_ALL(1 << 0)) {
329 /* Match immediately */
330 return &varlist[vlind];
331 }
332 }
333
334 /*
335 * Loop through each usage in the collection list, including
336 * the 'item' itself on the final iteration. For each usage,
337 * test which variables named in the rule list are still
338 * applicable - if any.
339 */
340 for (colind = 0; vlactive > 0 && colind <= collen; colind++) {
341 struct usagedata cache;
342
343 cache.isfinal = (colind == collen);
344 if (cache.isfinal)
345 cache.usage_id = item->usage;
346 else
347 cache.usage_id = collist[colind];
348
349 cache.usage_name = NULL((void *)0);
350 cache.page_name = NULL((void *)0);
351
352 /*
353 * Loop through each rule, testing whether the rule is
354 * still applicable or not. For each rule,
355 * 'matchindex' retains the current match state as an
356 * index into the variable name string, or -1 if this
357 * rule has been proven not to match.
358 */
359 for (vlind = 0; vlind < vlsize; vlind++) {
360 struct Susbvar *var;
361 int matchres;
362
363 var = &varlist[vlind];
364
365 if (var->matchindex < 0)
366 /* Mismatch at a previous level */
367 continue;
368
369 matchres = hidtestrule(var, &cache);
370
371 if (matchres < 0) {
372 /* Bad match */
373 var->matchindex = -1;
374 vlactive--;
375 continue;
376 } else if (matchres > 0) {
377 /* Complete match */
378 return var;
379 }
380 }
381 }
382
383 return NULL((void *)0);
384}
385
386static void
387allocreport(struct Sreport *report, report_desc_t rd, int repindex)
388{
389 int reptsize;
390
391 reptsize = hid_report_size(rd, reptoparam[repindex].hid_kind, reportid);
392 if (reptsize < 0)
393 errx(1, "Negative report size");
394 report->size = reptsize;
395
396 if (report->size > 0) {
397 report->buffer = malloc(sizeof(*report->buffer));
398 if (report->buffer == NULL((void *)0))
399 err(1, NULL((void *)0));
400 } else
401 report->buffer = NULL((void *)0);
402
403 report->status = srs_clean;
404}
405
406static void
407freereport(struct Sreport *report)
408{
409 free(report->buffer);
410 report->status = srs_uninit;
411}
412
413static void
414getreport(struct Sreport *report, int hidfd, report_desc_t rd, int repindex)
415{
416 if (report->status == srs_uninit) {
417 allocreport(report, rd, repindex);
418 if (report->size == 0)
419 return;
420
421 report->buffer->ucr_report = reptoparam[repindex].uhid_report;
422 if (ioctl(hidfd, USB_GET_REPORT(((unsigned long)0x80000000|(unsigned long)0x40000000) | ((sizeof
(struct usb_ctl_report) & 0x1fff) << 16) | ((('U'))
<< 8) | ((23)))
, report->buffer) == -1)
423 err(1, "USB_GET_REPORT (probably not supported by "
424 "device)");
425 }
426}
427
428static void
429setreport(struct Sreport *report, int hidfd, int repindex)
430{
431 if (report->status == srs_dirty) {
432 report->buffer->ucr_report = reptoparam[repindex].uhid_report;
433
434 if (ioctl(hidfd, USB_SET_REPORT((unsigned long)0x80000000 | ((sizeof(struct usb_ctl_report) &
0x1fff) << 16) | ((('U')) << 8) | ((24)))
, report->buffer) == -1)
435 err(1, "USB_SET_REPORT(%s)",
436 reptoparam[repindex].name);
437
438 report->status = srs_clean;
439 }
440}
441
442static int
443varop_value(struct hid_item *item, struct Susbvar *var,
444 u_int32_t const *collist, size_t collen, u_char *buf)
445{
446 printf("%d\n", hid_get_data(buf, item));
447 return 0;
448}
449
450static int
451varop_display(struct hid_item *item, struct Susbvar *var,
452 u_int32_t const *collist, size_t collen, u_char *buf)
453{
454 size_t colitem;
455 int val, i;
456
457 for (i = 0; i < item->report_count; i++) {
458 for (colitem = 0; colitem < collen; colitem++) {
459 if (var->mflags & MATCH_SHOWPAGENAME(1 << 5))
460 printf("%s:",
461 hid_usage_page(HID_PAGE(collist[colitem])(((collist[colitem]) >> 16) & 0xffff)));
462 printf("%s.", hid_usage_in_page(collist[colitem]));
463 }
464 if (var->mflags & MATCH_SHOWPAGENAME(1 << 5))
465 printf("%s:", hid_usage_page(HID_PAGE(item->usage)(((item->usage) >> 16) & 0xffff)));
466 val = hid_get_data(buf, item);
467 item->pos += item->report_size;
468 if (item->usage_minimum != 0 || item->usage_maximum != 0) {
469 val += item->usage_minimum;
470 printf("%s=1", hid_usage_in_page(val));
471 } else {
472 printf("%s=%d%s", hid_usage_in_page(item->usage),
473 val, item->flags & HIO_CONST0x001 ? " (const)" : "");
474 }
475 if (item->report_count > 1)
476 printf(" [%d]", i);
477 printf("\n");
478 }
479 return 0;
480}
481
482static int
483varop_modify(struct hid_item *item, struct Susbvar *var,
484 u_int32_t const *collist, size_t collen, u_char *buf)
485{
486 u_int dataval;
487
488 dataval = (u_int)strtol(var->value, NULL((void *)0), 10);
489
490 hid_set_data(buf, item, dataval);
491
492 if (var->mflags & MATCH_SHOWVALUES(1 << 8))
493 /* Display set value */
494 varop_display(item, var, collist, collen, buf);
495
496 return 1;
497}
498
499static void
500reportitem(char const *label, struct hid_item const *item, unsigned int mflags)
501{
502 int isconst = item->flags & HIO_CONST0x001,
503 isvar = item->flags & HIO_VARIABLE0x002;
504 printf("%s size=%d count=%d%s%s page=%s", label,
505 item->report_size, item->report_count,
506 isconst ? " Const" : "",
507 !isvar && !isconst ? " Array" : "",
508 hid_usage_page(HID_PAGE(item->usage)(((item->usage) >> 16) & 0xffff)));
509 if (item->usage_minimum != 0 || item->usage_maximum != 0) {
510 printf(" usage=%s..%s", hid_usage_in_page(item->usage_minimum),
511 hid_usage_in_page(item->usage_maximum));
512 if (mflags & MATCH_SHOWNUMERIC(1 << 6))
513 printf(" (%u:0x%x..%u:0x%x)",
514 HID_PAGE(item->usage_minimum)(((item->usage_minimum) >> 16) & 0xffff),
515 HID_USAGE(item->usage_minimum)((item->usage_minimum) & 0xffff),
516 HID_PAGE(item->usage_maximum)(((item->usage_maximum) >> 16) & 0xffff),
517 HID_USAGE(item->usage_maximum)((item->usage_maximum) & 0xffff));
518 } else {
519 printf(" usage=%s", hid_usage_in_page(item->usage));
520 if (mflags & MATCH_SHOWNUMERIC(1 << 6))
521 printf(" (%u:0x%x)",
522 HID_PAGE(item->usage)(((item->usage) >> 16) & 0xffff), HID_USAGE(item->usage)((item->usage) & 0xffff));
523 }
524 printf(", logical range %d..%d",
525 item->logical_minimum, item->logical_maximum);
526 if (item->physical_minimum != item->physical_maximum)
527 printf(", physical range %d..%d",
528 item->physical_minimum, item->physical_maximum);
529 if (item->unit)
530 printf(", unit=0x%02x exp=%d", item->unit,
531 item->unit_exponent);
532 printf("\n");
533}
534
535static int
536varop_report(struct hid_item *item, struct Susbvar *var,
537 u_int32_t const *collist, size_t collen, u_char *buf)
538{
539 switch (item->kind) {
540 case hid_collection:
541 printf("Collection page=%s usage=%s",
542 hid_usage_page(HID_PAGE(item->usage)(((item->usage) >> 16) & 0xffff)),
543 hid_usage_in_page(item->usage));
544 if (var->mflags & MATCH_SHOWNUMERIC(1 << 6))
545 printf(" (%u:0x%x)\n",
546 HID_PAGE(item->usage)(((item->usage) >> 16) & 0xffff), HID_USAGE(item->usage)((item->usage) & 0xffff));
547 else
548 printf("\n");
549 break;
550 case hid_endcollection:
551 printf("End collection\n");
552 break;
553 case hid_input:
554 reportitem("Input ", item, var->mflags);
555 break;
556 case hid_output:
557 reportitem("Output ", item, var->mflags);
558 break;
559 case hid_feature:
560 reportitem("Feature", item, var->mflags);
561 break;
562 }
563
564 return 0;
565}
566
567static void
568devloop(int hidfd, report_desc_t rd, struct Susbvar *varlist, size_t vlsize)
569{
570 u_char *dbuf;
571 struct hid_data *hdata;
572 size_t collind, dlen;
573 struct hid_item hitem;
574 u_int32_t colls[128];
575 struct Sreport inreport;
576
577 allocreport(&inreport, rd, REPORT_INPUT0);
578
579 if (inreport.size <= 0)
580 errx(1, "Input report descriptor invalid length");
581
582 dlen = inreport.size;
583 dbuf = inreport.buffer->ucr_data;
584
585 for (;;) {
586 ssize_t readlen;
587
588 readlen = read(hidfd, dbuf, dlen);
589 if (readlen == -1)
590 err(1, "Device read error");
591 if (dlen != (size_t)readlen)
592 errx(1, "Unexpected response length: %lu != %lu",
593 (unsigned long)readlen, (unsigned long)dlen);
594
595 collind = 0;
596 hdata = hid_start_parse(rd, 1 << hid_input, reportid);
597 if (hdata == NULL((void *)0))
598 errx(1, "Failed to start parser");
599
600 while (hid_get_item(hdata, &hitem)) {
601 struct Susbvar *matchvar;
602
603 switch (hitem.kind) {
604 case hid_collection:
605 if (collind >= (sizeof(colls) / sizeof(*colls)))
606 errx(1, "Excessive nested collections");
607 colls[collind++] = hitem.usage;
608 break;
609 case hid_endcollection:
610 if (collind == 0)
611 errx(1, "Excessive collection ends");
612 collind--;
613 break;
614 case hid_input:
615 break;
616 case hid_output:
617 case hid_feature:
618 errx(1, "Unexpected non-input item returned");
619 }
620
621 if (reportid != -1 && hitem.report_ID != reportid)
622 continue;
623
624 matchvar = hidmatch(colls, collind, &hitem,
625 varlist, vlsize);
626
627 if (matchvar != NULL((void *)0))
628 matchvar->opfunc(&hitem, matchvar,
629 colls, collind,
630 inreport.buffer->ucr_data);
631 }
632 hid_end_parse(hdata);
633 printf("\n");
634 }
635 /* NOTREACHED */
636}
637
638static void
639devshow(int hidfd, report_desc_t rd, struct Susbvar *varlist, size_t vlsize,
640 int kindset)
641{
642 struct hid_data *hdata;
643 size_t collind, repind, vlind;
644 struct hid_item hitem;
645 u_int32_t colls[128];
646 struct Sreport reports[REPORT_MAXVAL2 + 1];
647
648
649 for (repind = 0; repind < (sizeof(reports) / sizeof(*reports));
40
Loop condition is true. Entering loop body
41
Loop condition is true. Entering loop body
42
Loop condition is true. Entering loop body
43
Loop condition is false. Execution continues on line 655
650 repind++) {
651 reports[repind].status = srs_uninit;
652 reports[repind].buffer = NULL((void *)0);
653 }
654
655 collind = 0;
656 hdata = hid_start_parse(rd, kindset, reportid);
657 if (hdata == NULL((void *)0))
44
Assuming 'hdata' is not equal to NULL
45
Taking false branch
658 errx(1, "Failed to start parser");
659
660 while (hid_get_item(hdata, &hitem)) {
46
Loop condition is true. Entering loop body
661 struct Susbvar *matchvar;
662 int repindex;
663
664 if (verbose
46.1
'verbose' is <= 3
> 3)
47
Taking false branch
665 printf("item: kind=%d repid=%d usage=0x%x\n",
666 hitem.kind, hitem.report_ID, hitem.usage);
667 repindex = -1;
668 switch (hitem.kind) {
48
Control jumps to 'case hid_collection:' at line 669
669 case hid_collection:
670 if (collind >= (sizeof(colls) / sizeof(*colls)))
49
Taking false branch
671 errx(1, "Excessive nested collections");
672 colls[collind++] = hitem.usage;
673 break;
50
Execution continues on line 690
674 case hid_endcollection:
675 if (collind == 0)
676 errx(1, "Excessive collection ends");
677 collind--;
678 break;
679 case hid_input:
680 repindex = REPORT_INPUT0;
681 break;
682 case hid_output:
683 repindex = REPORT_OUTPUT1;
684 break;
685 case hid_feature:
686 repindex = REPORT_FEATURE2;
687 break;
688 }
689
690 if (reportid != -1 && hitem.report_ID != reportid)
691 continue;
692
693 matchvar = hidmatch(colls, collind, &hitem, varlist, vlsize);
694
695 if (matchvar != NULL((void *)0)) {
51
Assuming 'matchvar' is not equal to NULL
52
Taking true branch
696 u_char *bufdata;
697 struct Sreport *repptr;
698
699 matchvar->mflags |= MATCH_WASMATCHED(1 << 4);
700
701 if (repindex
52.1
'repindex' is < 0
>= 0)
53
Taking false branch
702 repptr = &reports[repindex];
703 else
704 repptr = NULL((void *)0);
54
Null pointer value stored to 'repptr'
705
706 if (repptr
54.1
'repptr' is equal to NULL
!= NULL((void *)0) &&
707 !(matchvar->mflags & MATCH_NODATA(1 << 2)))
708 getreport(repptr, hidfd, rd, repindex);
709
710 bufdata = (repptr
54.2
'repptr' is equal to NULL
== NULL((void *)0) || repptr->buffer == NULL((void *)0)) ?
711 NULL((void *)0) : repptr->buffer->ucr_data;
712
713 if (matchvar->opfunc(&hitem, matchvar, colls, collind,
55
Assuming the condition is true
56
Taking true branch
714 bufdata))
715 repptr->status = srs_dirty;
57
Access to field 'status' results in a dereference of a null pointer (loaded from variable 'repptr')
716 }
717 }
718 hid_end_parse(hdata);
719
720 for (repind = 0; repind < (sizeof(reports) / sizeof(*reports));
721 repind++) {
722 setreport(&reports[repind], hidfd, repind);
723 freereport(&reports[repind]);
724 }
725
726 /* Warn about any items that we couldn't find a match for */
727 for (vlind = 0; vlind < vlsize; vlind++) {
728 struct Susbvar *var;
729
730 var = &varlist[vlind];
731
732 if (var->variable != NULL((void *)0) &&
733 !(var->mflags & MATCH_WASMATCHED(1 << 4)))
734 warnx("Failed to match: %.*s", (int)var->varlen,
735 var->variable);
736 }
737}
738
739static void
740usage(void)
741{
742 extern char *__progname;
743
744 fprintf(stderr(&__sF[2]), "usage: %s -f device [-t table] [-alv]\n",
745 __progname);
746 fprintf(stderr(&__sF[2]), " %s -f device [-t table] [-v] -r\n",
747 __progname);
748 fprintf(stderr(&__sF[2]),
749 " %s -f device [-t table] [-lnv] name ...\n",
750 __progname);
751 fprintf(stderr(&__sF[2]),
752 " %s -f device [-t table] -w name=value ...\n",
753 __progname);
754 fprintf(stderr(&__sF[2]), " %s -f device -R\n", __progname);
755 exit(1);
756}
757
758int
759main(int argc, char **argv)
760{
761 char const *dev;
762 char const *table;
763 size_t varnum;
764 uint32_t repsize;
765 int aflag, lflag, nflag, rflag, Rflag, wflag;
766 int ch, hidfd, x;
767 uint8_t *repdata;
768 report_desc_t repdesc;
769 char devnamebuf[PATH_MAX1024];
770 struct Susbvar variables[128];
771
772 wflag = aflag = nflag = verbose = rflag = Rflag = lflag = 0;
773 dev = NULL((void *)0);
774 table = NULL((void *)0);
775 while ((ch = getopt(argc, argv, "af:lnRrt:vw")) != -1) {
1
Assuming the condition is true
2
Loop condition is true. Entering loop body
4
Execution continues on line 775
5
Assuming the condition is false
6
Loop condition is false. Execution continues on line 809
776 switch (ch) {
3
Control jumps to 'case 102:' at line 780
777 case 'a':
778 aflag = 1;
779 break;
780 case 'f':
781 dev = optarg;
782 break;
783 case 'l':
784 lflag = 1;
785 break;
786 case 'n':
787 nflag = 1;
788 break;
789 case 'r':
790 rflag = 1;
791 break;
792 case 'R':
793 Rflag = 1;
794 break;
795 case 't':
796 table = optarg;
797 break;
798 case 'v':
799 verbose++;
800 break;
801 case 'w':
802 wflag = 1;
803 break;
804 default:
805 usage();
806 /* NOTREACHED */
807 }
808 }
809 argc -= optind;
810 argv += optind;
811 if (dev == NULL((void *)0) || (lflag
7.1
'lflag' is 0
&& (wflag || rflag || Rflag)) ||
7
Assuming 'dev' is not equal to NULL
812 (rflag
7.2
'rflag' is 0
&& Rflag)) {
813 /*
814 * No device specified, or attempting to loop and set
815 * or dump report at the same time
816 */
817 usage();
818 /* NOTREACHED */
819 }
820
821 if (argc == 0 && rflag == 0)
8
Assuming 'argc' is not equal to 0
822 aflag = 1;
823
824 for (varnum = 0; varnum < (size_t)argc; varnum++) {
9
Assuming 'varnum' is < 'argc'
10
Loop condition is true. Entering loop body
16
Assuming 'varnum' is >= 'argc'
825 char const *name, *valuesep;
826 struct Susbvar *svar;
827
828 svar = &variables[varnum];
829 name = argv[varnum];
830 valuesep = strchr(name, DELIM_SET'=');
831
832 svar->variable = name;
833 svar->mflags = 0;
834
835 if (valuesep == NULL((void *)0)) {
11
Assuming 'valuesep' is equal to NULL
12
Taking true branch
836 /* Read variable */
837 if (wflag
12.1
'wflag' is 0
)
13
Taking false branch
838 errx(1, "Must not specify -w to read variables");
839 svar->value = NULL((void *)0);
840 svar->varlen = strlen(name);
841
842 if (nflag
13.1
'nflag' is 0
) {
14
Taking false branch
843 /* Display value of variable only */
844 svar->opfunc = varop_value;
845 } else {
846 /* Display name and value of variable */
847 svar->opfunc = varop_display;
848
849 if (verbose
14.1
'verbose' is < 1
>= 1)
15
Taking false branch
850 /* Show page names in verbose modes */
851 svar->mflags |= MATCH_SHOWPAGENAME(1 << 5);
852 }
853 } else {
854 /* Write variable */
855 if (!wflag)
856 errx(2, "Must specify -w to set variables");
857 svar->mflags |= MATCH_WRITABLE(1 << 7);
858 if (verbose >= 1)
859 /*
860 * Allow displaying of set value in
861 * verbose mode. This isn't
862 * particularly useful though, so
863 * don't bother documenting it.
864 */
865 svar->mflags |= MATCH_SHOWVALUES(1 << 8);
866 svar->varlen = valuesep - name;
867 svar->value = valuesep + 1;
868 svar->opfunc = varop_modify;
869 }
870 }
871
872 if (aflag
16.1
'aflag' is 0
|| rflag
16.2
'rflag' is 0
) {
17
Taking false branch
873 struct Susbvar *svar;
874
875 svar = &variables[varnum++];
876
877 svar->variable = NULL((void *)0);
878 svar->mflags = MATCH_ALL(1 << 0);
879
880 if (rflag) {
881 /*
882 * Dump report descriptor. Do dump collection
883 * items also, and hint that it won't be
884 * necessary to get the item status.
885 */
886 svar->opfunc = varop_report;
887 svar->mflags |= MATCH_COLLECTIONS(1 << 1) | MATCH_NODATA(1 << 2);
888
889 switch (verbose) {
890 default:
891 /* Level 2: Show item numerics and constants */
892 svar->mflags |= MATCH_SHOWNUMERIC(1 << 6);
893 /* FALLTHROUGH */
894 case 1:
895 /* Level 1: Just show constants */
896 svar->mflags |= MATCH_CONSTANTS(1 << 3);
897 /* FALLTHROUGH */
898 case 0:
899 break;
900 }
901 } else {
902 /* Display name and value of variable */
903 svar->opfunc = varop_display;
904
905 switch (verbose) {
906 default:
907 /* Level 2: Show constants and page names */
908 svar->mflags |= MATCH_CONSTANTS(1 << 3);
909 /* FALLTHROUGH */
910 case 1:
911 /* Level 1: Just show page names */
912 svar->mflags |= MATCH_SHOWPAGENAME(1 << 5);
913 /* FALLTHROUGH */
914 case 0:
915 break;
916 }
917 }
918 }
919
920 if (varnum
17.1
'varnum' is not equal to 0
== 0) {
18
Taking false branch
921 /* Nothing to do... Display usage information. */
922 usage();
923 /* NOTREACHED */
924 }
925
926 if (hid_start(table) == -1)
19
Assuming the condition is false
20
Taking false branch
927 errx(1, "hid_init");
928
929 if (dev[0] != '/') {
21
Assuming the condition is false
22
Taking false branch
930 snprintf(devnamebuf, sizeof(devnamebuf), "/dev/%s%s",
931 isdigit((unsigned char)dev[0]) ? "uhid" : "", dev);
932 dev = devnamebuf;
933 }
934
935 hidfd = open(dev, wflag
22.1
'wflag' is 0
? O_RDWR0x0002 : O_RDONLY0x0000);
23
'?' condition is false
936 if (hidfd == -1)
24
Assuming the condition is false
25
Taking false branch
937 err(1, "%s", dev);
938
939 if (unveil("/", "") == -1)
26
Assuming the condition is false
27
Taking false branch
940 err(1, "unveil /");
941 if (unveil(NULL((void *)0), NULL((void *)0)) == -1)
28
Assuming the condition is false
29
Taking false branch
942 err(1, "unveil");
943
944 if (ioctl(hidfd, USB_GET_REPORT_ID((unsigned long)0x40000000 | ((sizeof(int) & 0x1fff) <<
16) | ((('U')) << 8) | ((25)))
, &reportid) == -1
)
30
Assuming the condition is true
31
Taking true branch
945 reportid = -1;
946 if (verbose
31.1
'verbose' is <= 1
> 1)
32
Taking false branch
947 printf("report ID=%d\n", reportid);
948 repdesc = hid_get_report_desc(hidfd);
949 if (repdesc == 0)
33
Assuming 'repdesc' is not equal to null
34
Taking false branch
950 errx(1, "USB_GET_REPORT_DESC");
951
952 if (Rflag
34.1
'Rflag' is 0
) {
35
Taking false branch
953 hid_get_report_desc_data(repdesc, &repdata, &repsize);
954
955 for (x = 0; x < repsize; x++)
956 printf("%s0x%02x", x > 0 ? " " : "", repdata[x]);
957 printf("\n");
958 }
959
960 if (lflag
35.1
'lflag' is 0
) {
36
Taking false branch
961 devloop(hidfd, repdesc, variables, varnum);
962 /* NOTREACHED */
963 }
964
965 if (rflag
36.1
'rflag' is 0
)
37
Taking false branch
966 /* Report mode header */
967 printf("Report descriptor:\n");
968
969 if (!Rflag
37.1
'Rflag' is 0
)
38
Taking true branch
970 devshow(hidfd, repdesc, variables, varnum,
39
Calling 'devshow'
971 1 << hid_input |
972 1 << hid_output |
973 1 << hid_feature);
974
975 if (rflag) {
976 /* Report mode trailer */
977 size_t repindex;
978 for (repindex = 0;
979 repindex < (sizeof(reptoparam) / sizeof(*reptoparam));
980 repindex++) {
981 int size;
982 size = hid_report_size(repdesc,
983 reptoparam[repindex].hid_kind,
984 reportid);
985 printf("Total %7s size %d bytes\n",
986 reptoparam[repindex].name, size);
987 }
988 }
989
990 hid_dispose_report_desc(repdesc);
991 exit(0);
992 /* NOTREACHED */
993}