Bug Summary

File:src/usr.bin/usbhidctl/usbhid.c
Warning:line 432, column 30
Access to field 'ucr_report' results in a dereference of a null pointer (loaded from field 'buffer')

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 usbhid.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/usbhidctl/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/usbhidctl/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/usbhidctl/usbhid.c
1/* $OpenBSD: usbhid.c,v 1.18 2021/12/15 11:21:35 mestre 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)
67
Assuming 'reptsize' is >= 0
68
Taking false branch
393 errx(1, "Negative report size");
394 report->size = reptsize;
395
396 if (report->size > 0) {
69
Assuming field 'size' is <= 0
70
Taking false branch
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);
71
Null pointer value stored to field 'buffer'
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
64.1
Field 'status' is equal to srs_uninit
== srs_uninit) {
65
Taking true branch
417 allocreport(report, rd, repindex);
66
Calling 'allocreport'
72
Returning from 'allocreport'
418 if (report->size
72.1
Field 'size' is equal to 0
== 0)
73
Taking true branch
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
80.1
Field 'status' is equal to srs_dirty
== srs_dirty) {
81
Taking true branch
432 report->buffer->ucr_report = reptoparam[repindex].uhid_report;
82
Access to field 'ucr_report' results in a dereference of a null pointer (loaded from field 'buffer')
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
442/* ARGSUSED1 */
443static int
444varop_value(struct hid_item *item, struct Susbvar *var,
445 u_int32_t const *collist, size_t collen, u_char *buf)
446{
447 printf("%d\n", hid_get_data(buf, item));
448 return 0;
449}
450
451/* ARGSUSED1 */
452static int
453varop_display(struct hid_item *item, struct Susbvar *var,
454 u_int32_t const *collist, size_t collen, u_char *buf)
455{
456 size_t colitem;
457 int val, i;
458
459 for (i = 0; i < item->report_count; i++) {
460 for (colitem = 0; colitem < collen; colitem++) {
461 if (var->mflags & MATCH_SHOWPAGENAME(1 << 5))
462 printf("%s:",
463 hid_usage_page(HID_PAGE(collist[colitem])(((collist[colitem]) >> 16) & 0xffff)));
464 printf("%s.", hid_usage_in_page(collist[colitem]));
465 }
466 if (var->mflags & MATCH_SHOWPAGENAME(1 << 5))
467 printf("%s:", hid_usage_page(HID_PAGE(item->usage)(((item->usage) >> 16) & 0xffff)));
468 val = hid_get_data(buf, item);
469 item->pos += item->report_size;
470 if (item->usage_minimum != 0 || item->usage_maximum != 0) {
471 val += item->usage_minimum;
472 printf("%s=1", hid_usage_in_page(val));
473 } else {
474 printf("%s=%d%s", hid_usage_in_page(item->usage),
475 val, item->flags & HIO_CONST0x001 ? " (const)" : "");
476 }
477 if (item->report_count > 1)
478 printf(" [%d]", i);
479 printf("\n");
480 }
481 return 0;
482}
483
484/* ARGSUSED1 */
485static int
486varop_modify(struct hid_item *item, struct Susbvar *var,
487 u_int32_t const *collist, size_t collen, u_char *buf)
488{
489 u_int dataval;
490
491 dataval = (u_int)strtol(var->value, NULL((void *)0), 10);
492
493 hid_set_data(buf, item, dataval);
494
495 if (var->mflags & MATCH_SHOWVALUES(1 << 8))
496 /* Display set value */
497 varop_display(item, var, collist, collen, buf);
498
499 return 1;
500}
501
502static void
503reportitem(char const *label, struct hid_item const *item, unsigned int mflags)
504{
505 int isconst = item->flags & HIO_CONST0x001,
506 isvar = item->flags & HIO_VARIABLE0x002;
507 printf("%s size=%d count=%d%s%s page=%s", label,
508 item->report_size, item->report_count,
509 isconst ? " Const" : "",
510 !isvar && !isconst ? " Array" : "",
511 hid_usage_page(HID_PAGE(item->usage)(((item->usage) >> 16) & 0xffff)));
512 if (item->usage_minimum != 0 || item->usage_maximum != 0) {
513 printf(" usage=%s..%s", hid_usage_in_page(item->usage_minimum),
514 hid_usage_in_page(item->usage_maximum));
515 if (mflags & MATCH_SHOWNUMERIC(1 << 6))
516 printf(" (%u:0x%x..%u:0x%x)",
517 HID_PAGE(item->usage_minimum)(((item->usage_minimum) >> 16) & 0xffff),
518 HID_USAGE(item->usage_minimum)((item->usage_minimum) & 0xffff),
519 HID_PAGE(item->usage_maximum)(((item->usage_maximum) >> 16) & 0xffff),
520 HID_USAGE(item->usage_maximum)((item->usage_maximum) & 0xffff));
521 } else {
522 printf(" usage=%s", hid_usage_in_page(item->usage));
523 if (mflags & MATCH_SHOWNUMERIC(1 << 6))
524 printf(" (%u:0x%x)",
525 HID_PAGE(item->usage)(((item->usage) >> 16) & 0xffff), HID_USAGE(item->usage)((item->usage) & 0xffff));
526 }
527 printf(", logical range %d..%d",
528 item->logical_minimum, item->logical_maximum);
529 if (item->physical_minimum != item->physical_maximum)
530 printf(", physical range %d..%d",
531 item->physical_minimum, item->physical_maximum);
532 if (item->unit)
533 printf(", unit=0x%02x exp=%d", item->unit,
534 item->unit_exponent);
535 printf("\n");
536}
537
538/* ARGSUSED1 */
539static int
540varop_report(struct hid_item *item, struct Susbvar *var,
541 u_int32_t const *collist, size_t collen, u_char *buf)
542{
543 switch (item->kind) {
544 case hid_collection:
545 printf("Collection page=%s usage=%s",
546 hid_usage_page(HID_PAGE(item->usage)(((item->usage) >> 16) & 0xffff)),
547 hid_usage_in_page(item->usage));
548 if (var->mflags & MATCH_SHOWNUMERIC(1 << 6))
549 printf(" (%u:0x%x)\n",
550 HID_PAGE(item->usage)(((item->usage) >> 16) & 0xffff), HID_USAGE(item->usage)((item->usage) & 0xffff));
551 else
552 printf("\n");
553 break;
554 case hid_endcollection:
555 printf("End collection\n");
556 break;
557 case hid_input:
558 reportitem("Input ", item, var->mflags);
559 break;
560 case hid_output:
561 reportitem("Output ", item, var->mflags);
562 break;
563 case hid_feature:
564 reportitem("Feature", item, var->mflags);
565 break;
566 }
567
568 return 0;
569}
570
571static void
572devloop(int hidfd, report_desc_t rd, struct Susbvar *varlist, size_t vlsize)
573{
574 u_char *dbuf;
575 struct hid_data *hdata;
576 size_t collind, dlen;
577 struct hid_item hitem;
578 u_int32_t colls[128];
579 struct Sreport inreport;
580
581 allocreport(&inreport, rd, REPORT_INPUT0);
582
583 if (inreport.size <= 0)
584 errx(1, "Input report descriptor invalid length");
585
586 dlen = inreport.size;
587 dbuf = inreport.buffer->ucr_data;
588
589 for (;;) {
590 ssize_t readlen;
591
592 readlen = read(hidfd, dbuf, dlen);
593 if (readlen == -1)
594 err(1, "Device read error");
595 if (dlen != (size_t)readlen)
596 errx(1, "Unexpected response length: %lu != %lu",
597 (unsigned long)readlen, (unsigned long)dlen);
598
599 collind = 0;
600 hdata = hid_start_parse(rd, 1 << hid_input, reportid);
601 if (hdata == NULL((void *)0))
602 errx(1, "Failed to start parser");
603
604 while (hid_get_item(hdata, &hitem)) {
605 struct Susbvar *matchvar;
606
607 switch (hitem.kind) {
608 case hid_collection:
609 if (collind >= (sizeof(colls) / sizeof(*colls)))
610 errx(1, "Excessive nested collections");
611 colls[collind++] = hitem.usage;
612 break;
613 case hid_endcollection:
614 if (collind == 0)
615 errx(1, "Excessive collection ends");
616 collind--;
617 break;
618 case hid_input:
619 break;
620 case hid_output:
621 case hid_feature:
622 errx(1, "Unexpected non-input item returned");
623 }
624
625 if (reportid != -1 && hitem.report_ID != reportid)
626 continue;
627
628 matchvar = hidmatch(colls, collind, &hitem,
629 varlist, vlsize);
630
631 if (matchvar != NULL((void *)0))
632 matchvar->opfunc(&hitem, matchvar,
633 colls, collind,
634 inreport.buffer->ucr_data);
635 }
636 hid_end_parse(hdata);
637 printf("\n");
638 }
639 /* NOTREACHED */
640}
641
642static void
643devshow(int hidfd, report_desc_t rd, struct Susbvar *varlist, size_t vlsize,
644 int kindset)
645{
646 struct hid_data *hdata;
647 size_t collind, repind, vlind;
648 struct hid_item hitem;
649 u_int32_t colls[128];
650 struct Sreport reports[REPORT_MAXVAL2 + 1];
651
652
653 for (repind = 0; repind < (sizeof(reports) / sizeof(*reports));
41
Loop condition is true. Entering loop body
42
Loop condition is true. Entering loop body
43
Loop condition is true. Entering loop body
44
Loop condition is false. Execution continues on line 659
654 repind++) {
655 reports[repind].status = srs_uninit;
656 reports[repind].buffer = NULL((void *)0);
657 }
658
659 collind = 0;
660 hdata = hid_start_parse(rd, kindset, reportid);
661 if (hdata == NULL((void *)0))
45
Assuming 'hdata' is not equal to NULL
46
Taking false branch
662 errx(1, "Failed to start parser");
663
664 while (hid_get_item(hdata, &hitem)) {
47
Loop condition is true. Entering loop body
57
Loop condition is true. Entering loop body
78
Loop condition is false. Execution continues on line 722
665 struct Susbvar *matchvar;
666 int repindex;
667
668 if (verbose
47.1
'verbose' is <= 3
57.1
'verbose' is <= 3
> 3)
48
Taking false branch
58
Taking false branch
669 printf("item: kind=%d repid=%d usage=0x%x\n",
670 hitem.kind, hitem.report_ID, hitem.usage);
671 repindex = -1;
672 switch (hitem.kind) {
49
Control jumps to 'case hid_feature:' at line 689
59
Control jumps to 'case hid_input:' at line 683
673 case hid_collection:
674 if (collind >= (sizeof(colls) / sizeof(*colls)))
675 errx(1, "Excessive nested collections");
676 colls[collind++] = hitem.usage;
677 break;
678 case hid_endcollection:
679 if (collind == 0)
680 errx(1, "Excessive collection ends");
681 collind--;
682 break;
683 case hid_input:
684 repindex = REPORT_INPUT0;
685 break;
60
Execution continues on line 694
686 case hid_output:
687 repindex = REPORT_OUTPUT1;
688 break;
689 case hid_feature:
690 repindex = REPORT_FEATURE2;
691 break;
50
Execution continues on line 694
692 }
693
694 if (reportid != -1 && hitem.report_ID != reportid)
51
Assuming the condition is false
695 continue;
696
697 matchvar = hidmatch(colls, collind, &hitem, varlist, vlsize);
698
699 if (matchvar
51.1
'matchvar' is not equal to NULL
60.1
'matchvar' is not equal to NULL
!= NULL((void *)0)) {
52
Taking true branch
61
Taking true branch
700 u_char *bufdata;
701 struct Sreport *repptr;
702
703 matchvar->mflags |= MATCH_WASMATCHED(1 << 4);
704
705 if (repindex
52.1
'repindex' is >= 0
61.1
'repindex' is >= 0
>= 0)
53
Taking true branch
62
Taking true branch
706 repptr = &reports[repindex];
707 else
708 repptr = NULL((void *)0);
709
710 if (repptr
53.1
'repptr' is not equal to NULL
62.1
'repptr' is not equal to NULL
!= NULL((void *)0) &&
54
Taking true branch
63
Taking true branch
711 !(matchvar->mflags & MATCH_NODATA(1 << 2)))
712 getreport(repptr, hidfd, rd, repindex);
64
Calling 'getreport'
74
Returning from 'getreport'
713
714 bufdata = (repptr
54.1
'repptr' is not equal to NULL
74.1
'repptr' is not equal to NULL
== NULL((void *)0) || repptr->buffer
54.2
Field 'buffer' is equal to NULL
74.2
Field 'buffer' is equal to NULL
== NULL((void *)0)) ?
55
'?' condition is true
75
'?' condition is true
715 NULL((void *)0) : repptr->buffer->ucr_data;
716
717 if (matchvar->opfunc(&hitem, matchvar, colls, collind,
56
Taking false branch
76
Assuming the condition is true
77
Taking true branch
718 bufdata))
719 repptr->status = srs_dirty;
720 }
721 }
722 hid_end_parse(hdata);
723
724 for (repind = 0; repind < (sizeof(reports) / sizeof(*reports));
79
Loop condition is true. Entering loop body
725 repind++) {
726 setreport(&reports[repind], hidfd, repind);
80
Calling 'setreport'
727 freereport(&reports[repind]);
728 }
729
730 /* Warn about any items that we couldn't find a match for */
731 for (vlind = 0; vlind < vlsize; vlind++) {
732 struct Susbvar *var;
733
734 var = &varlist[vlind];
735
736 if (var->variable != NULL((void *)0) &&
737 !(var->mflags & MATCH_WASMATCHED(1 << 4)))
738 warnx("Failed to match: %.*s", (int)var->varlen,
739 var->variable);
740 }
741}
742
743static void
744usage(void)
745{
746 extern char *__progname;
747
748 fprintf(stderr(&__sF[2]), "usage: %s -f device [-t table] [-alv]\n",
749 __progname);
750 fprintf(stderr(&__sF[2]), " %s -f device [-t table] [-v] -r\n",
751 __progname);
752 fprintf(stderr(&__sF[2]),
753 " %s -f device [-t table] [-lnv] name ...\n",
754 __progname);
755 fprintf(stderr(&__sF[2]),
756 " %s -f device [-t table] -w name=value ...\n",
757 __progname);
758 fprintf(stderr(&__sF[2]), " %s -f device -R\n", __progname);
759 exit(1);
760}
761
762int
763main(int argc, char **argv)
764{
765 char const *dev;
766 char const *table;
767 size_t varnum;
768 uint32_t repsize;
769 int aflag, lflag, nflag, rflag, Rflag, wflag;
770 int ch, hidfd, x;
771 uint8_t *repdata;
772 report_desc_t repdesc;
773 char devnamebuf[PATH_MAX1024];
774 struct Susbvar variables[128];
775
776 wflag = aflag = nflag = verbose = rflag = Rflag = lflag = 0;
777 dev = NULL((void *)0);
778 table = NULL((void *)0);
779 while ((ch = getopt(argc, argv, "?af:lnRrt:vw")) != -1) {
1
Assuming the condition is true
2
Loop condition is true. Entering loop body
5
Assuming the condition is false
6
Loop condition is false. Execution continues on line 814
780 switch (ch) {
3
Control jumps to 'case 102:' at line 784
781 case 'a':
782 aflag = 1;
783 break;
784 case 'f':
785 dev = optarg;
786 break;
4
Execution continues on line 779
787 case 'l':
788 lflag = 1;
789 break;
790 case 'n':
791 nflag = 1;
792 break;
793 case 'r':
794 rflag = 1;
795 break;
796 case 'R':
797 Rflag = 1;
798 break;
799 case 't':
800 table = optarg;
801 break;
802 case 'v':
803 verbose++;
804 break;
805 case 'w':
806 wflag = 1;
807 break;
808 case '?':
809 default:
810 usage();
811 /* NOTREACHED */
812 }
813 }
814 argc -= optind;
815 argv += optind;
816 if (dev == NULL((void *)0) || (lflag
7.1
'lflag' is 0
&& (wflag || rflag || Rflag)) ||
7
Assuming 'dev' is not equal to NULL
817 (rflag
7.2
'rflag' is 0
&& Rflag)) {
818 /*
819 * No device specified, or attempting to loop and set
820 * or dump report at the same time
821 */
822 usage();
823 /* NOTREACHED */
824 }
825
826 if (argc == 0 && rflag == 0)
8
Assuming 'argc' is not equal to 0
827 aflag = 1;
828
829 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'
17
Loop condition is false. Execution continues on line 877
830 char const *name, *valuesep;
831 struct Susbvar *svar;
832
833 svar = &variables[varnum];
834 name = argv[varnum];
835 valuesep = strchr(name, DELIM_SET'=');
836
837 svar->variable = name;
838 svar->mflags = 0;
839
840 if (valuesep == NULL((void *)0)) {
11
Assuming 'valuesep' is equal to NULL
12
Taking true branch
841 /* Read variable */
842 if (wflag
12.1
'wflag' is 0
)
13
Taking false branch
843 errx(1, "Must not specify -w to read variables");
844 svar->value = NULL((void *)0);
845 svar->varlen = strlen(name);
846
847 if (nflag
13.1
'nflag' is 0
) {
14
Taking false branch
848 /* Display value of variable only */
849 svar->opfunc = varop_value;
850 } else {
851 /* Display name and value of variable */
852 svar->opfunc = varop_display;
853
854 if (verbose
14.1
'verbose' is < 1
>= 1)
15
Taking false branch
855 /* Show page names in verbose modes */
856 svar->mflags |= MATCH_SHOWPAGENAME(1 << 5);
857 }
858 } else {
859 /* Write variable */
860 if (!wflag)
861 errx(2, "Must specify -w to set variables");
862 svar->mflags |= MATCH_WRITABLE(1 << 7);
863 if (verbose >= 1)
864 /*
865 * Allow displaying of set value in
866 * verbose mode. This isn't
867 * particularly useful though, so
868 * don't bother documenting it.
869 */
870 svar->mflags |= MATCH_SHOWVALUES(1 << 8);
871 svar->varlen = valuesep - name;
872 svar->value = valuesep + 1;
873 svar->opfunc = varop_modify;
874 }
875 }
876
877 if (aflag
17.1
'aflag' is 0
|| rflag
17.2
'rflag' is 0
) {
18
Taking false branch
878 struct Susbvar *svar;
879
880 svar = &variables[varnum++];
881
882 svar->variable = NULL((void *)0);
883 svar->mflags = MATCH_ALL(1 << 0);
884
885 if (rflag) {
886 /*
887 * Dump report descriptor. Do dump collection
888 * items also, and hint that it won't be
889 * necessary to get the item status.
890 */
891 svar->opfunc = varop_report;
892 svar->mflags |= MATCH_COLLECTIONS(1 << 1) | MATCH_NODATA(1 << 2);
893
894 switch (verbose) {
895 default:
896 /* Level 2: Show item numerics and constants */
897 svar->mflags |= MATCH_SHOWNUMERIC(1 << 6);
898 /* FALLTHROUGH */
899 case 1:
900 /* Level 1: Just show constants */
901 svar->mflags |= MATCH_CONSTANTS(1 << 3);
902 /* FALLTHROUGH */
903 case 0:
904 break;
905 }
906 } else {
907 /* Display name and value of variable */
908 svar->opfunc = varop_display;
909
910 switch (verbose) {
911 default:
912 /* Level 2: Show constants and page names */
913 svar->mflags |= MATCH_CONSTANTS(1 << 3);
914 /* FALLTHROUGH */
915 case 1:
916 /* Level 1: Just show page names */
917 svar->mflags |= MATCH_SHOWPAGENAME(1 << 5);
918 /* FALLTHROUGH */
919 case 0:
920 break;
921 }
922 }
923 }
924
925 if (varnum
18.1
'varnum' is not equal to 0
== 0) {
19
Taking false branch
926 /* Nothing to do... Display usage information. */
927 usage();
928 /* NOTREACHED */
929 }
930
931 if (hid_start(table) == -1)
20
Assuming the condition is false
21
Taking false branch
932 errx(1, "hid_init");
933
934 if (dev[0] != '/') {
22
Assuming the condition is false
23
Taking false branch
935 snprintf(devnamebuf, sizeof(devnamebuf), "/dev/%s%s",
936 isdigit((unsigned char)dev[0]) ? "uhid" : "", dev);
937 dev = devnamebuf;
938 }
939
940 hidfd = open(dev, wflag
23.1
'wflag' is 0
? O_RDWR0x0002 : O_RDONLY0x0000);
24
'?' condition is false
941 if (hidfd == -1)
25
Assuming the condition is false
26
Taking false branch
942 err(1, "%s", dev);
943
944 if (unveil("/", "") == -1)
27
Assuming the condition is false
28
Taking false branch
945 err(1, "unveil /");
946 if (unveil(NULL((void *)0), NULL((void *)0)) == -1)
29
Assuming the condition is false
30
Taking false branch
947 err(1, "unveil");
948
949 if (ioctl(hidfd, USB_GET_REPORT_ID((unsigned long)0x40000000 | ((sizeof(int) & 0x1fff) <<
16) | ((('U')) << 8) | ((25)))
, &reportid) == -1
)
31
Assuming the condition is false
32
Taking false branch
950 reportid = -1;
951 if (verbose
32.1
'verbose' is <= 1
> 1)
33
Taking false branch
952 printf("report ID=%d\n", reportid);
953 repdesc = hid_get_report_desc(hidfd);
954 if (repdesc == 0)
34
Assuming 'repdesc' is not equal to null
35
Taking false branch
955 errx(1, "USB_GET_REPORT_DESC");
956
957 if (Rflag
35.1
'Rflag' is 0
) {
36
Taking false branch
958 hid_get_report_desc_data(repdesc, &repdata, &repsize);
959
960 for (x = 0; x < repsize; x++)
961 printf("%s0x%02x", x > 0 ? " " : "", repdata[x]);
962 printf("\n");
963 }
964
965 if (lflag
36.1
'lflag' is 0
) {
37
Taking false branch
966 devloop(hidfd, repdesc, variables, varnum);
967 /* NOTREACHED */
968 }
969
970 if (rflag
37.1
'rflag' is 0
)
38
Taking false branch
971 /* Report mode header */
972 printf("Report descriptor:\n");
973
974 if (!Rflag
38.1
'Rflag' is 0
)
39
Taking true branch
975 devshow(hidfd, repdesc, variables, varnum,
40
Calling 'devshow'
976 1 << hid_input |
977 1 << hid_output |
978 1 << hid_feature);
979
980 if (rflag) {
981 /* Report mode trailer */
982 size_t repindex;
983 for (repindex = 0;
984 repindex < (sizeof(reptoparam) / sizeof(*reptoparam));
985 repindex++) {
986 int size;
987 size = hid_report_size(repdesc,
988 reptoparam[repindex].hid_kind,
989 reportid);
990 printf("Total %7s size %d bytes\n",
991 reptoparam[repindex].name, size);
992 }
993 }
994
995 hid_dispose_report_desc(repdesc);
996 exit(0);
997 /* NOTREACHED */
998}