summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJavid Khan <[email protected]>2026-07-07 05:26:03 +0530
committerJavid Khan <[email protected]>2026-07-07 05:26:03 +0530
commitbe8e6990cee5f7c2d5f09fc8494a1159c5c7c5a7 (patch)
treed0424cfc8409b15f687d63507d653cf901bd2a62
parenteebd0c1c1c04d68a56eac09f56ed16e0ee570a0d (diff)
gate acm_open descriptor bounds behind CFG_TUH_VALIDATION_LEVEL
add a three-level TUSB_VALIDATION_NONE/BASIC/STRICT knob and default CFG_TUH_VALIDATION_LEVEL to BASIC, then make acm_open the first user so the enumeration bounds checks compile out at NONE for trusted-device setups and stay on by default.
-rw-r--r--src/class/cdc/cdc_host.c28
-rw-r--r--src/tusb_option.h13
2 files changed, 33 insertions, 8 deletions
diff --git a/src/class/cdc/cdc_host.c b/src/class/cdc/cdc_host.c
index 6045350a8..d1cbc2924 100644
--- a/src/class/cdc/cdc_host.c
+++ b/src/class/cdc/cdc_host.c
@@ -714,11 +714,21 @@ bool cdch_xfer_cb(uint8_t daddr, uint8_t ep_addr, xfer_result_t event, uint32_t
//--------------------------------------------------------------------+
// Enumeration
//--------------------------------------------------------------------+
+
+ // Descriptor-walk hardening gated by CFG_TUH_VALIDATION_LEVEL: at NONE the guards collapse to a pass so
+ // trusted-device setups pay no code size; at BASIC (default) the walk stays inside the enumeration buffer.
+ #if CFG_TUH_VALIDATION_LEVEL >= TUSB_VALIDATION_BASIC
+ #define TU_DESC_VALIDATE(_cond) (_cond)
+ #else
+ #define TU_DESC_VALIDATE(_cond) (true)
+ #endif
+
static bool open_ep_stream_pair(cdch_interface_t *p_cdc, tusb_desc_endpoint_t const *desc_ep) {
for (size_t i = 0; i < 2; i++) {
// pin bLength so tu_desc_next() below cannot walk the second endpoint past a caller-checked bound
- TU_ASSERT(sizeof(tusb_desc_endpoint_t) == desc_ep->bLength &&
- TUSB_DESC_ENDPOINT == desc_ep->bDescriptorType && TUSB_XFER_BULK == desc_ep->bmAttributes.xfer, 0);
+ TU_ASSERT(TU_DESC_VALIDATE(sizeof(tusb_desc_endpoint_t) == desc_ep->bLength) &&
+ TUSB_DESC_ENDPOINT == desc_ep->bDescriptorType && TUSB_XFER_BULK == desc_ep->bmAttributes.xfer,
+ 0);
TU_ASSERT(tuh_edpt_open(p_cdc->daddr, desc_ep));
const uint8_t ep_dir = tu_edpt_dir(desc_ep->bEndpointAddress);
tu_edpt_stream_t *stream = (ep_dir == TUSB_DIR_IN) ? &p_cdc->stream.rx : &p_cdc->stream.tx;
@@ -1024,9 +1034,10 @@ static uint16_t acm_open(uint8_t daddr, const tusb_desc_interface_t *itf_desc, u
// Communication Functional Descriptors
// need the 3-byte header (bLength/bDescriptorType/bDescriptorSubType) in bounds before reading it, and a
// bLength >= 3 both keeps those reads valid and stops a zero-length descriptor from spinning the walk
- while (p_desc + 3 <= desc_end && TUSB_DESC_CS_INTERFACE == tu_desc_type(p_desc) && tu_desc_len(p_desc) >= 3) {
+ while ((p_desc < desc_end) && TU_DESC_VALIDATE(p_desc + 3 <= desc_end) &&
+ TUSB_DESC_CS_INTERFACE == tu_desc_type(p_desc) && TU_DESC_VALIDATE(tu_desc_len(p_desc) >= 3)) {
if (CDC_FUNC_DESC_ABSTRACT_CONTROL_MANAGEMENT == cdc_functional_desc_typeof(p_desc) &&
- p_desc + sizeof(cdc_desc_func_acm_t) <= desc_end) {
+ TU_DESC_VALIDATE(p_desc + sizeof(cdc_desc_func_acm_t) <= desc_end)) {
// save ACM bmCapabilities
p_cdc->acm.capability = ((cdc_desc_func_acm_t const *) p_desc)->bmCapabilities;
}
@@ -1037,7 +1048,7 @@ static uint16_t acm_open(uint8_t daddr, const tusb_desc_interface_t *itf_desc, u
// Open notification endpoint of control interface if any
if (itf_desc->bNumEndpoints == 1) {
// whole endpoint descriptor must fit: tuh_edpt_open reads the full struct regardless of bLength
- TU_ASSERT(p_desc + sizeof(tusb_desc_endpoint_t) <= desc_end, 0);
+ TU_ASSERT(TU_DESC_VALIDATE(p_desc + sizeof(tusb_desc_endpoint_t) <= desc_end), 0);
TU_ASSERT(TUSB_DESC_ENDPOINT == tu_desc_type(p_desc), 0);
const tusb_desc_endpoint_t *desc_ep = (const tusb_desc_endpoint_t *)p_desc;
TU_ASSERT(tuh_edpt_open(daddr, desc_ep), 0);
@@ -1048,14 +1059,15 @@ static uint16_t acm_open(uint8_t daddr, const tusb_desc_interface_t *itf_desc, u
}
//------------- Data Interface (if any) -------------//
- if (p_desc + sizeof(tusb_desc_interface_t) <= desc_end && TUSB_DESC_INTERFACE == tu_desc_type(p_desc)) {
+ if (TU_DESC_VALIDATE(p_desc + sizeof(tusb_desc_interface_t) <= desc_end) &&
+ TUSB_DESC_INTERFACE == tu_desc_type(p_desc)) {
const tusb_desc_interface_t *data_itf = (const tusb_desc_interface_t *)p_desc;
if (data_itf->bInterfaceClass == TUSB_CLASS_CDC_DATA) {
p_desc += sizeof(tusb_desc_interface_t); // fixed struct size to endpoint descriptor, not device bLength
// open_ep_stream_pair consumes exactly two endpoints; require that count and that both fit before reading them
- TU_ASSERT(data_itf->bNumEndpoints == 2, 0);
- TU_ASSERT(p_desc + 2 * sizeof(tusb_desc_endpoint_t) <= desc_end, 0);
+ TU_ASSERT(TU_DESC_VALIDATE(data_itf->bNumEndpoints == 2), 0);
+ TU_ASSERT(TU_DESC_VALIDATE(p_desc + 2 * sizeof(tusb_desc_endpoint_t) <= desc_end), 0);
TU_ASSERT(open_ep_stream_pair(p_cdc, (const tusb_desc_endpoint_t *)p_desc), 0);
p_desc += 2 * sizeof(tusb_desc_endpoint_t);
}
diff --git a/src/tusb_option.h b/src/tusb_option.h
index e19ee1629..de8439cfb 100644
--- a/src/tusb_option.h
+++ b/src/tusb_option.h
@@ -240,6 +240,15 @@
#define OPT_MODE_SPEED_MASK 0xff00u
//--------------------------------------------------------------------+
+// Descriptor Validation Level
+// How much the stack hardens itself against mal-configured or hostile devices, traded against code size.
+// Higher levels add more checks; set CFG_TUD_VALIDATION_LEVEL / CFG_TUH_VALIDATION_LEVEL to pick one.
+//--------------------------------------------------------------------+
+#define TUSB_VALIDATION_NONE 0 ///< trusted devices only, minimal code size
+#define TUSB_VALIDATION_BASIC 1 ///< default: mal-configured devices, no OOB reads / zero-length loops
+#define TUSB_VALIDATION_STRICT 2 ///< reject malformed/hostile descriptors, stricter class validation
+
+//--------------------------------------------------------------------+
// Include tusb_config.h
//--------------------------------------------------------------------+
@@ -694,6 +703,10 @@
#ifndef CFG_TUH_ENUMERATION_BUFSIZE
#define CFG_TUH_ENUMERATION_BUFSIZE 256
#endif
+
+ #ifndef CFG_TUH_VALIDATION_LEVEL
+ #define CFG_TUH_VALIDATION_LEVEL TUSB_VALIDATION_BASIC
+ #endif
#endif // CFG_TUH_ENABLED
// Attribute to place data in accessible RAM for host controller (default: CFG_TUSB_MEM_SECTION)