1 // Copyright 2018 The Fuchsia Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include <fbl/alloc_checker.h>
6 #include <fbl/auto_call.h>
7 #include <pretty/hexdump.h>
8
9 #include <utility>
10
11 #include "debug-logging.h"
12 #include "usb-audio-descriptors.h"
13
14 namespace audio {
15 namespace usb {
16
~DescriptorListMemory()17 DescriptorListMemory::~DescriptorListMemory() {
18 // According to docs in the header, data allocated using
19 // usb_get_descriptor_list should be free'ed using a standard C "free"
20 if (data_ != nullptr) {
21 free(data_);
22 }
23 }
24
Create(usb_protocol_t * proto)25 fbl::RefPtr<DescriptorListMemory> DescriptorListMemory::Create(usb_protocol_t* proto) {
26 ZX_DEBUG_ASSERT(proto != nullptr);
27
28 fbl::AllocChecker ac;
29 auto ret = fbl::AdoptRef(new (&ac) DescriptorListMemory());
30 if (!ac.check()) {
31 GLOBAL_LOG(ERROR, "Failed to allocate descriptor list memory!");
32 return nullptr;
33 }
34
35 size_t desc_length = usb_get_descriptors_length(proto);
36 ret->data_ = malloc(desc_length);
37 if (!ret->data_) {
38 return nullptr;
39 }
40 usb_get_descriptors(proto, ret->data_, desc_length, &ret->size_);
41
42 if (zxlog_level_enabled(SPEW)) {
43 GLOBAL_LOG(SPEW, "Descriptor List is %zu bytes long\n", ret->size_);
44 hexdump8_ex(ret->data_, ret->size_, 0u);
45 }
46
47 return ret;
48 }
49
Iterator(fbl::RefPtr<DescriptorListMemory> mem)50 DescriptorListMemory::Iterator::Iterator(fbl::RefPtr<DescriptorListMemory> mem)
51 : mem_(std::move(mem)) {
52 ZX_DEBUG_ASSERT(mem_ != nullptr);
53 // Make sure our offset is valid, or go ahead and invalidate it right from
54 // the start.
55 ValidateOffset();
56 }
57
Next()58 bool DescriptorListMemory::Iterator::Next() {
59 auto h = hdr();
60
61 // Have we already run out of headers?
62 if (h == nullptr) {
63 return false;
64 }
65
66 // Advance to the next header, then validate our offset. Note that there is
67 // no overflow check here. If we overflow a 64 bit size_t, the implication
68 // is that we are holding a USB descriptor list in RAM whose size is within
69 // 256 bytes of our entire 64 bit address space. This really should be
70 // impossible, so we don't bother to check.
71 offset_ += h->bLength;
72 return ValidateOffset();
73 }
74
ValidateOffset()75 bool DescriptorListMemory::Iterator::ValidateOffset() {
76 ZX_DEBUG_ASSERT(offset_ <= mem_->size());
77 size_t space = mem_->size() - offset_;
78
79 if (!space) {
80 return false;
81 }
82
83 // If anything goes wrong from here on out, make sure to invalidate our offset.
84 auto cleanup = fbl::MakeAutoCall([this] { offset_ = mem_->size(); });
85
86 if (space < sizeof(usb_descriptor_header_t)) {
87 GLOBAL_LOG(WARN,
88 "Insufficient space at offset %zu to contain even the most basic USB descriptor "
89 "(space needed %zu, space left %zu)\n",
90 offset_, sizeof(usb_descriptor_header_t), space);
91 return false;
92 }
93
94 auto tmp = reinterpret_cast<uintptr_t>(mem_->data());
95 auto h = reinterpret_cast<const usb_descriptor_header_t*>(tmp + offset_);
96 if (h->bLength > space) {
97 GLOBAL_LOG(WARN,
98 "Malformed USB descriptor header (type %u) at offset %zu. "
99 "Header indicates that it is %u bytes long, but there %zu bytes remaining\n",
100 h->bDescriptorType,
101 offset_,
102 h->bLength,
103 space);
104 return false;
105 }
106
107 GLOBAL_LOG(SPEW, "Found Descriptor [type 0x%02x, len 0x%02x] at offset 0x%zx/0x%zx\n",
108 h->bDescriptorType, h->bLength, offset_, mem_->size());
109
110 cleanup.cancel();
111 return true;
112 }
113
114 } // namespace usb
115 } // namespace audio
116