Skip to content

Merged changes ahead of v6.5.2.202603 - #296

Merged
fdesbiens merged 16 commits into
masterfrom
dev
Oct 2, 2026
Merged

fdesbiens merged 16 commits into
masterfrom
dev

Conversation

@fdesbiens

Copy link
Copy Markdown
Contributor

No description provided.

fdesbiens and others added 16 commits July 28, 2026 10:58
- ux_device_stack_interface_get: renamed to
  ux_device_stack_alternate_setting_get; added #pragma message.
- ux_utility_string_length_get: no upper bound on scan; can overread
  non-NUL-terminated buffers; added #pragma message directing callers
  to ux_utility_string_length_check().
- ux_utility_memory_free_block_best_get: already compiled out (#if 0);
  updated description to clarify it is dead code.
- ux_host_class_asix_reception_callback: ASIX driver redesigned;
  callback no longer invoked; updated description accordingly.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
A UTF-8 byte order mark sat in the middle of ux_test.c, immediately before
the first include rather than at the start of the file. GCC does not accept
one there and reports "stray '#' in program", which fails test_utility and
takes every regression test with it, so the suite could not be built at all.

Deleting the three bytes is the whole change. The file has no byte order
mark anywhere else, and it is the only source file in the repository that
carries one.

The suite builds again and reports 430/430 in default_build_coverage,
against no build at all before.

Assisted-by: Claude Code (Opus 5) <noreply@anthropic.com>
prepare_release.sh committed the version passes with a Co-authored-by trailer naming an
AI. That trailer asserts authorship an AI cannot hold: the human contributor signs the
ECA and is solely responsible for the contribution. It is already in the published
history, on the 6.5.1.202602 preparation.

The commits now carry their subject alone. A version pass is mechanical sed output, so
no agent produces it at run time; an agent that runs the script records its own
Assisted-by trailer on that run instead. The script also gains the AI disclosure line it
was missing.

Ran the patched script against a scratch clone targeting 6.5.2.202603. Both commits come
out with an empty trailer block, and the version constants and 31 port version strings
update as before.

Assisted-by: Claude Code (Opus 5) <noreply@anthropic.com>
* Refused PIMA responses that outrun the buffer sized for them

The receive buffer for GetObjectHandles is sized from the object count the
session already holds, and the returned count was checked only against the
caller's array. A count between the two walked the unpack cursor off the
allocation. Clamping it instead would have let a device that reports an
impossible count return success with a truncated list, which is the case
MSRC 72525 made an error, so the count is refused rather than trimmed. The
raw length is at least one ULONG, so the capacity cannot underflow.

The object count a device reports is no longer capped at 4096. Storage
holding more than that is ordinary, the cap silently truncated enumeration
for it, and the allocation it guarded is already covered by the overflow
safe arithmetic and the allocation failure check.

The early return for an undersized caller array now releases the receive
buffer, which a device could otherwise leak on every call.

430/430 default and 33/33 msrc_rtos pass. The MSRC 72525 test fails on the
clamp and passes on the refusal, so it stands as the guard for both.

Assisted-by: Claude Code (Opus 5) <noreply@anthropic.com>

* Added a regression test for the PIMA object handle count bound

The GetObjectHandles receive buffer is sized from the object count the session
already holds, while the only count check the suite exercised was the one
against the caller's array. A declared count between the two passed every
existing test, so the bound added with the fix had nothing standing on it.

The new test hooks the device side and sends a data container that declares one
handle count while carrying another, which the MSRC 72525 helper cannot do
because it grows the container to fit whatever it declares. It covers the two
counts a buffer must admit, the smallest over-report, an empty session where any
claimed handle is one too many, a count that still fits the caller's array, and
one filling that array exactly. It then checks that a refusal leaves the session
count and the memory pools untouched, and that the device still enumerates
afterwards.

430/430 default and 34/34 msrc_rtos. Removing the bound fails the smallest
over-report case, where the host unpacked three handles out of a buffer holding
two.

Assisted-by: Claude Code (Opus 5) <noreply@anthropic.com>

* Removed the now unreachable caller array check in object handles get

The caller's array is measured against the session object count on entry, and
the count a device reports is now bounded by that same session count. A
reported count can therefore never exceed the caller's array, so the second
check and the free it guarded could not run, and no test could reach them.

430/430 default and 34/34 msrc_rtos.

Assisted-by: Claude Code (Opus 5) <noreply@anthropic.com>

* Added a regression test for the PIMA info dataset bounds

The device, object and storage info parsers walk a cursor through a fixed size
receive buffer, advancing it by lengths the device declares. Each length is
checked against its own cap, and nothing checked the running total, so a device
could pass every per field test and still walk the cursor off the allocation.
The bounds added with the fix had no coverage.

The new test hooks the device side and sends whole datasets built field by
field, so a declared length owes nothing to what the response carries. Device
info is driven past the end through its third array and again through its tail
strings, storage info through a volume label that a maximum description leaves
no room for, and object info through keywords behind a filename and two dates.
Each parser also gets a modest dataset and, for device info, the largest one
that still fits, so the bounds are shown to admit what they should.

430/430 default and 35/35 msrc_rtos. Reverting any one of the three parsers
fails that parser's over-run case on its own.

Assisted-by: Claude Code (Opus 5) <noreply@anthropic.com>
* Bounded the video format walks to the block behind the selected format

A VideoStreaming input header reports wTotalLength for the whole class-specific
block, and the frame queries walk from the selected format rather than from the
header. Measuring the budget from the header while starting the walk further in
left the cursor able to run past the block, and clamping that same budget
against the configuration buffer from the format instead refused any device
whose streaming descriptors reach the end of its configuration.

The budget is now the block remaining behind the selected format, still held to
the configuration buffer so a device cannot enlarge it. The clamps where
wTotalLength is first recorded are unchanged.

A new regression test drives a two format MJPEG block that ends with the
configuration: it fails on the previous bound, and covers an oversized
wTotalLength and a format sitting past the reported block. 431/431 pass.

Assisted-by: Claude Code (Opus 5) <noreply@anthropic.com>

* Extended the video walk test to the format and interval queries

The test stood on the frame query alone. Reverting either of the other two
bounded walks left it passing, so two thirds of the change had no guard, and the
one negative case aimed at the format query returned the same status with and
without the bound because a walk off the end meets a short descriptor soon after.

The block now carries bytes behind the configuration holding a well formed format
descriptor, so reaching past the buffer is observed as the walk reporting success
and handing back a descriptor count read out of bounds, rather than inferred from
a status. The interval query gains the two refusals the frame query already had.

431/431. Reverting any one of the three walks now fails that walk's case on its
own; the format query reports frames 7 out of memory that is not the
configuration.

Assisted-by: Claude Code (Opus 5) <noreply@anthropic.com>

* Covered the two video walks the format test was not reaching

The test exercised three of the six functions this fix changes. Reverting
ux_host_class_video_input_format_get.c or ux_host_class_video_entities_parse.c
on its own left every test passing, so half the guards shipped unproven.

Both are now covered. The input header case hands the walk a configuration whose
VS_INPUT_HEADER declares more formats bytes than the configuration holds behind
it; without the guard the length is taken at face value and the formats address
published, and the test reports the status it got instead. The entities case
hands the walk a parser that reports the descriptor corrupted; without the guard
any non-zero status merely ended the loop and the caller was told the parse
succeeded.

Reverting each guard alone now fails this test: "oversized input header
accepted" and "a corrupted parse was reported as success" respectively. The
entity walk checks the device state and the streaming interface number before it
reaches a parser, so the fixture provides both.

ux_host_class_video_activate.c is still not covered. Its bound is the same
condition as the input header one above, and the status it propagates comes from
the entity walk, so both behaviours are proven; what is untested is that call
site composing them, which needs the simulated host and device stack.

Assisted-by: Claude Code (Opus 5) <noreply@anthropic.com>
* Refused HID report items whose declared data is not there

A report descriptor item declares how many data bytes follow its header. The
analysis recorded that declaration and let the item through without measuring it
against what was left of the descriptor, and the walk then advanced by it, so a
device could send an item whose data was not there and have the host read past
the descriptor it received.

The analysis now refuses an item whose header and declared data do not fit in
the remaining length, and the walk leaves the loop on that verdict rather than
advancing over the refused item and letting the per type parse overwrite the
status. The length check at the bottom of the loop caught the same descriptor
one item later, which is why the caller saw an error either way and the read in
between went unnoticed.

The test drives the analysis directly: a one byte descriptor declaring four data
bytes, a long item whose header alone exceeds what follows, a well formed item
and an empty descriptor. Reverting the bound fails it.

Assisted-by: Claude Code (Opus 5) <noreply@anthropic.com>

* Added a regression test for the report descriptor walk leaving the loop

The bounds test covers the item analysis alone. Reverting the break that stops
the walk on a refused item left it passing, so half the change had no guard.

That half resists a status assertion: a walk that carries on is caught by the
length check at the bottom of the loop one item later, and the caller sees the
same error either way. What differs is that the refused item is advanced over
and parsed first, reading past the descriptor. The new test drives a report
descriptor ending in a USAGE_PAGE header whose two declared bytes are not there,
and counts the refusals, which is the only trace the over-read leaves.

432/432. Reverting the break reports nine refusals where six are expected,
three enumeration attempts raising one apiece beyond the item itself.

Assisted-by: Claude Code (Opus 5) <noreply@anthropic.com>

* Closed the report descriptor walk on the standalone host path

The bound added to the item analysis only helps a caller that reads its verdict.
The standalone host enumeration state machine discarded it: it called the
analysis, ignored the return, and advanced the cursor by sizes the analysis had
just refused. Every shape the fix closed on the RTOS path was still open there.

Long items made that worse. The analysis reported their header size as the tag
mask, 0xf0, where the header is the three bytes it occupies, so the walk cleared
a three byte header by moving 240 bytes down the descriptor before parsing.

The standalone walk now stops on a refusal the way the other one does, and a
long item reports a header of 3. Nothing reads the format as a marker, so
narrowing it to a size changes no other caller.

432/432 default, 45/45 standalone host, 432/432 error check. The walk test runs
in both host builds and counts the refusals each reports: reverting the
standalone call site gives none where one is due. Reverting the header size
refuses a well formed long item outright.

Assisted-by: Claude Code (Opus 5) <noreply@anthropic.com>

* Aligned the standalone walk's length guard with the RTOS one

The two copies of the report descriptor walk disagreed on what a remaining
length has to cover. The RTOS copy counts the item header and its data, the
standalone copy counted only the data, so a descriptor whose last item ends
exactly at the buffer passed the guard and then had the header subtracted from
a length that no longer held it, wrapping it to near ULONG_MAX.

The analysis now refuses such an item before either walk reaches the guard, so
this was latent rather than live. The guards say the same thing regardless.

432/432 default, 45/45 standalone host.

Assisted-by: Claude Code (Opus 5) <noreply@anthropic.com>

---------

Co-authored-by: Frederic Desbiens <frederic.desbiens@eclipse-foundation.org>
…d form (#277)

Nothing enforced the disclosure convention. In the sibling repositories the
drift it exists to prevent returned twice, once when a port merged after the
normalisation pass carrying the older per-edit form, and once because that pass
had covered source files only. This repository is currently clean, and this
keeps it so.

Added scripts/check_ai_disclosure.sh, which rejects the superseded per-edit
form, a doubled comment marker, more than one disclosure line in a file, and
any spelling of the line that is not exact. It runs from repo_checks.yml, a
workflow with no path filter, because a source-path filter is what hid the
build files elsewhere.

The check passes on this repository as it stands. Each of its four rules was
confirmed to fail on a tree with that defect reintroduced, and to pass once it
was removed.

Assisted-by: Claude Code (Opus 5) <noreply@anthropic.com>
* Enabled pinned USBX regression and merged coverage on dev

Dev pull requests did not run the USBX regression gate, dependencies floated, and
coverage represented one filtered configuration. GCC 14 also rejected pointer types and
missing declarations in five test sources and the sample.

Pinned compatible dependencies and the reviewed ThreadX workflow/bootstrap, enabled all
17 profiles on every dev PR, and merged unfiltered raw coverage with exact source-line
union checks. Added descriptor-size regressions, failure-path runner tests, safe manual
subsets and Ubuntu 24.04 setup. Pages deployment remains limited to successful full
master runs. The check stays auto_tests / run_tests; eclipse-threadx/.eclipsefdn#18
remains disabled pending rollout to dev, so passing CI is not yet an enforced merge
requirement.

All 17 GCC 14 CMake/Ninja configurations built; 3,043/3,043 CTest cases passed. The
exact coverage union measured 66.3244% lines and 55.0718% branches. Eight runner tests,
real failure/subset probes, actionlint, ShellCheck and whitespace checks passed.

* Excluded reusable ThreadX workflow updates from Dependabot

Dependabot identifies reusable workflows by repository and workflow path. A
repository-only ignore entry would allow automatic changes to the workflow pin.

Matched the full dependency name so workflow and bootstrap revisions retain
explicit review together.

Verified the name against Dependabot's workflow parser and both workflow calls.
Whitespace checks passed.

Assisted-by: Codex (GPT-6-Sol) <noreply@openai.com>
The port stage matched a version only where "Version" was followed immediately by
four dotted numbers. A port writing three numbers, or a stray letter before the
first, was passed over and kept its old release, and the pass reported success
either way. Four ports across the suite had gone stale that way, one of them for
two consecutive releases.

The pattern now accepts three or four numbers and tolerates a leading letter. A
check follows it: any port header that names a release other than the one being
prepared is listed and the pass stops, so a port the substitution cannot reach
fails the release instead of shipping a version that misreports itself.

Verified both ways against a FileX tree: with a port whose string the
substitution cannot reach, the pass names the file and exits non-zero; with that
string corrected, all four ports update and the pass completes.

Assisted-by: Claude Code (Opus 5) <noreply@anthropic.com>
#276)

Running git blame on a file header returns a tree-wide copyright, disclosure or
version-constant pass rather than the commit that last touched the code. There
have been 4 such passes in this repository since 2024, and every header line
in the tree now points at one of them.

Added .git-blame-ignore-revs listing those commits. GitHub applies the file to
its blame view automatically; locally it takes one git config command, which
the file documents in its own header.

Additive. No source file is touched, and every listed commit was verified to be
an ancestor of dev.

Assisted-by: Claude Code (Opus 5) <noreply@anthropic.com>
* Linked USBX readers to published documentation

The USBX README sent readers to archived Markdown documentation.

I linked the overview to its published HTML guide and the general documentation entry to
the latest release.

Both replacement URLs returned HTTP 200; git diff --check passed.

* Pointed the overview link at the release it ships in

The link introduced here named 6.5.1, so 6.5.2 readers would have been sent to
the previous release's overview from the day it shipped.

It is pinned deliberately rather than floating, so it needs revisiting at each
release, which is the same handling the other version-bearing strings get. The
releases/latest link alongside it is unaffected.

Assisted-by: Claude Code (Opus 5) <noreply@anthropic.com>
Assisted-by: Codex (gpt-6-astra) <noreply@openai.com>
* Updated USBX contribution guide for current workflows

The existing guide omitted USBX build, test, and submission practices.

The guide now covers the project process and USBX C99, regression profiles,
coverage, attribution, and release practices. It also states that the
regression workflow does not run for pull requests to dev.

Local Markdown links resolve and git diff --check passed. Build tests were
not run for this documentation change.

Assisted-by: Codex (gpt-6-sol) <noreply@openai.com>

* Documented USBX Windows and dev regression workflows

The contributor guide omitted Windows simulator runners and described the old
master-only regression and coverage workflow.

The guide now covers both Windows ports, all 17 Linux CI profiles on dev, merged
coverage floors, pinned dependencies, and the updated host tools.

git diff --check passed. Build tests were not run for this documentation change.

* Added visible spacing between contributor setup steps

The ECA and Git setup items had no visible gap in GitHub Markdown.

An indented line break now separates them while preserving list numbering.

GitHub Markdown rendered the items in one list with the visible gap, and
git diff --check passed.

Assisted-by: Codex (GPT-6-Sol) <noreply@openai.com>
* fix: skip zero-length ISO OUT packets in audio read thread

A zero-length isochronous OUT transfer (actual_length == 0) carries no
audio data, but the read thread still recorded it as a frame: it wrote
frame_length = 0 into the slot at transfer_pos, advanced transfer_pos
and fired the frame_done callback.

Because the frame FIFO uses frame_length == 0 as the sentinel for an
empty slot, this made the slot indistinguishable from a free one.
The consumer then read at access_pos, saw frame_length == 0, reported
it as an underflow (UX_BUFFER_OVERFLOW) and did not advance access_pos.
As a result transfer_pos moved forward while access_pos stayed put,
permanently desyncing the producer/consumer positions.

Skip zero-length packets entirely: leave transfer_pos in place,
do not fire the callback, and re-issue the transfer at the same slot.
This preserves the "frame_length == 0 means empty slot" invariant
and keeps the transfer and access positions in sync.

* Brought the zero-length skip into the surrounding style

House style puts the brace on its own line and comments the reason, and the
change arrived with neither, plus a line of trailing whitespace.

The comment records why the skip matters rather than what it does: a zero length
frame is what marks a slot free, so logging one leaves a slot the reader never
drains while the transfer position advances past it.

No behaviour change.

---------
Co-authored-by: Frédéric Desbiens <frederic.desbiens@eclipse-foundation.org>
Assisted-by: Claude Code (Opus 5) <noreply@anthropic.com>
…292)

repo_checks has been red on dev since the contribution guide landed. The guide
explains the disclosure convention, so it spells the accepted text and discusses
AI assistance in prose, and the near-miss rule flags any line mentioning AI
assistance that is not the fixed text character for character. Documenting the
rule was therefore a violation of it.

The guide is now excluded from the scan, for the same reason this script already
excludes itself: it has to spell the text in order to explain it.

GUIX carried the same defect and took the same fix.

Verified that the check still catches what it is for: a second disclosure line
in a source file still fails it, and the tree passes without one.

Assisted-by: Claude Code (Opus 5) <noreply@anthropic.com>
USBX had no Windows regression capability. The suite built and ran on Linux only, so
defects specific to MSVC, to the LLP64 model and to the Windows simulator's tick
behaviour could be found only by hand, if at all.

This adds Win64 and Win32 simulator ports for the regression suite, with
architecture-selectable PowerShell entry points driving CMake, Ninja and the MSVC Build
Tools, so nothing depends on the Visual Studio IDE. Windows timing is supplied through
compile-time overrides rather than edits to shared test code: the simulator connect and
disconnect sleeps, the enumeration debounce and a tick scale factor are macros whose
Linux defaults reproduce the previous behaviour exactly, with the reduced values
confined to the MSVC branch of the build. Genuine portability defects surfaced along
the way and are fixed, including Win32 HID initialisation and pointer-width arithmetic.

Two consequences a reviewer would not predict from the diff. Twenty-seven regression
sources came back from the Windows editor carrying a UTF-8 byte order mark, which has
been stripped; those files are in the diff for their real changes, not for the mark.
And the PowerShell helper now resolves its ThreadX, FileX and NetX Duo locations to
sibling directories named after the upstream repositories, where it previously used
names from one local checkout and failed for everyone else; -ThreadXDir, -FilexDir and
-NetxduoDir override for a different layout.

Win64 430/430 and Win32 430/430, measured before the final comment, byte order mark and
default path commits. On Linux, 433/433 default, 35/35 msrc_rtos and 45/45
standalone-host on the current head, with the AI disclosure check passing.

Depends on eclipse-threadx/threadx#736, eclipse-threadx/filex#100 and
eclipse-threadx/netxduo#437, all merged.

Assisted-by: Codex (gpt-5.6-sol) <noreply@openai.com>
Assisted-by: Claude Code (Opus 5) <noreply@anthropic.com>
The tree still advertised 6.5.1.202602 everywhere, while the release going out is
6.5.2.202603.

The version constants in common/core/inc/ux_api.h move to 6.5.2.202603, and 31 of the
33 port headers follow. The remaining two are the Win32 and Win64 simulator ports added
in #273, which were written against the target version and already carried it.

433/433 default, 35/35 msrc_rtos, 45/45 standalone-host. No port header advertises
another release: the stale-port check added in #288 passes, which it would not have
done before that fix, since a port whose string is shaped unexpectedly used to be
skipped without a word.

Assisted-by: Claude Code (Opus 5) <noreply@anthropic.com>
@github-actions

github-actions Bot commented Oct 2, 2026

Copy link
Copy Markdown

Test Results

3 064 tests   3 064 ✅  35m 31s ⏱️
   17 suites      0 💤
   17 files        0 ❌

Results for commit 4a2356e.

@github-actions

github-actions Bot commented Oct 2, 2026

Copy link
Copy Markdown

Code Coverage

Package Line Rate Branch Rate Health
common.core.src 87% 82% ➖
common.usbx_device_classes.src 73% 61% ➖
common.usbx_host_classes.src 65% 52% ❌
common.usbx_host_controllers.src 0% 0% ❌
common.usbx_network.src 84% 68% ➖
common.usbx_pictbridge.src 66% 42% ➖
Summary 67% (21153 / 31745) 55% (12256 / 22176) ➖

Minimum allowed line rate is 66%

@fdesbiens
fdesbiens merged commit d947676 into master Oct 2, 2026
11 checks passed
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.

3 participants