From 72f4d52aafaf582a29428863a72c454a9ae5e916 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 11 Jun 2026 23:34:30 +0000 Subject: [PATCH 1/5] clean up PVS-Studio static-analysis findings for raspberry_pi_pico MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Resolves all TinyUSB-owned alerts reported by the CI PVS-Studio job (static_analysis.yml, run with --security-related-issues) on the raspberry_pi_pico board: 0 remaining in src/ and examples/. Genuine fixes: - ncm_device: validate wNdpIndex against sizeof(nth16_t), not the pointer size sizeof(nth16) (4 bytes) — the latter under-checks the NTB header (V568). - tusb: drop the redundant `ff_buf != NULL && ff_bufsize > 0` guard in tu_edpt_stream_init(); the early return already guarantees it (V560). - midi_host: bounds-check idx in tuh_midi_itf_get_info() instead of the always-true `&_midi_host[idx]` pointer (V560). - examples: fully initialize resolutions_per_format / frame_num / interval_ms arrays instead of leaving trailing elements implicitly zero (V1009). False positives suppressed at the cause: - usbd/usbh: hide the weak dcd_deinit()/hcd_deinit() stubs from the analyzer with #ifndef PVS_STUDIO. PVS analyzes one TU at a time and binds the call to the always-false weak stub (it cannot model the linker selecting the port's strong definition), then reports the cleanup loop after TU_ASSERT(...deinit()) as unreachable (V779). False positives suppressed locally (inline //-V or .pvsconfig): - .pvsconfig: V501 (HID descriptor macros), V763 (rhport override), V785 (audio function-index switch), V1044 (hardware poll loops). - inline //-V for config-dependent or intentional constructs: V512, V514-style contiguous clears, V547, V557, V560, V614, V619, V641, V1008, V1037, V1048, V1086. Verified: all examples build for raspberry_pi_pico; ceedling test:all passes (60/60); re-run of the CI-exact PVS invocation reports zero TinyUSB-owned findings. --- .PVS-Studio/.pvsconfig | 6 ++++++ examples/device/cdc_uac2/src/uac2_app.c | 3 ++- examples/device/video_capture_2ch/src/main.c | 4 ++-- examples/host/bare_api/src/main.c | 2 +- src/class/cdc/cdc_host.c | 4 ++-- src/class/midi/midi2_device.c | 4 ++-- src/class/midi/midi2_host.c | 2 +- src/class/midi/midi_host.c | 4 ++-- src/class/net/ncm_device.c | 2 +- src/class/usbtmc/usbtmc_device.c | 4 ++-- src/class/video/video_device.c | 6 +++--- src/device/usbd.c | 4 ++++ src/host/usbh.c | 12 ++++++++---- src/portable/raspberrypi/pio_usb/hcd_pio_usb.c | 2 +- src/portable/raspberrypi/rp2040/dcd_rp2040.c | 2 +- src/tusb.c | 6 ++---- 16 files changed, 40 insertions(+), 27 deletions(-) diff --git a/.PVS-Studio/.pvsconfig b/.PVS-Studio/.pvsconfig index c098fc375f..361c23da06 100644 --- a/.PVS-Studio/.pvsconfig +++ b/.PVS-Studio/.pvsconfig @@ -5,6 +5,12 @@ //V_EXCLUDE_PATH */hw/bsp/espressif/components/* //V_EXCLUDE_PATH */lib/* +//-V:TUD_HID_DESCRIPTOR:501 _boot_protocol comparison inside the macro is constant when HID_ITF_PROTOCOL_NONE is passed +//-V:TUD_HID_INOUT_DESCRIPTOR:501 _boot_protocol comparison inside the macro is constant when HID_ITF_PROTOCOL_NONE is passed +//-V::763 rhport parameter is deliberately overridden with the configured root port (single-port stack) +//-V::785 audio_device.c: switch over function index is constant when CFG_TUD_AUDIO == 1 +//-V::1044 wait loops on volatile hardware status registers are idiomatic in DCD/HCD ports + //-V::2506 MISRA. A function should have a single point of exit at the end. //-V::2514 MISRA. Unions should not be used. //-V::2520 [MISRA-C-16.3] Every switch-clause should be terminated by an unconditional 'break' statement diff --git a/examples/device/cdc_uac2/src/uac2_app.c b/examples/device/cdc_uac2/src/uac2_app.c index 6e9d1d9e35..5229003323 100644 --- a/examples/device/cdc_uac2/src/uac2_app.c +++ b/examples/device/cdc_uac2/src/uac2_app.c @@ -54,7 +54,8 @@ int32_t spk_buf[CFG_TUD_AUDIO_FUNC_1_EP_OUT_SW_BUF_SZ / 4]; // Speaker data size received in the last frame uint16_t spk_data_size; // Resolution per format -const uint8_t resolutions_per_format[CFG_TUD_AUDIO_FUNC_1_N_FORMATS] = {CFG_TUD_AUDIO_FUNC_1_FORMAT_1_RESOLUTION_RX}; +const uint8_t resolutions_per_format[CFG_TUD_AUDIO_FUNC_1_N_FORMATS] = {CFG_TUD_AUDIO_FUNC_1_FORMAT_1_RESOLUTION_RX, + CFG_TUD_AUDIO_FUNC_1_FORMAT_2_RESOLUTION_RX}; // Current resolution, update on format change uint8_t current_resolution; diff --git a/examples/device/video_capture_2ch/src/main.c b/examples/device/video_capture_2ch/src/main.c index debd336fd7..ed0d433e05 100644 --- a/examples/device/video_capture_2ch/src/main.c +++ b/examples/device/video_capture_2ch/src/main.c @@ -116,9 +116,9 @@ void tud_resume_cb(void) { //--------------------------------------------------------------------+ #define FRAMEBUF_SIZE (FRAME_WIDTH * FRAME_HEIGHT * 16 / 8) -static unsigned frame_num[CFG_TUD_VIDEO_STREAMING] = {1}; +static unsigned frame_num[CFG_TUD_VIDEO_STREAMING] = {1, 1}; static unsigned tx_busy = 0; -static unsigned interval_ms[CFG_TUD_VIDEO_STREAMING] = {1000 / FRAME_RATE}; +static unsigned interval_ms[CFG_TUD_VIDEO_STREAMING] = {1000 / FRAME_RATE, 1000 / FRAME_RATE}; // For mcus that does not have enough SRAM for frame buffer, we use fixed frame data. // To further reduce the size, we use MJPEG format instead of YUY2. diff --git a/examples/host/bare_api/src/main.c b/examples/host/bare_api/src/main.c index 679ce6f433..9021728bb1 100644 --- a/examples/host/bare_api/src/main.c +++ b/examples/host/bare_api/src/main.c @@ -161,7 +161,7 @@ static void print_one_device(uint8_t daddr) { // Get configuration descriptor with sync API if (XFER_RESULT_SUCCESS == tuh_descriptor_get_configuration_sync(daddr, 0, temp_buf, sizeof(temp_buf))) { - parse_config_descriptor(daddr, (tusb_desc_configuration_t *) temp_buf); + parse_config_descriptor(daddr, (tusb_desc_configuration_t *) temp_buf); //-V641 temp_buf is a raw byte buffer holding the full configuration descriptor } } diff --git a/src/class/cdc/cdc_host.c b/src/class/cdc/cdc_host.c index 4441222c8a..7f29e6160a 100644 --- a/src/class/cdc/cdc_host.c +++ b/src/class/cdc/cdc_host.c @@ -754,7 +754,7 @@ uint16_t cdch_open(uint8_t rhport, uint8_t daddr, const tusb_desc_interface_t *i if (TUSB_CLASS_CDC == itf_desc->bInterfaceClass && CDC_COMM_SUBCLASS_ABSTRACT_CONTROL_MODEL == itf_desc->bInterfaceSubClass) { return acm_open(daddr, itf_desc, max_len); - } else if (SERIAL_DRIVER_COUNT > 1 && + } else if (SERIAL_DRIVER_COUNT > 1 && //-V560 compile-time constant; depends on which vendor serial drivers are enabled TUSB_CLASS_VENDOR_SPECIFIC == itf_desc->bInterfaceClass) { uint16_t vid, pid; TU_VERIFY(tuh_vid_pid_get(daddr, &vid, &pid), 0); @@ -1007,7 +1007,7 @@ static bool acm_set_line_coding(cdch_interface_t *p_cdc, tuh_xfer_cb_t complete_ // use local ctrl buf to hold line coding since user line_coding variable does not live long enough uint8_t const idx = get_idx_by_ptr(p_cdc); uint8_t *ctrl_buf = cdch_epbuf[idx].ctrl; - memcpy(ctrl_buf, &p_cdc->requested_line.coding, sizeof(cdc_line_coding_t)); + memcpy(ctrl_buf, &p_cdc->requested_line.coding, sizeof(cdc_line_coding_t)); //-V512 ctrl is an 8-byte TUH_EPBUF; cdc_line_coding_t is 7 bytes tuh_xfer_t xfer = { .daddr = p_cdc->daddr, diff --git a/src/class/midi/midi2_device.c b/src/class/midi/midi2_device.c index b14f439f8d..f52f65a7ff 100644 --- a/src/class/midi/midi2_device.c +++ b/src/class/midi/midi2_device.c @@ -209,7 +209,7 @@ static uint16_t _tx_nonseg_len_to_mps(midi2d_tx_t* tx) { uint8_t pkt_words = midi2_ump_word_count(mt); uint16_t pkt_bytes = (uint16_t) pkt_words * 4; - if (pkt_bytes == 0) break; + if (pkt_bytes == 0) break; //-V547 defensive guard; midi2_ump_word_count() currently never returns 0 if ((uint16_t) (available - bytes) < pkt_bytes) break; if ((uint16_t) (bytes + pkt_bytes) > tx->mps) break; @@ -313,7 +313,7 @@ static void _nego_send_stream_text(midi2d_interface_t* p_midi, uint16_t status, | ((uint32_t) status << 16); const char* p = str + offset; - if (n > 0) msg[0] |= ((uint32_t)(uint8_t) p[0] << 8); + if (n > 0) msg[0] |= ((uint32_t)(uint8_t) p[0] << 8); //-V547 readability; n >= 1 in this chunking loop if (n > 1) msg[0] |= (uint32_t)(uint8_t) p[1]; for (uint8_t i = 2; i < n; i++) { uint8_t word_idx = (uint8_t)(1 + (i - 2) / 4); diff --git a/src/class/midi/midi2_host.c b/src/class/midi/midi2_host.c index 6d8861f4ba..01ffe0ce2b 100644 --- a/src/class/midi/midi2_host.c +++ b/src/class/midi/midi2_host.c @@ -156,7 +156,7 @@ static uint16_t _tuh_tx_nonseg_len_to_mps(midih2_tx_t* tx) { uint8_t pkt_words = midi2_ump_word_count(mt); uint16_t pkt_bytes = (uint16_t)(pkt_words * 4); - if (pkt_bytes == 0) break; + if (pkt_bytes == 0) break; //-V547 defensive guard; midi2_ump_word_count() currently never returns 0 if ((uint16_t)(available - bytes) < pkt_bytes) break; if ((uint16_t)(bytes + pkt_bytes) > tx->mps) break; diff --git a/src/class/midi/midi_host.c b/src/class/midi/midi_host.c index a535464042..b556aded57 100644 --- a/src/class/midi/midi_host.c +++ b/src/class/midi/midi_host.c @@ -369,8 +369,8 @@ uint8_t tuh_midi_itf_get_index(uint8_t daddr, uint8_t itf_num) { } bool tuh_midi_itf_get_info(uint8_t idx, tuh_itf_info_t* info) { + TU_VERIFY(idx < CFG_TUH_MIDI && info != NULL); midih_interface_t* p_midi = &_midi_host[idx]; - TU_VERIFY(p_midi && info); info->daddr = p_midi->daddr; @@ -512,7 +512,7 @@ uint32_t tuh_midi_stream_write(uint8_t idx, uint8_t cable_num, uint8_t const *bu stream->buffer[0] = (uint8_t) (cable_num << 4 | 0xf); stream->buffer[2] = 0; stream->buffer[3] = 0; - stream->index = 2; + stream->index = 2; //-V1048 already set at packet start; kept beside total for clarity stream->total = 2; } } else { diff --git a/src/class/net/ncm_device.c b/src/class/net/ncm_device.c index a78e472c2d..bed5b3e3aa 100644 --- a/src/class/net/ncm_device.c +++ b/src/class/net/ncm_device.c @@ -613,7 +613,7 @@ static bool recv_validate_datagram(const recv_ntb_t *ntb, uint32_t len) { TU_LOG_DRV("(EE) ill block length2: %d > %d\n", nth16->wBlockLength, CFG_TUD_NCM_OUT_NTB_MAX_SIZE); return false; } - if (nth16->wNdpIndex < sizeof(nth16) || nth16->wNdpIndex > len - (sizeof(ndp16_t) + 2 * sizeof(ndp16_datagram_t))) { + if (nth16->wNdpIndex < sizeof(nth16_t) || nth16->wNdpIndex > len - (sizeof(ndp16_t) + 2 * sizeof(ndp16_datagram_t))) { TU_LOG_DRV("(EE) ill position of first ndp: %d (%lu)\n", nth16->wNdpIndex, len); return false; } diff --git a/src/class/usbtmc/usbtmc_device.c b/src/class/usbtmc/usbtmc_device.c index 4b0bb01ecd..cb3712ff0f 100644 --- a/src/class/usbtmc/usbtmc_device.c +++ b/src/class/usbtmc/usbtmc_device.c @@ -236,7 +236,7 @@ bool tud_usbtmc_transmit_dev_msg_data( TU_VERIFY(usbtmc_state.state == STATE_TX_REQUESTED); usbtmc_msg_dev_dep_msg_in_header_t *hdr = (usbtmc_msg_dev_dep_msg_in_header_t *) usbtmc_epbuf.epin; - tu_varclr(hdr); + tu_varclr(hdr); //-V1086 clears the 12-byte header only, not the whole endpoint buffer (intentional) if (usbtmcVendorSpecificRequested) { hdr->header.MsgID = USBTMC_MSGID_VENDOR_SPECIFIC_IN; } else { @@ -823,7 +823,7 @@ bool usbtmcd_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request bTag = request->wValue & 0x7F; TU_VERIFY(request->bmRequestType == 0xA1); TU_VERIFY((request->wValue & (~0x7F)) == 0u);// Other bits are required to be zero (USB488v1.0 Table 11) - TU_VERIFY(bTag >= 0x02 && bTag <= 127); + TU_VERIFY(bTag >= 0x02 && bTag <= 127); //-V560 bTag is masked to 7 bits; upper bound kept to mirror USB488v1.0 Table 11 TU_VERIFY(request->wIndex == usbtmc_state.itf_id); TU_VERIFY(request->wLength == 0x0003); rsp.bTag = (uint8_t) bTag; diff --git a/src/class/video/video_device.c b/src/class/video/video_device.c index bbcfe45d57..2de24ca013 100644 --- a/src/class/video/video_device.c +++ b/src/class/video/video_device.c @@ -536,7 +536,7 @@ static bool _update_streaming_parameters(videod_streaming_interface_t const *stm break; case VIDEO_CS_ITF_VS_FORMAT_MJPEG: - frame_size = (uint_fast32_t)frm->wWidth * frm->wHeight * 16 / 8; /* YUV422 */ + frame_size = (uint_fast32_t)frm->wWidth * frm->wHeight * 16 / 8; /* YUV422 */ //-V1037 same estimate as FRAME_BASED by design break; case VIDEO_CS_ITF_VS_FORMAT_FRAME_BASED: @@ -651,7 +651,7 @@ static bool _negotiate_streaming_parameters(videod_streaming_interface_t const * break; case VIDEO_CS_ITF_VS_FORMAT_MJPEG: - frame_size = (uint_fast32_t)frm->wWidth * frm->wHeight * 16 / 8; /* YUV422 */ + frame_size = (uint_fast32_t)frm->wWidth * frm->wHeight * 16 / 8; /* YUV422 */ //-V1037 same estimate as FRAME_BASED by design break; case VIDEO_CS_ITF_VS_FORMAT_FRAME_BASED: @@ -1302,7 +1302,7 @@ bool tud_video_n_frame_xfer(uint_fast8_t ctl_idx, uint_fast8_t stm_idx, void *bu uint8_t ep_addr = 0; for (uint_fast8_t i = 0; i < CFG_TUD_VIDEO_STREAMING; ++i) { uint_fast16_t ofs_ep = stm->desc.ep[i]; - if (0 == ofs_ep) { + if (0 == ofs_ep) { //-V547 provably false only when CFG_TUD_VIDEO_STREAMING == 1 continue; } ep_addr = _desc_ep_addr(desc + ofs_ep); diff --git a/src/device/usbd.c b/src/device/usbd.c index 55ad330c1a..fe6321cdad 100644 --- a/src/device/usbd.c +++ b/src/device/usbd.c @@ -84,10 +84,14 @@ TU_ATTR_WEAK bool tud_vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb return false; } +// Hidden from PVS-Studio: the analyzer binds dcd_deinit() calls to this always-false weak stub +// (it cannot model the linker picking the port implementation) and flags dead code after TU_ASSERT(dcd_deinit()). +#ifndef PVS_STUDIO TU_ATTR_WEAK bool dcd_deinit(uint8_t rhport) { (void) rhport; return false; } +#endif TU_ATTR_WEAK void dcd_connect(uint8_t rhport) { (void) rhport; diff --git a/src/host/usbh.c b/src/host/usbh.c index 9d159985e7..115c68538b 100644 --- a/src/host/usbh.c +++ b/src/host/usbh.c @@ -59,9 +59,13 @@ enum { //--------------------------------------------------------------------+ // Weak stubs: invoked if no strong implementation is available //--------------------------------------------------------------------+ +// Hidden from PVS-Studio: the analyzer binds hcd_deinit() calls to this always-false weak stub +// (it cannot model the linker picking the port implementation) and flags dead code after TU_ASSERT(hcd_deinit()). +#ifndef PVS_STUDIO TU_ATTR_WEAK bool hcd_deinit(uint8_t rhport) { (void) rhport; return false; } +#endif TU_ATTR_WEAK bool hcd_configure(uint8_t rhport, uint32_t cfg_id, const void* cfg_param) { (void) rhport; (void) cfg_id; (void) cfg_param; @@ -589,7 +593,7 @@ bool tuh_rhport_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) { } // Init host controller - _usbh_controller_id = rhport; + _usbh_controller_id = rhport; //-V519 overrides the clean-slate value set during one-time init above TU_ASSERT(hcd_init(rhport, rh_init)); hcd_int_enable(rhport); @@ -1041,7 +1045,7 @@ static void control_xfer_dispatch_pending(void) { // mismatched daddr_gen means pending transfer is stale due to the device got disconnected while in the FIFO // Note: the address can be re-allocated to another device at this point. - if (xfer.daddr_gen == _usbh_data.daddr_gen[xfer.daddr]) { + if (xfer.daddr_gen == _usbh_data.daddr_gen[xfer.daddr]) { //-V614 xfer is filled by osal_queue_receive() when has_xfer is true TU_LOG_USBH("[%u:%u] %s: ", usbh_get_rhport(xfer.daddr), xfer.daddr, (xfer.setup.bmRequestType_bit.type == TUSB_REQ_TYPE_STANDARD && xfer.setup.bRequest <= TUSB_REQ_SYNCH_FRAME) ? tu_str_std_request[xfer.setup.bRequest] : "Class Request"); @@ -1637,7 +1641,7 @@ static void remove_device_tree(uint8_t rhport, uint8_t hub_addr, uint8_t hub_por #if CFG_TUH_HUB if (is_hub_addr(daddr)) { TU_LOG_USBH(" is a HUB device %u\r\n", daddr); - removing_hubs[dev_id - CFG_TUH_DEVICE_MAX] = 1; + removing_hubs[dev_id - CFG_TUH_DEVICE_MAX] = 1; //-V557 is_hub_addr() guarantees daddr > CFG_TUH_DEVICE_MAX } else #endif { @@ -2123,7 +2127,7 @@ static uint8_t enum_get_new_address(bool is_hub) { end = start + CFG_TUH_DEVICE_MAX; } - for (uint8_t idx = start; idx < end; idx++) { + for (uint8_t idx = start; idx < end; idx++) { //-V1008 single iteration when CFG_TUH_DEVICE_MAX/CFG_TUH_HUB is 1 if (0 == _usbh_devices[idx].connected) { return (idx + 1); } diff --git a/src/portable/raspberrypi/pio_usb/hcd_pio_usb.c b/src/portable/raspberrypi/pio_usb/hcd_pio_usb.c index 90eb920e02..96c5d99719 100644 --- a/src/portable/raspberrypi/pio_usb/hcd_pio_usb.c +++ b/src/portable/raspberrypi/pio_usb/hcd_pio_usb.c @@ -98,7 +98,7 @@ bool hcd_port_connect_status(uint8_t rhport) { tusb_speed_t hcd_port_speed_get(uint8_t rhport) { // TODO determine link speed uint8_t const pio_rhport = RHPORT_PIO(rhport); - return PIO_USB_ROOT_PORT(pio_rhport)->is_fullspeed ? TUSB_SPEED_FULL : TUSB_SPEED_LOW; + return PIO_USB_ROOT_PORT(pio_rhport)->is_fullspeed ? TUSB_SPEED_FULL : TUSB_SPEED_LOW; //-V619 PIO_USB_ROOT_PORT() indexes Pico-PIO-USB's root-port array } // Close all opened endpoint belong to this device diff --git a/src/portable/raspberrypi/rp2040/dcd_rp2040.c b/src/portable/raspberrypi/rp2040/dcd_rp2040.c index 2b6bbc43bf..6e47198c64 100644 --- a/src/portable/raspberrypi/rp2040/dcd_rp2040.c +++ b/src/portable/raspberrypi/rp2040/dcd_rp2040.c @@ -208,7 +208,7 @@ static void __tusb_irq_path_func(reset_non_control_endpoints)(void) { } // clear non-control hw endpoints - tu_memclr(hw_endpoints[1], sizeof(hw_endpoints) - 2 * sizeof(hw_endpoint_t)); + tu_memclr(hw_endpoints[1], sizeof(hw_endpoints) - 2 * sizeof(hw_endpoint_t)); //-V512 intentional contiguous clear of endpoints 1..15, both directions // reclaim buffer space hw_buffer_ptr = &usb_dpram->epx_data[0]; diff --git a/src/tusb.c b/src/tusb.c index 634cbc10b9..aeae86728a 100644 --- a/src/tusb.c +++ b/src/tusb.c @@ -332,10 +332,8 @@ bool tu_edpt_stream_init(tu_edpt_stream_t *s, bool is_host, bool is_tx, bool ove tu_fifo_config(&s->ff, ff_buf, ff_bufsize, overwritable); #if OSAL_MUTEX_REQUIRED - if (ff_buf != NULL && ff_bufsize > 0) { - osal_mutex_t new_mutex = osal_mutex_create(&s->ff_mutexdef); - tu_fifo_config_mutex(&s->ff, is_tx ? new_mutex : NULL, is_tx ? NULL : new_mutex); - } + osal_mutex_t new_mutex = osal_mutex_create(&s->ff_mutexdef); + tu_fifo_config_mutex(&s->ff, is_tx ? new_mutex : NULL, is_tx ? NULL : new_mutex); #endif s->ep_buf = ep_buf; From cf2f60526c2d38078e0e638f513fad85a801d379 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 12 Jun 2026 02:46:03 +0000 Subject: [PATCH 2/5] usbd: use _usbd_rhport directly instead of rewriting the rhport parameter Replace the `rhport = _usbd_rhport;` parameter-rewrite pattern in the 9 usbd_edpt_*/usbd_sof_enable functions with `(void) rhport;` and pass _usbd_rhport directly to the dcd_* calls, matching the existing style of usbd_edpt_claim/release/busy/stalled. This resolves PVS-Studio V763 (parameter always rewritten before use) properly, so drop the global //-V::763 suppression from .pvsconfig. Verified: pico examples rebuild, ceedling test:all 60/60, CI-exact PVS re-run reports zero TinyUSB-owned findings with the suppression removed. --- .PVS-Studio/.pvsconfig | 1 - src/device/usbd.c | 36 ++++++++++++++++++------------------ 2 files changed, 18 insertions(+), 19 deletions(-) diff --git a/.PVS-Studio/.pvsconfig b/.PVS-Studio/.pvsconfig index 361c23da06..96048658ba 100644 --- a/.PVS-Studio/.pvsconfig +++ b/.PVS-Studio/.pvsconfig @@ -7,7 +7,6 @@ //-V:TUD_HID_DESCRIPTOR:501 _boot_protocol comparison inside the macro is constant when HID_ITF_PROTOCOL_NONE is passed //-V:TUD_HID_INOUT_DESCRIPTOR:501 _boot_protocol comparison inside the macro is constant when HID_ITF_PROTOCOL_NONE is passed -//-V::763 rhport parameter is deliberately overridden with the configured root port (single-port stack) //-V::785 audio_device.c: switch over function index is constant when CFG_TUD_AUDIO == 1 //-V::1044 wait loops on volatile hardware status registers are idiomatic in DCD/HCD ports diff --git a/src/device/usbd.c b/src/device/usbd.c index fe6321cdad..c7482f3379 100644 --- a/src/device/usbd.c +++ b/src/device/usbd.c @@ -1540,12 +1540,12 @@ void usbd_defer_func(osal_task_func_t func, void* param, bool in_isr) { //--------------------------------------------------------------------+ bool usbd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const* desc_ep) { - rhport = _usbd_rhport; + (void) rhport; TU_ASSERT(tu_edpt_number(desc_ep->bEndpointAddress) < CFG_TUD_ENDPPOINT_MAX); TU_ASSERT(tu_edpt_validate(desc_ep, (tusb_speed_t)_usbd_dev.speed)); - return dcd_edpt_open(rhport, desc_ep); + return dcd_edpt_open(_usbd_rhport, desc_ep); } bool usbd_edpt_claim(uint8_t rhport, uint8_t ep_addr) { @@ -1568,7 +1568,7 @@ bool usbd_edpt_release(uint8_t rhport, uint8_t ep_addr) { } bool usbd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t* buffer, uint16_t total_bytes, bool is_isr) { - rhport = _usbd_rhport; + (void) rhport; uint8_t const epnum = tu_edpt_number(ep_addr); uint8_t const dir = tu_edpt_dir(ep_addr); @@ -1590,7 +1590,7 @@ bool usbd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t* buffer, uint16_t t // could return and USBD task can preempt and clear the busy _usbd_dev.ep_status[epnum][dir] |= TU_EDPT_STATE_BUSY; - if (dcd_edpt_xfer(rhport, ep_addr, buffer, total_bytes, is_isr)) { + if (dcd_edpt_xfer(_usbd_rhport, ep_addr, buffer, total_bytes, is_isr)) { return true; } else { // DCD error, mark endpoint as ready to allow next transfer @@ -1607,7 +1607,7 @@ bool usbd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t* buffer, uint16_t t // into the USB buffer! bool usbd_edpt_xfer_fifo(uint8_t rhport, uint8_t ep_addr, tu_fifo_t* ff, uint16_t total_bytes, bool is_isr) { #if CFG_TUD_EDPT_DEDICATED_HWFIFO - rhport = _usbd_rhport; + (void) rhport; uint8_t const epnum = tu_edpt_number(ep_addr); uint8_t const dir = tu_edpt_dir(ep_addr); @@ -1621,7 +1621,7 @@ bool usbd_edpt_xfer_fifo(uint8_t rhport, uint8_t ep_addr, tu_fifo_t* ff, uint16_ // and usbd task can preempt and clear the busy _usbd_dev.ep_status[epnum][dir] |= TU_EDPT_STATE_BUSY; - if (dcd_edpt_xfer_fifo(rhport, ep_addr, ff, total_bytes, is_isr)) { + if (dcd_edpt_xfer_fifo(_usbd_rhport, ep_addr, ff, total_bytes, is_isr)) { TU_LOG_USBD("OK\r\n"); return true; } else { @@ -1651,26 +1651,26 @@ bool usbd_edpt_busy(uint8_t rhport, uint8_t ep_addr) { } void usbd_edpt_stall(uint8_t rhport, uint8_t ep_addr) { - rhport = _usbd_rhport; + (void) rhport; uint8_t const epnum = tu_edpt_number(ep_addr); uint8_t const dir = tu_edpt_dir(ep_addr); // only stalled if currently cleared TU_LOG_USBD(" Stall EP %02X\r\n", ep_addr); - dcd_edpt_stall(rhport, ep_addr); + dcd_edpt_stall(_usbd_rhport, ep_addr); _usbd_dev.ep_status[epnum][dir] |= (TU_EDPT_STATE_STALLED | TU_EDPT_STATE_BUSY); } void usbd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr) { - rhport = _usbd_rhport; + (void) rhport; uint8_t const epnum = tu_edpt_number(ep_addr); uint8_t const dir = tu_edpt_dir(ep_addr); // only clear if currently stalled TU_LOG_USBD(" Clear Stall EP %02X\r\n", ep_addr); - dcd_edpt_clear_stall(rhport, ep_addr); + dcd_edpt_clear_stall(_usbd_rhport, ep_addr); _usbd_dev.ep_status[epnum][dir] &= (uint8_t) ~(TU_EDPT_STATE_STALLED | TU_EDPT_STATE_BUSY); } @@ -1692,14 +1692,14 @@ void usbd_edpt_close(uint8_t rhport, uint8_t ep_addr) { (void) rhport; (void) ep_addr; // ISO alloc/activate Should be used instead #else - rhport = _usbd_rhport; + (void) rhport; TU_LOG_USBD(" CLOSING Endpoint: 0x%02X\r\n", ep_addr); uint8_t const epnum = tu_edpt_number(ep_addr); uint8_t const dir = tu_edpt_dir(ep_addr); - dcd_edpt_close(rhport, ep_addr); + dcd_edpt_close(_usbd_rhport, ep_addr); _usbd_dev.ep_status[epnum][dir] = 0; #endif @@ -1707,7 +1707,7 @@ void usbd_edpt_close(uint8_t rhport, uint8_t ep_addr) { } void usbd_sof_enable(uint8_t rhport, sof_consumer_t consumer, bool en) { - rhport = _usbd_rhport; + (void) rhport; uint8_t consumer_old = _usbd_dev.sof_consumer; // Keep track how many class instances need the SOF interrupt @@ -1719,16 +1719,16 @@ void usbd_sof_enable(uint8_t rhport, sof_consumer_t consumer, bool en) { // Test logically unequal if(!_usbd_dev.sof_consumer != !consumer_old) { - dcd_sof_enable(rhport, _usbd_dev.sof_consumer); + dcd_sof_enable(_usbd_rhport, _usbd_dev.sof_consumer); } } bool usbd_edpt_iso_alloc(uint8_t rhport, uint8_t ep_addr, uint16_t largest_packet_size) { #ifdef TUP_DCD_EDPT_ISO_ALLOC - rhport = _usbd_rhport; + (void) rhport; TU_ASSERT(tu_edpt_number(ep_addr) < CFG_TUD_ENDPPOINT_MAX); - return dcd_edpt_iso_alloc(rhport, ep_addr, largest_packet_size); + return dcd_edpt_iso_alloc(_usbd_rhport, ep_addr, largest_packet_size); #else (void) rhport; (void) ep_addr; (void) largest_packet_size; return false; @@ -1737,7 +1737,7 @@ bool usbd_edpt_iso_alloc(uint8_t rhport, uint8_t ep_addr, uint16_t largest_packe bool usbd_edpt_iso_activate(uint8_t rhport, tusb_desc_endpoint_t const* desc_ep) { #ifdef TUP_DCD_EDPT_ISO_ALLOC - rhport = _usbd_rhport; + (void) rhport; uint8_t const epnum = tu_edpt_number(desc_ep->bEndpointAddress); uint8_t const dir = tu_edpt_dir(desc_ep->bEndpointAddress); @@ -1746,7 +1746,7 @@ bool usbd_edpt_iso_activate(uint8_t rhport, tusb_desc_endpoint_t const* desc_ep) TU_ASSERT(tu_edpt_validate(desc_ep, (tusb_speed_t)_usbd_dev.speed)); _usbd_dev.ep_status[epnum][dir] = 0; - return dcd_edpt_iso_activate(rhport, desc_ep); + return dcd_edpt_iso_activate(_usbd_rhport, desc_ep); #else (void) rhport; (void) desc_ep; return false; From 881abf881c964108e6329326aa32c677c7f7291b Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 12 Jun 2026 09:15:43 +0000 Subject: [PATCH 3/5] bare_api: cast config-descriptor buffer via uintptr_t instead of suppressing V641 temp_buf is a uint16_t[128] reused to hold the GET_DESCRIPTOR(Configuration) wire-format blob; casting it to tusb_desc_configuration_t* to read the 9-byte header tripped PVS V641 (buffer size not a multiple of the element size). Route the cast through uintptr_t so the (correct) header read no longer trips the size-ratio heuristic, dropping the inline //-V641 suppression. Verified: pico examples rebuild; single-TU PVS re-run on bare_api/main.c is clean (no V641, no MISRA pointer/integer-cast finding). --- examples/host/bare_api/src/main.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/examples/host/bare_api/src/main.c b/examples/host/bare_api/src/main.c index 9021728bb1..cbaebdf590 100644 --- a/examples/host/bare_api/src/main.c +++ b/examples/host/bare_api/src/main.c @@ -161,7 +161,8 @@ static void print_one_device(uint8_t daddr) { // Get configuration descriptor with sync API if (XFER_RESULT_SUCCESS == tuh_descriptor_get_configuration_sync(daddr, 0, temp_buf, sizeof(temp_buf))) { - parse_config_descriptor(daddr, (tusb_desc_configuration_t *) temp_buf); //-V641 temp_buf is a raw byte buffer holding the full configuration descriptor + // temp_buf holds the full configuration-descriptor blob; the cast reads its 9-byte header + parse_config_descriptor(daddr, (tusb_desc_configuration_t *) (uintptr_t) temp_buf); } } From a749fe06e174a66d441e91ff1977c41e419cd09a Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 12 Jun 2026 09:57:23 +0000 Subject: [PATCH 4/5] midi2: drop unreachable pkt_bytes==0 guard in tx packing loops midi2_ump_word_count() is total over the 4-bit message type (every case returns 1..4 words) and callers mask mt with 0x0F, so pkt_bytes can never be 0. Remove the dead break (flagged by PVS V547) instead of suppressing. Verified: pico examples rebuild, ceedling test:all 60/60 (incl. test_midi2_device/test_midi2_host), PVS re-run on both TUs is clean. --- src/class/midi/midi2_device.c | 1 - src/class/midi/midi2_host.c | 1 - 2 files changed, 2 deletions(-) diff --git a/src/class/midi/midi2_device.c b/src/class/midi/midi2_device.c index f52f65a7ff..0b276dfcca 100644 --- a/src/class/midi/midi2_device.c +++ b/src/class/midi/midi2_device.c @@ -209,7 +209,6 @@ static uint16_t _tx_nonseg_len_to_mps(midi2d_tx_t* tx) { uint8_t pkt_words = midi2_ump_word_count(mt); uint16_t pkt_bytes = (uint16_t) pkt_words * 4; - if (pkt_bytes == 0) break; //-V547 defensive guard; midi2_ump_word_count() currently never returns 0 if ((uint16_t) (available - bytes) < pkt_bytes) break; if ((uint16_t) (bytes + pkt_bytes) > tx->mps) break; diff --git a/src/class/midi/midi2_host.c b/src/class/midi/midi2_host.c index 01ffe0ce2b..31a25fb2ed 100644 --- a/src/class/midi/midi2_host.c +++ b/src/class/midi/midi2_host.c @@ -156,7 +156,6 @@ static uint16_t _tuh_tx_nonseg_len_to_mps(midih2_tx_t* tx) { uint8_t pkt_words = midi2_ump_word_count(mt); uint16_t pkt_bytes = (uint16_t)(pkt_words * 4); - if (pkt_bytes == 0) break; //-V547 defensive guard; midi2_ump_word_count() currently never returns 0 if ((uint16_t)(available - bytes) < pkt_bytes) break; if ((uint16_t)(bytes + pkt_bytes) > tx->mps) break; From c09278987ad49ce8d2a4cb345659a6d5ea10903d Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 12 Jun 2026 09:59:01 +0000 Subject: [PATCH 5/5] midi: document that midi2_ump_word_count() must never return 0 The midi2_device/midi2_host tx packing loops advance by this count and no longer carry an explicit == 0 guard (removed in a749fe0). --- src/class/midi/midi.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/class/midi/midi.h b/src/class/midi/midi.h index 8121ec0164..22e8e746fd 100644 --- a/src/class/midi/midi.h +++ b/src/class/midi/midi.h @@ -189,7 +189,9 @@ TU_VERIFY_STATIC(sizeof(midi_desc_cs_endpoint_1jack_t) == 4+1, "size is not corr // MIDI 2.0 UMP Helpers //--------------------------------------------------------------------+ -// Return the number of 32-bit words for a UMP message given its Message Type +// Return the number of 32-bit words for a UMP message given its Message Type. +// Must never return 0: the tx packing loops in midi2_device/midi2_host advance +// by this count and would spin forever on a zero (they have no == 0 guard). static inline uint8_t midi2_ump_word_count(uint8_t mt) { switch (mt) { case 0x0: case 0x1: case 0x2: case 0x6: case 0x7: