Skip to content

Conversation

@TDA-2030
Copy link
Collaborator

@TDA-2030 TDA-2030 commented Nov 27, 2025

Description

  • Added SOI check to prevent output of corrupted MJPEG frames
  • Moved usb_types_uvc.h to private_include directory
  • Fixed an issue where the stream interface could not be found because some endpoints in the descriptor had excessively high MPS.
  • Improved basic_uvc_stream example to automatically open the first available format

Related

Testing


Checklist

Before submitting a Pull Request, please ensure the following:

  • 🚨 This PR does not introduce breaking changes.
  • All CI checks (GH Actions) pass.
  • Documentation is updated as needed.
  • Tests are updated or added as necessary.
  • Code is well-commented, especially in complex areas.
  • Git history is clean — commits are squashed to the minimum necessary.

Note

Validate UVC headers and MJPEG SOI, harden bulk EOF handling, auto-pick first available stream format in example, relocate UVC types header, and bump to 2.4.1.

  • Streaming robustness:
    • Add uvc_frame_payload_header_validate() and use it in ISOC/BULK paths to reject invalid payloads.
    • Add MJPEG SOI check to drop corrupted frames.
    • Rework BULK state machine: detect EOF via short packet and finalize frames reliably.
    • ISOC: handle missed EoF, skip tiny frames, and improve warnings.
  • Descriptor parsing:
    • Improve endpoint/interface selection to continue scanning when MPS/mult not optimal.
  • Example (examples/basic_uvc_stream):
    • Use driver event_cb to get frame list and start tasks; auto-select first available format (derive FPS from default_interval).
    • Minor API adjustments (queue/task setup, device count handling).
    • Update sdkconfig defaults and add target-specific defaults for esp32p4/s2/s3.
  • Headers/layout:
    • Move usb_types_uvc.h to private_include and update includes.
  • Versioning:
    • Bump component to 2.4.1; update CHANGELOG.md.

Written by Cursor Bugbot for commit 747a7ea. This will update automatically on new commits. Configure here.

Copy link

@cursor cursor bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This PR is being reviewed by Cursor Bugbot

Details

Your team is on the Bugbot Free tier. On this plan, Bugbot will review limited PRs each billing cycle for each member of your team.

To receive Bugbot reviews on all of your PRs, visit the Cursor dashboard to activate Pro and start your 14-day free trial.


// Check mjpeg frame start
if (uvc_stream->dynamic.vs_format.format == UVC_VS_FORMAT_MJPEG &&
payload_data[0] != 0xff && payload_data[1] != 0xd8) {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: Wrong boolean operator in MJPEG SOI validation check

The MJPEG SOI (Start of Image) check uses && instead of ||. A valid JPEG starts with bytes 0xFF 0xD8. The current condition payload_data[0] != 0xff && payload_data[1] != 0xd8 only evaluates to true when both bytes are wrong, so frames with only one incorrect byte (like 0x00 0xD8 or 0xFF 0x00) will incorrectly pass validation. The condition needs || (OR) to skip frames where either byte is wrong.

Additional Locations (1)

Fix in Cursor Fix in Web

.urb_size = 10 * 1024,
},
};
BaseType_t task_created = xTaskCreatePinnedToCore(frame_handling_task, "mjpeg_handling", 4096, (void *)&stream_mjpeg_config, EXAMPLE_USB_HOST_PRIORITY - 2, NULL, tskNO_AFFINITY);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: Stack-local config passed to task causes use-after-free

The stream_mjpeg_config and stream_h265_config variables are local to start_uvc_frame_handling_tasks() but their addresses are passed to xTaskCreatePinnedToCore. When this function returns, those stack variables go out of scope. The newly created tasks copy the config in frame_handling_task() at line 110, but there is no synchronization to ensure the task runs before the function returns. This creates a race condition where the task may read garbage from deallocated stack memory.

Additional Locations (1)

Fix in Cursor Fix in Web

frame_info_list[stream_index][i].v_res,
UVC_DESC_DWFRAMEINTERVAL_TO_FPS(frame_info_list[stream_index][i].default_interval));
}
start_uvc_frame_handling_tasks();
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: Multiple streams trigger duplicate task creation

When EXAMPLE_NUMBER_OF_STREAMS is greater than 1, the uvc_event_cb callback is triggered separately for each stream index that connects. Each invocation calls start_uvc_frame_handling_tasks(), which unconditionally creates tasks for all streams. This results in duplicate task instances being created - for example, with 2 streams, the tasks would be created twice, causing 4 task instances instead of 2. The old code created tasks once at startup in app_main(), but the new event-driven approach lacks a guard to ensure tasks are only created once.

Fix in Cursor Fix in Web

@TDA-2030 TDA-2030 force-pushed the fix/add_frame_checks_for_uvc branch from 29d392e to 747a7ea Compare November 27, 2025 10:25
Copy link

@cursor cursor bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This PR is being reviewed by Cursor Bugbot

Details

Your team is on the Bugbot Free tier. On this plan, Bugbot will review limited PRs each billing cycle for each member of your team.

To receive Bugbot reviews on all of your PRs, visit the Cursor dashboard to activate Pro and start your 14-day free trial.


// Check mjpeg frame start
if (uvc_stream->dynamic.vs_format.format == UVC_VS_FORMAT_MJPEG &&
(payload_data[0] != 0xff || payload_data[1] != 0xd8)) {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: MJPEG SOI check accesses payload without bounds verification

The MJPEG SOI marker check accesses payload_data[0] and payload_data[1] without first verifying that payload_data_len >= 2. The header validation function uvc_frame_payload_header_validate only ensures packet_len > header_len, meaning the payload data could be just 1 byte. If a malformed packet arrives with exactly 1 byte of payload after the header, accessing payload_data[1] causes an out-of-bounds read.

Additional Locations (1)

Fix in Cursor Fix in Web

_stream_config.usb.uvc_stream_index = 1;
_stream_config.vs_format.h_res = frame_info_list[1][0].h_res;
_stream_config.vs_format.v_res = frame_info_list[1][0].v_res;
_stream_config.vs_format.fps = UVC_DESC_DWFRAMEINTERVAL_TO_FPS(frame_info_list[1][0].default_interval);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: Example accesses uninitialized frame_info_list for second stream

When EXAMPLE_NUMBER_OF_STREAMS > 1, start_uvc_frame_handling_tasks() accesses frame_info_list[1][0] to read h_res, v_res, and default_interval. However, this function is called immediately after the first UVC_HOST_DRIVER_EVENT_DEVICE_CONNECTED callback for stream index 0. The second callback (for stream index 1) increments device_count to 2 and breaks early due to the "Multiple devices connected" check, so frame_info_list[1] is never populated. This results in a NULL pointer dereference.

Fix in Cursor Fix in Web

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants