Repository navigation
Conversation
The per-edit disclosure named the product and model, so every tool and every
model version appended another line rather than recognising the one already
there. Deduplication was by exact string, which a version bump defeats.
Fourteen files here carried more than one line.
Every file now carries exactly one line, fixed text naming no product:
Portions of this file were generated with AI assistance.
written with the comment character that file already uses. Precise attribution
stays on the commit, where the Assisted-by trailer is dated and attached to the
diff it describes. A header line cannot keep that record honest, because the
code it names gets rewritten and the line stays.
Comment-only, 70 files: every removed line was a disclosure line, every added
line is the fixed text, and no file is left with zero or more than one.
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. The script has not yet cut a release, so nothing has shipped with it. 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. 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 47 port version strings update as before. Assisted-by: Claude Code (Opus 5) <noreply@anthropic.com>
* Enabled complete GUIX regression and coverage checks on dev Linux and Studio View validation did not run for dev changes. GCC 14 exposed regression build failures, unchecked API mappings were incomplete, and coverage omitted optional configurations. Enabled the full 18-configuration Linux matrix and Studio View on dev, preserved the existing Windows and manual packaging workflows, and pinned the reviewed ThreadX bootstrap and reusable workflow. Corrected profile definitions, unchecked API declarations, and test fixtures. Added failure propagation, bounded setup, JUnit retention, raw coverage unions, partial-run labeling, and measured line and branch floors. Pages remains restricted to successful complete master runs. CMake/Ninja and CTest passed 1,849/1,849 tests across all configurations. Merged coverage reached 46,115/46,888 lines and 25,635/26,675 branches. Ten report tests and checkout, dependency, CTest, and missing/empty coverage failure probes passed. Actionlint, ShellCheck, and whitespace checks passed. Remaining coverage gaps and exact profile measurements are documented. Assisted-by: Codex (GPT-6) <noreply@openai.com> * Fixed fresh ThreadX checkout validation A new clone created without checkout has an empty working tree. The dependency cleanliness check treated its absent files as local modifications and stopped CI before configuration. Distinguished new clones from existing checkouts, checking cleanliness before switching revisions only when a working tree already exists. Fresh dependency setup passed. Repeated setup preserved the library modification time, and an intentionally modified checkout was rejected. ShellCheck passed. Assisted-by: Codex (GPT-6) <noreply@openai.com> * Staged Linux regression compilation within runner time limits The hosted Linux runner timed out its 15-minute build step after completing the first large configuration and most of the second. No CTest or merged report could run. Staged the full 18-configuration build across the reusable workflow’s bounded steps: the default profile during installation, five profiles during build, and the remaining 12 before the complete CTest run. Tests run concurrently while raw coverage collection stays serial. Each stage retains its profile list, timing, and diagnostics; any build failure still stops the job. The complete local staged run passed 1,849/1,849 tests. Merged coverage matched the serial run exactly: 46,115/46,888 lines and 25,635/26,675 branches. Ten report tests, actionlint, ShellCheck, and whitespace checks passed. Assisted-by: Codex (GPT-6) <noreply@openai.com>
…so (#191) * Normalized the AI disclosure line in the remaining non-source files The repository-wide pass covered source files only, so these kept the older per-edit form of the disclosure comment, which names a product and a model version. Every source file already carries the fixed line. Replaced it with the fixed text, using the comment syntax each file already uses. Comment-only. 3 files, one line each. The repository now holds 73 files carrying exactly one disclosure line and none carrying the old form. Assisted-by: Claude Code (Opus 5) <noreply@anthropic.com> * Added a check that keeps the AI disclosure comment in its one accepted form Nothing enforced the disclosure convention, so 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, leaving build files and scripts untouched for months. 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 the first time. The check passes on this branch. 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>
* Fixed the unbounded pixelmap index in the binary resource theme loader _gx_binres_pixelmap_table_load() indexes the pixelmap table with map_id, read verbatim from the resource, while the table is sized from the theme header's pixelmap count. Nothing relates the two, so a resource declaring an index beyond the table drove the loop past its end, storing GX_NULL and then a pixelmap pointer there. The status of _gx_binres_one_pixelmap_load() was examined only after those stores, so a failed load also stored an unset pointer. An index that does not fit the table is now rejected, and the status is checked before any store. The font sizing and load divergence reported alongside this defect is not addressed here: its write is closed by the arena bound added with the glyph range fix. All ten binary resource regression tests pass. There is no regression test for this defect yet. The function is static and only reachable through gx_binres_theme_load(), which needs a malformed theme fixture with a pixelmap header at a known offset. Assisted-by: Claude Code (Opus 5) <noreply@anthropic.com> * Initialised the pixelmap table locals and reworded the bound comments The two comments added with the pixelmap index bound described how the loop used to behave rather than what the code does now, and map_id and pixelmap were still declared without initialisers although static analysis cannot see that _gx_binres_one_pixelmap_load() sets both on every success path. Both locals are now initialised (MISRA C:2012 Rule 9.1), the comments state the invariant each check enforces, and the file carries the AI assistance line. All 10 binres regression tests pass in default_build_coverage, with no warnings from gx_binres_theme_load.c. Assisted-by: Claude Code (Opus 5.5) <noreply@anthropic.com>
The theme loader sized its buffer for the theme it was asked for and allocated it even when the resource holds no theme with that id, giving a buffer of zero bytes. The load pass has no bound of its own, so for an id past the last theme it read beyond the theme table and wrote a theme record and its tables into that buffer. The theme count comes from the resource, so a theme file declaring fewer themes than the application loads was enough, given an allocator that serves a request for zero bytes. _gx_binres_theme_buffer_allocate() now refuses a theme id at or past the declared theme count with GX_INVALID_VALUE, before anything is allocated. A negative id converts to a count no resource can declare and is refused by the same test. guix_binres_theme_id_bound_no_output loads through an allocator that serves zero bytes and guards every allocation: it segfaults on dev and passes with the fix. 18/18 binres tests; 734/735 in default_build_coverage, the one failure a timeout in guix_single_line_text_input_24xrgb, which does not load resources. Assisted-by: Claude Code (Opus 5.5) <noreply@anthropic.com>
* Fixed the glyph range underflow and out-of-bounds write in the binary resource loader
A font page header carries its glyph range as two values read verbatim from the
file, and every consumer turns them into a count as last - first + 1. Only the
upper end was ever validated, so a page declaring first_glyph above last_glyph
made that count underflow to a very large number. The sizing pass then multiplied
it by a glyph record size without an overflow check, the product wrapped to a
small value, and the buffer it reserved was far smaller than the load pass went
on to fill with glyph records taken from the file.
The buffer belongs to GUIX rather than to the caller, so an application had no
way to size it defensively.
Four changes, each closing a separate step of that chain:
- Validate the range in _gx_binres_page_header_load(), which is the one point
both the sizing pass and the load pass read a page header through, so a
malformed range cannot reach either. This alone stops the reported chain.
- Use the existing GX_UTILITY_MATH_UINT_MULT for the three reservation
multiplies. Only the accumulation was checked before, and the normal,
compressed and kerning formats each had an unchecked multiply, so a fix
confined to one of them would have left the other two reachable.
- Compute the load pass count in the same width as the sizing pass. The two
disagreed, UINT against USHORT, which is the second half of the defect:
space was reserved for one count and written for another.
- Reserve glyph space through a single helper that checks the request against
gx_binres_buffer_size and only advances gx_binres_buffer_index once the
records are known to fit. The index used to be advanced by the full amount
before any write, so aborting on a bad glyph magic number left it past the
end of the arena and a later load continued from a corrupted offset.
The record size is deliberately the second argument to the checked multiply,
because that macro divides by that argument. A sizeof() is never zero; a glyph
count is exactly zero when first_glyph is last_glyph + 1, which would divide by
zero if the range check above were ever removed.
Both public entry points are covered without touching either: gx_binres_theme_load
and gx_binres_font_load share _gx_binres_font_buffer_size_get and
_gx_binres_one_font_load through gx_binres_loader.h.
Verified against the reported input on the ABI it was reported on, a 32-bit build
where sizeof(GX_GLYPH) is 12. A page with first_glyph 0xEAAAAAAB and last_glyph 0
gives the count 0x15555556 from the report; before this change the sizing pass
returned GX_SUCCESS having reserved 32 bytes, and now it returns GX_INVALID_FONT.
Well-formed fonts are unaffected, reserving the same 36 bytes for a single glyph
and 1164 for the range 0x20 to 0x7E as before. With the range check disabled on
purpose, the same input stops at the checked multiply with GX_MATH_OVERFLOW, so
the later layers are live rather than decorative.
Builds clean with -Wall -Wextra across the extended-Unicode, kerning and default
configurations, and the 1315 files of common/src still compile with no new
warnings.
* Added a regression test for the binary resource font glyph range
Drives _gx_binres_font_buffer_size_get() over a hand-built one-page font, which
is where the glyph range is first turned into a count. Both public entry points
reach it, gx_binres_theme_load() and gx_binres_font_load(), so covering it covers
both.
Four cases. An inverted range using the span from the report, and one inverted by
a single glyph so the boundary is covered rather than only an extreme; both have
to be rejected, because that is what stops the count underflowing and the
reservation multiply wrapping. Then a single glyph and the printable ASCII range,
which have to keep succeeding, with a check that the wider range still reserves
more space than the single glyph. A wrapped multiply shows up as a reservation
that is far too small rather than as a failure status, so comparing the two sizes
is what would catch it.
Reverting the fix makes both inverted ranges return GX_SUCCESS instead of
GX_INVALID_FONT, so the test measures what it claims to.
The font is built in the test rather than loaded from a resource file, because
the size pass reads only a font header and a page header, and the defect is in
the arithmetic over two fields of the latter. The range check is not conditional
on the extended Unicode configuration: an inverted range underflows the count
whatever the width of GX_CHAR_CODE, and only the subsequent multiply wrap needed
a wide one.
* Extended the binres glyph range tests to the load pass and every format
The reporter supplied regression inputs for the advisory: an inverted range in each
of the three glyph formats, a span wider than a USHORT can count, and a well-formed
control. The test on this branch drove only the size pass, over the normal format
alone, so the writers, the compressed and kerning formats, and the width the load
pass counts in were all unexercised.
The cases move into a shared header that three tests run, one per font
configuration. The configurations build disjoint demo sets and none compiles every
glyph format, so a single test cannot cover them all: the default build covers the
normal and compressed formats, and the extended Unicode and kerning builds each
cover what only they compile. Extended Unicode matters on its own, because a 32-bit
character code is what lets a page declare a span wide enough to wrap the
reservation multiply. Every case now drives _gx_binres_one_font_load() as well as
_gx_binres_font_buffer_size_get(), and a well-formed font is additionally loaded
into a buffer too small for its glyphs, which is the only way to reach the writers'
own bound while the range check above them is in place.
The span case asserts that a range wider than 0xFFFF is accepted, not rejected.
That is worth stating because the input was offered as one a fix should reject.
Such a span is well formed, and a character code wide enough to index it renders
it, so rejecting it would refuse a usable font; what was wrong was counting it in a
USHORT in one pass and a UINT in the other. The case pins both halves: the size
pass reserves for the whole span, and the load pass refuses a buffer that holds one
record, which it can only do if it counted the whole span too.
733 tests in the default build, 3 in extended Unicode, 4 with kerning, all passing
except five in the default build that fail on dev as well, each reporting a missing
golden file. Reverting the loader fix fails 7 cases in the default and extended
Unicode builds and 9 with kerning, so the tests measure what they claim.
The suite already builds -m32, so these run on the 32-bit ABI the issue was
reported on, where sizeof(GX_GLYPH) is 12.
Assisted-by: Claude Code (Opus 5) <noreply@anthropic.com>
* Bounded the font page record against the load buffer
Change 4 of this branch bounded the three glyph writers against
gx_binres_buffer_size but left the page's own GX_FONT record taken from the
buffer unchecked, which is the one write in the load pass that could still pass
the end of the arena. It is not reachable today: both passes derive the page
count as a USHORT from the same font header and both follow gx_font_header_data_offset
the same way, so they agree on how many pages there are. It is reachable the
moment they stop agreeing, which is exactly the failure this branch exists to
make impossible.
The record is now reserved through the same helper as the glyphs. The helper
reserves records of a given size rather than glyphs specifically, so it is
renamed _gx_binres_buffer_reserve.
The test gains a filled margin after the buffer the loader is given, checked
after every load. A status on its own cannot show this: with a buffer too small
for the page record the glyph reservation fails too, so the load reports a
rejection whether or not the record was written first. The margin is what
distinguishes them. Removing the bound leaves the margin broken in two cases and
the tests red; restoring it leaves them green.
733 tests in the default build, 3 in extended Unicode, 4 with kerning, unchanged
from before this commit apart from the new assertions.
Assisted-by: Claude Code (Opus 5) <noreply@anthropic.com>
* Bounded binary resource reads by the extent of the resource The binary resource loaders received the resource as a bare pointer, and GX_BINRES_DATA_INFO had no field to carry its length. Every read was therefore performed at an offset and for a count the resource itself declared, against a buffer whose extent the loader did not know. A resource declaring more than it holds was read past its end, and a glyph's map offset became a pointer that was kept in the loaded font and followed at draw time, so an attacker chose what memory was read and rendered. GX_BINRES_DATA_INFO now carries the extent, and _gx_binres_range_check() gates every header the loaders read, the offsets a resource supplies for its font and pixelmap data, the standalone resource offset table, and the three string table walks. The header loaders already returned a status that most callers dropped; those now propagate it, because a rejected header must not be left to be caught by a magic number check over an uninitialised struct. The extent reaches the loader through six new entry points that take it: gx_binres_theme_load_ext, gx_binres_font_load_ext, gx_binres_pixelmap_load_ext, gx_binres_language_count_get_ext, gx_binres_language_info_load_ext and gx_binres_language_table_load_ext2, the last named so because _ext is already the GX_STRING form. The existing entry points keep working and derive an extent from the size the resource declares, which catches truncation but not a resource built to mislead, since the same attacker chose that size. That split follows the deprecated string API: GX_DISABLE_DEPRECATED_BINRES_API turns off the pragma pointing at the _ext forms. Two limits worth knowing. A resource declaring a data size of zero is treated as having an unknown extent rather than a 20-byte one, so the older resources that leave the field empty keep loading unbounded as they always have. And the standalone format that gx_binres_font_load and gx_binres_pixelmap_load read carries no total size at all, so those two derive nothing and only their _ext forms are bounded. Verified under AddressSanitizer on a 32-bit build. Through the _ext entry points with a true length, a page declaring 101 glyph records in a 193-byte resource is rejected with GX_INVALID_FORMAT where it previously read off the end, and so is the span of 65537 from the advisory for this defect; well-formed resources still load. Through the old entry points, the same input with a correctly declared size is rejected and a valid one loads. 733 tests in the default build, 3 in extended Unicode, 4 with kerning, all passing except five that fail on dev as well, each reporting a missing golden file. Neutering the range check turns the four new cases red. Assisted-by: Claude Code (Opus 5) <noreply@anthropic.com> * Deprecated the unbounded binary resource loaders and bounded the rest The loaders that take only an address cannot bound a resource built to mislead, so a replacement was added for each. Nothing removed the old ones, and the manual kept pointing at them. Several reads reached by the new bound were also still unchecked: a glyph's kerning table, a theme's color and palette tables, and every record the theme loader placed in its buffer outside _gx_binres_buffer_reserve. GX_DISABLE_DEPRECATED_BINRES_API now compiles the six old loaders out, following GX_DISABLE_DEPRECATED_STRING_API, and disable_deprecated_binres_api_build covers that configuration. Both binres tutorials use the _ext loaders; the STM32F439 path needs the size of the image programmed to flash, so it gains a define for it. A glyph's data block is bounded as a whole rather than by its map offset alone, which a Studio font's alignment padding put outside the block; the kerning, color and palette tables are bounded before being kept as pointers; the theme buffer is released on every failure rather than one; and the pixelmap, theme, font table and pixelmap table records go through the reserve, so a disagreement between the two passes ends in a rejected resource. The char language table loader derives an extent like its siblings, and re-checks the allocation pass has already made were removed rather than left to imply they can fail. guix_binres_ext_api_no_output drives all six _ext loaders and both layers: parameter checks, a load at the true length, every length short of it, each byte corrupted in turn, and an allocator that guards what it hands out. 137/137 binres, theme, font, glyph and pixelmap tests; 729/734 in default_build_coverage, the 5 failures identical on this branch before this change; 11 of the 12 binres files at 100% line coverage and the theme loader at 98%. One case in that test, the truncation sweep over a theme whose pixelmap data the resource misreports, needs the fix on fix/binres-pixelmap-map-id and fails until that branch merges. It passes with that branch applied. Assisted-by: Claude Code (Opus 5) <noreply@anthropic.com> * Guarded the binres ext parameter checks against disabled error checking The validation test asserts GX_PTR_ERROR, GX_INVALID_VALUE and GX_INVALID_SIZE for null pointers, a negative theme id and zero sizes, and GX_SYSTEM_MEMORY_ERROR for a missing allocator. Every one of those statuses is raised by the _gxe_ wrappers, which GX_DISABLE_ERROR_CHECKING compiles out. In disable_error_check_build the arguments therefore reached the loaders, which dereferenced a null root address and then the deliberately invalid pointer the non-null checks are fed. Those checks, and the invalid pointer itself, are now compiled only when error checking is enabled. The allocator is still restored outside the guard, because the rest of the test loads through it. disable_error_check_build::guix_binres_ext_api_no_output segfaulted in _gx_binres_resource_header_load before the change and passes after, in both disable_error_check_build and default_build_coverage, with no new warnings. Assisted-by: Claude Code (Opus 5) <noreply@anthropic.com>
The install step here does not only install: the regression suite is staged across the install, build and test steps to keep each inside its limit, so this one also builds default_build_coverage. The template's ten minutes is sized for a consumer that installs packages and stops, which every other one does. Measured on this job: the best install step on record took 8.2 of the ten minutes, and four have been lost at the cap. That is a chronically marginal budget rather than an unlucky one, so the packages and the ThreadX checkout only have to run slow for the build to lose its remainder. Repinned to the template revision that takes install_timeout_minutes, and asks for twenty. Nothing else changes, and no other consumer is affected: the input defaults to the ten they have now. The build does not belong in this step at all, which is issue #205. Assisted-by: Claude Code (Opus 5) <noreply@anthropic.com>
The regression job clones ThreadX to get its build bootstrap, under a 180 second budget, and that budget has run out twice on dev. The step cap then absorbed the pinned-revision fetch that follows, so a run spent its whole ten minutes on two network commands and never reached a test. The clone now filters blobs. Only one pinned revision is ever checked out, so the history's blobs are never wanted: the full pack is 26.86 MiB, blobless is 3.7 MiB to clone and 9.3 MiB once the revision's own blobs arrive on checkout. Nothing else changes, and the revision pinning is untouched. Measured end to end from an empty directory: clone 0.76 s, checkout 2.75 s, HEAD matching the pinned revision and the bootstrap script in place. Run through run.sh itself with the existing checkout removed, which produced the same. This does not make a dead link work. It cuts what the budget has to cover by roughly two thirds, which is what turned a slow mirror into a failed run. Assisted-by: Claude Code (Opus 5) <noreply@anthropic.com>
…207) The build step builds five configurations, because the suite is staged across the install, build and test steps. The best run on record took 11.8 of the default fifteen minutes, which is 79 per cent of the cap, and six advisories added regression tests this week, so the margin is closing rather than steady. Repinned to the template revision that takes build_timeout_minutes, and asks for twenty five. The install timeout set earlier is carried across unchanged. No other consumer is affected: the input defaults to the fifteen they have, and their build steps measure 0.5 minutes for LevelX, 1.1 for FileX and 6.4 for USBX. Moving that work out of these two steps is issue #205, which the measurements now point at the test step for, since it uses 30.9 of its sixty minutes. Assisted-by: Claude Code (Opus 5) <noreply@anthropic.com>
* Fixed the JPEG marker ordering and segment validation defects
Covers both JPEG advisories, since one dispatcher carries all of it.
The marker loop was a stateless switch. Nothing stopped a second frame header,
and the scan case broke out of the switch rather than the loop, so scanning
continued afterwards and a later frame header re-entered the parser once the
output buffer had been allocated and its geometry latched. The pixelmap was then
published from a mixture of both generations of state.
Separately, each segment handler read a fixed number of bytes while only the
declared segment length had been bounded against the buffer, the scan header's
return status was discarded so its component-count check had no effect, and the
chroma sampling factors were never checked for zero, which left a decode loop
able not to terminate.
Five changes:
- Track the frame header and the scan in the dispatcher. A second frame header
is refused, as is one arriving after the scan, and a scan is refused when no
frame header preceded it or when one has already been decoded.
- Publish the pixelmap from the latched output geometry alone. The dimensions
and the reported size came from different generations of state, so the
structure described no allocation that had ever existed, and a consumer
bounding its reads by that size was out of bounds as well.
- Validate each segment's declared length against what the handler will read,
before reading, in all four handlers. The quantization table case had the
check already but ran it after the read it was guarding, and its length
subtraction could underflow on a segment shorter than its own length field.
- Capture the scan header's status, and validate the component count before
storing it rather than after, so a rejected count neither survives in the
decoder state nor gets ignored by the caller.
- Reject zero sampling factors. The horizontal factor is bounded at the JPEG
maximum of 4 rather than at 2, because 4:1:1 images carry 4 and decode
correctly today; the vertical factor keeps its existing limit.
Verified against the JPEGs already in the repository and against malformed files
derived from one of them, so that only the property under test is malformed.
No regression: all 38 decodable JPEGs under test, tutorials and samples still
decode, and the four subsampling variants in guix_studio_test are byte for byte
unchanged at 330x199 with the reported size equal to the allocation. The two
files rejected for exceeding the resolution limit were rejected identically
before this change.
Malformed inputs, before and after: a zero horizontal and a zero vertical
sampling factor both ran without terminating and are now rejected; a short scan,
a short frame header and a short restart interval were accepted and are now
rejected; an over-range component count in the scan returned success and now
returns failure, which is the discarded status being acted on; a duplicate frame
header and a scan with no frame header were accepted and are now rejected.
Two limits of that verification, since they matter for what still needs checking.
A frame header appended after the scan is not reached by the marker loop in these
files, so the after-scan branch of the ordering check is not exercised by them,
and the published mismatch itself was therefore not reproduced here. Truncating a
segment inside a large file makes the handler read past the declared segment but
still inside the allocation, so those reads are not visible to a sanitizer; the
reporter's minimal files are what demonstrate them.
Builds clean with -Wall -Wextra, and all 1315 files of common/src compile with no
new warnings.
Assisted-by: Claude Code (Opus 5) <noreply@anthropic.com>
* Added marker-ordering and segment-length cases to the JPEG decode validation test
The existing test already drives _gx_image_reader_jpeg_decode() over a table of
malformed files and requires each to be rejected, so these belong there.
Six files, 14 to 40 bytes, each isolating one defect: a DQT, a frame header, a
scan header and a restart interval whose declared length is shorter than the
fixed number of bytes the handler reads; two frame headers, since a JPEG carries
one and accepting a second let a file change the image dimensions after the
output buffer had been allocated; and a scan with no frame header before it.
The DQT case declares five bytes rather than the two its length field alone
occupies. A segment declaring only its own length is not malformed - it simply
carries no tables, and the handler correctly accepts it - so the length has to be
short rather than absent for the case to exercise the guard it claims to.
Reverting the fix makes the test fail.
Two parts of that fix are not covered here. The zero sampling factor needs a file
that reaches decompression, which needs valid quantization and Huffman tables
rather than a handful of bytes. The pixelmap publish consistency needs a file
that decodes successfully and then presents a second frame header, and the
malformed-input table only expects rejections. Both remain covered by the
verification recorded in the pull request.
Assisted-by: Claude Code (Opus 5) <noreply@anthropic.com>
* Covered the remaining JPEG validation paths in the decode regression test
Three branches added by the decoder fix were never reached: a frame header too short
for its components, a scan header shorter than three bytes, and a scan too short for
its components. The short scan case carried no frame header, so the ordering check
rejected it before the length check it names. Nothing exercised a zero sampling
factor, a DQT length below two, or an over-range scan component count.
The short scan case now follows a valid frame, and six cases cover the rest.
Every branch the fix added is now reached, each once. With the fix reverted, the
zero-factor cases hang, and the short scan, both component-length cases and the
component count return GX_SUCCESS. The two DQT cases still return an error without
the fix and only show the over-read under a sanitizer. All 4 JPEG tests pass.
Assisted-by: Claude Code (Opus 5.5) <noreply@anthropic.com>
* Bounded the JPEG luma sampling product to the size of the luma block buffer
The sampling check accepted h up to 4 with v up to 2, but gx_jpeg_Y_block holds 256
bytes, four 8x8 luma blocks, and the MCU loop places the second block row at a fixed
offset of 128. With h=3 or h=4 and v=2, luma blocks were written up to offset 383 and
pixel output read up to offset 511, past the Y block and into the neighbouring
fields of GX_JPEG_INFO. Both stay within the structure, so the result is a wrongly
decoded image rather than a heap overrun, and both decoded with GX_SUCCESS.
The check now also rejects h * v > 4, which still admits 4:1:1 (h=4, v=1) and 4:2:0.
The comments added with the fix, in the decoder and the test, are reworded to state
what each check enforces.
Instrumenting the block writer, h=2 v=2, h=4 v=1 and h=1 v=2 stay within 256 bytes;
h=3 v=2 and h=4 v=2 reached 319 and 383. Three new cases (3x2, 4x2, 1x3) are rejected,
and gcov shows every branch of the sampling check taken. All 4 JPEG tests pass;
727/732 in the default suite, the same five failing without this branch.
Assisted-by: Claude Code (Opus 5.5) <noreply@anthropic.com>
* Fixed the duplicate IHDR heap out-of-bounds write in the PNG decoder
The chunk dispatch loop tracked whether IDAT had been seen, and whether a tRNS
had already supplied transparency, but nothing stopped IHDR being read a second
time. A PNG carries exactly one IHDR and it comes first, so a file that places
another one after IDAT rewrote the width, height, bit depth and pixel depth after
the decoded data buffer had already been allocated and filled from the original
values. The unfilter pass then derived its own extent from the new dimensions and
walked the old allocation, moving image bytes past the end of it.
The inflate path was never the problem: it bounds every write against the
allocation and even asserts that the decompressed length matches. Only the pass
after it worked from dimensions that could have changed underneath.
Two changes:
- Reject a chunk order that a valid PNG cannot have. A second IHDR and a second
PLTE are refused, and IDAT before any IHDR is refused, since that is where the
dimensions the buffer is sized from come from. This is the root cause, and
the flags mirror the idat_done flag already in the loop.
- Have the unfilter pass confirm its walk fits the data that was actually
decoded before it starts. The two are normally the same and the check costs
nothing when they are, but it is what keeps any future divergence between the
allocation and the current dimensions from becoming a write past the end
rather than a rejected image.
The second check is written as a division so there is no product to overflow.
This also settles what the decoder hands back. A duplicate IHDR made
gx_pixelmap_width and gx_pixelmap_height describe the second header while
gx_pixelmap_data_size still described the first allocation, so the returned
pixelmap claimed more pixels than it owned. Rejecting the duplicate removes the
cause, and the unfilter check stops the inconsistent structure being reached even
if dimensions ever diverge again.
Verified with an independently built reproducer rather than the reporter's, on the
same shape they described: an 8x8 grayscale PNG followed by a second IHDR of
16x256, 94 bytes, the same size they reported. Before this change AddressSanitizer
reports a 16-byte memmove 0 bytes after the 72-byte allocation, in
_gx_image_reader_png_unfilter at the memmove, called from the decoder, on memory
allocated in _gx_image_reader_png_IDAT_chunk_read - the same three frames as the
report. After it, the file is rejected with GX_INVALID_FORMAT and no sanitizer
report. A duplicate IHDR before IDAT, an IDAT with no IHDR, and a re-entered PLTE
are rejected the same way. Well-formed single-IHDR and single-PLTE files still
decode to an identical 8x8 pixelmap with data_size 64.
With the chunk-ordering check disabled on purpose, the same input is stopped by
the unfilter check with no sanitizer report, so the second layer is live rather
than decorative.
Builds clean with -Wall -Wextra; the one warning the file still emits is a
pre-existing pointer-to-integer cast unrelated to this change, and all 1315 files
of common/src compile with no new warnings.
* Added chunk-ordering cases to the PNG decode validation test
The existing test already drives _gx_image_reader_png_decode() over a table of
malformed files and checks the status each one returns, so the chunk-ordering
cases belong there rather than in a new test.
Four files, each 44 to 129 bytes and built from a well-formed 8x8 grayscale base
so that only the ordering under test is wrong: a second IHDR after IDAT, which is
the reported defect; a second IHDR before IDAT, so the ordering state is tested
rather than only the after-IDAT case; an IDAT with no IHDR, where the buffer would
be sized from nothing; and a re-entered PLTE, which the loop also accepted.
All four are expected to return GX_INVALID_FORMAT.
The pixel data in the base image is zero on purpose. When a later chunk enlarges
the image, the unfilter pass reads its per-row filter byte at the wrong stride,
and zeros keep those misaligned reads valid, so the walk continues to the end of
the enlarged image instead of stopping early on an invalid filter type. With
non-zero pixel data the file is rejected before the defect is reached, and the
case would pass for the wrong reason.
Reverting the fix makes the test dump core rather than report a failed
assertion, since the first of these files then writes past its allocation.
Valid PNGs are already covered by the decode and draw tests elsewhere in this
suite, so a regression that rejected well-formed files would show up there.
* - fixed MISRA errors by removing semicolons from macro like defines. - removed trailing white space characters. * - main change is the semicolon added in line 2705 (as requested per review <#170 (review)>). - all other changes are trailing whitespaces removed automatically by VSCode.
Every command in install.sh reaches the network and each already carries a timeout, but none of them is retried, so one slow mirror minute ends the run. That is what happened three times today: the package install is 41 MB across 34 packages and ran out of its six minutes partway down the list, twice while fetching the 32-bit GCC runtime. The three network commands now run under a retry loop with three attempts and a widening pause, which is the shape ThreadX uses in tx_ci_common.sh and which this script never picked up. The worst case is unchanged: if all three attempts fail the run fails, as it did with one attempt. What the loop buys is the common case, where the mirror is slow once and not twice, and a log that names the attempt rather than a bare exit 124. This is worth having only because the step has room for it now. At the ten minutes the install step used to have, a second full attempt could not have fitted alongside the configuration build that also happens in this step. Assisted-by: Claude Code (Opus 5) <noreply@anthropic.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>
#190) 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 8 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 GUIX readers to published documentation The GUIX README and Studio guide commands opened archived Markdown pages. I linked readers and Studio users to published HTML guides. The general documentation entry now follows the latest release. All replacement URLs returned HTTP 200; git diff --check passed. Assisted-by: Codex (gpt-6-astra) <noreply@openai.com> * Pointed the documentation links at the release they ship in The links introduced here named 6.5.1, so the installer menu entries and the Studio Help item would have sent 6.5.2 users to the previous release's documentation from the day it shipped. Four links across the README, the Studio Help handler and two installer menu entries now name 6.5.2. These are pinned deliberately rather than pointed at a floating path, so they need revisiting at each release. That is the same handling the rest of the version-bearing strings get. Assisted-by: Claude Code (Opus 5) <noreply@anthropic.com>
* Updated GUIX contribution guide for current workflows The existing contributor guide omitted GUIX and GUIX Studio build, test, and submission practices. The guide now covers C99, all 18 Linux regression configurations, merged coverage gates, Windows Studio validation, attribution, and release practices. 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> * 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>
* Fixed MISRA rule 12.7 violation on GX_FIXED_VAL_... macros by replacing shift operations on signed operands with mul/div operations. On these lines also rule 19.10 got addressed. * Kept the arithmetic shift and made the radius signed at the call site Replacing the shifts with division changes the result for every negative operand: a right shift on a signed value rounds toward negative infinity and division rounds toward zero. Measured over -5000 to 5000 the two agree on every non-negative value and differ by one on every negative one, which is an off-by-one pixel anywhere a fixed-point coordinate goes negative. The shifts are restored. The parentheses around the macro arguments are kept, because they fix a real defect. In GX_FIXED_VAL_TO_INT the cast bound to the first token of its argument, so at the one call site that passes a product the cast landed on the radius rather than on the product. That is also why parenthesising alone fails to build: the radius is UINT, the sine and cosine are not, so the product became unsigned and -Wsign-conversion rejects it. The cast that used to happen by accident is now written at the call site, where it is visible and where the neighbouring branch already does it. The result is identical to the code before this change. MISRA 12.7 is deviated from here deliberately. The rule asks for unsigned operands; making these unsigned gives a logical shift, which is the wrong arithmetic for a signed fixed-point value, and changing the operator loses the rounding. Assisted-by: Claude Code (Opus 5) <noreply@anthropic.com> --------- Co-authored-by: Frédéric Desbiens <frederic.desbiens@eclipse-foundation.org>
…210) 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. The exclusion is by name rather than by extension, so a source file is still scanned whatever it is called, and Markdown that is not the guide still is too. Verified that the check still catches what it is for: a second disclosure line in a source file and the superseded per-edit form both still fail it, and the tree passes with neither present. Assisted-by: Claude Code (Opus 5) <noreply@anthropic.com>
… asset (#211) The README linked the 6.5.1.202602a installer by its full asset URL, with the version in both the tag and the file name. Nothing in the release process updates it: the version pass covers the library and the port headers, and the Studio script covers studiox.rc and the Inno script, so this line has to be edited by hand or it advertises the previous release for the life of the new one. It now points at the releases page, which always carries the current installer as an attachment. That removes the per-release edit rather than moving it forward one version, and matches how the other repositories link to it. Assisted-by: Claude Code (Opus 5) <noreply@anthropic.com>
Two GUIX timer checks cast null arguments to CHAR **, which is incompatible with the const-qualified ThreadX timer information API. The calls now pass the untyped null macro, which is valid with both the earlier and const-qualified ThreadX declarations. Both changed sources passed C99 syntax builds with warnings treated as errors against the updated ThreadX headers. Assisted-by: Codex (gpt-6-astra) <noreply@openai.com>
Prepared by scripts/prepare_release.sh 6.5.2.202603, in the two commits it makes: the version constants in common/inc/gx_api.h, then the version string every port reports at run time. 48 files, 47 of them port headers. Every port now names one version and only one: a grep across ports/ and guix_studio/ports/ returns a single distinct value, 6.5.2.202603. The hardened pass from #200 reported no port left advertising another release, so GUIX does not have the problem FileX had, where two ports had never been reached by any pass. Build clean and 737/737 in default_build_coverage, and the AI disclosure check passes. This is the library half. Studio carries its own version in studiox.rc and guix_installer_release.iss, which scripts/update_studio_release_version.ps1 handles on Windows; that is not part of this change. Assisted-by: Claude Code (Opus 5) <noreply@anthropic.com>
) PowerShell 7.3 and later escape embedded quotes in native-command arguments as \". ISCC does not understand that escape, so each /D version value it received from generate_studio_installer.ps1 carried a stray backslash at both ends. The numeric VersionInfoVersion directive rejected the result and aborted the compile; the other three values were corrupted silently. The /D arguments are now passed without embedded quotes. None of the values can contain spaces, and ISCC still defines them as strings. generate_studio_installer.ps1 -Version 6.5.2.202603 failed with ISCC exit code 2 on PowerShell 7.6 before the change and produced guix_studio_setup_version_6.5.2.202603.exe with it. Assisted-by: Claude Code (Opus 5.5) <noreply@anthropic.com>
The library moved to 6.5.2.202603 in #212, but GUIX Studio still identified itself as 6.5.1.202602a in its resources, installer, MSIX manifest and project version number, and the gx_api.h banner still read 6.5.1. Prepared by scripts/update_studio_release_version.ps1 -Version 6.5.2.202603. Studio now reports 6.5.2.202603, with 6.5.2.3 as the Win32 version, 6.5.2.03 for MSIX and 202603 as STUDIOX_VERSION_NUMBER. No 6.5.1 remains in Studio's version-bearing files. The installer builds as guix_studio_setup_version_6.5.2.202603.exe. Studio itself was not rebuilt from this commit. Assisted-by: Claude Code (Opus 5.5) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
No description provided.