Skip to content

release: reliable 1.4.5 - #67

Merged
rowan-claude merged 1 commit into
mainfrom
rowan/release-1.4.5
Sep 14, 2026
Merged

rowan-claude merged 1 commit into
mainfrom
rowan/release-1.4.5

Conversation

@rowan-claude

Copy link
Copy Markdown
Contributor

Cuts 1.4.5 off main at 1ea7f61, which merged #66: security#26-2 (the fragment reassembly buffer is zeroed after allocation) and security#26-3 (endpoint->config.name is NUL terminated at create, and the twelve create-time rejection logs plus the allocation-failure log print the name bounded). This PR is the version bump and the draft notes only — no code changes. Do not tag or publish the release from here.

Version sites

Both sites #64 touched for 1.4.4, and no others: grep -rn '1\.4\.4\|RELIABLE_VERSION' --include='*.h' --include='*.c' --include='*.txt' --include='*.md' --include='*.json' . finds only these two version literals (the CLAUDE.md hit is prose about when the defines were added, at 1.3.0, and reliable.pc.in takes @PROJECT_VERSION@ from CMake, so both follow without editing).

File Line Change
CMakeLists.txt 17 project(reliable VERSION 1.4.4 LANGUAGES C)1.4.5
reliable.h 35 #define RELIABLE_VERSION_FULL "1.4.4""1.4.5"
reliable.h 38 #define RELIABLE_VERSION_PATCH 45

RELIABLE_VERSION_MAJOR (1) and RELIABLE_VERSION_MINOR (4) are unchanged. LICENCE and all license prose are untouched (GATE-9).

Gate lines

version consistency (.github/workflows/release-check.yml), run locally with the workflow's own sed extraction:

CMakeLists.txt project version:   1.4.5
reliable.h RELIABLE_VERSION_FULL: 1.4.5
reliable.h MAJOR.MINOR.PATCH:     1.4.5
version consistency: PASS

(The tag arm of that job is not exercised here — it fires on refs/tags/*, and it will compare v1.4.5 against these two sites when the tag is pushed.)

Build and test, CMake Debug with -DRELIABLE_SANITIZE=ON, cmake --build build --clean-first --parallel (macOS 15, AppleClang):

100% tests passed out of 7
$ ./build/bin/test
...
test_fragment_reassembly_buffer_zeroed
test_endpoint_name_terminated
test_config_name_bounded_in_rejection_log
...
*** ALL TESTS PASSED ***

All three tests from #66 are present and green in this tree.

Release notes (draft, Rowan edits)

reliable 1.4.5: a reassembled packet never carries stale heap bytes, and a caller's unterminated endpoint name cannot be over-read

A packet reassembled from fragments is now built in zeroed memory, and an endpoint name the caller never terminated can no longer be read past. Both are from security#26 — items 2 and 3 — and landed together in reliable#66.

The reassembly buffer is allocated through the endpoint's allocator: allocate_function, the caller's own if one was supplied and reliable_default_allocate_function, a bare malloc, if not. That memory arrives uninitialized, holding whatever the heap last put there. The fix does not bypass the caller's allocator and does not add an allocation — reliable_endpoint_receive_packet still asks the same allocator for the same buffer, and now runs one memset over it before any fragment is stored. The length is the one already computed for that allocation: RELIABLE_MAX_PACKET_HEADER_BYTES, plus the fragment count times the endpoint's own config.fragment_size, plus eight. It is bounded by the endpoint's own configuration rather than by anything a peer sends, and it is one memset per reassembly allocation, not one per fragment. As the code stands the delivered bytes could not have contained stale heap: completeness is enforced by the fragment bitmap and every fragment's offset is validated before the copy. This change is about what happens when that stops being true — a future logic error that leaves a hole in the buffer now leaks zeros into the delivered packet instead of the contents of freed memory. test_fragment_reassembly_buffer_zeroed covers it.

reliable_config_t.name is a fixed array the caller fills, and nothing in the API requires the caller to NUL terminate it. reliable_endpoint_create copies the config into the endpoint and now forces a terminator onto the last byte of its own copy, so every log that prints the endpoint's name stops inside the array — including the allocation-failure log at create, which now prints that terminated copy rather than the caller's struct. The config checks run before the copy exists and so cannot rely on the terminator: each of the twelve create-time rejection logs now prints the caller's name with a precision, %.*s bounded by the size of the field, so printf stops at the end of the array instead of reading past it. Before this, a caller who filled the whole name with no terminator had the library read off the end of their struct — adjacent memory disclosed into the log, or a fault. test_endpoint_name_terminated and test_config_name_bounded_in_rejection_log cover it.

Nothing on the wire changes, no symbol changes, and reassembled bytes are unchanged. Found by an outside security review.


🤖 Generated with Claude Code

Bump the two version sites for the 1.4.5 release: project(reliable VERSION)
in CMakeLists.txt and RELIABLE_VERSION_FULL / RELIABLE_VERSION_PATCH in
reliable.h. The release carries security#26-2 (the fragment reassembly buffer
is zeroed after allocation) and security#26-3 (endpoint->config.name is
terminated at create and the create-time logs print the caller's name
bounded), merged in #66.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@rowan-claude
rowan-claude merged commit e4e7092 into main Sep 14, 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.

1 participant