fix: safe CARP_REALLOC and size_t capacity in read-append functions - #10
Conversation
On REALLOC failure the original pointer was lost (memory leak) and the next read would crash via NULL deref. The int capacity arithmetic overflowed on buffers approaching 2GB. Matches the fixes already applied in the TLS library.
There was a problem hiding this comment.
Build & Tests
Build: clean.
Tests: 16/16 pass (tcp_test 6/6, poll_test 5/5, poll_test_poll2 5/5). unix_test fails to build due to a pre-existing clang-14 segfault on armhf (Array_copy__Uint32), unrelated to this PR.
CI: passing on both ubuntu-latest and macos-latest.
Findings
None. The fix is correct and complete across all 5 CARP_REALLOC sites:
- NULL-check pattern: old code overwrote
buf->datadirectly with the realloc result, losing the original pointer on failure (memory leak + NULL deref on next read). New code stores ingrown, checks for NULL, returns -1 on failure. Correct. - size_t arithmetic: old
int new_capcould overflow at ~1GB. Newsize_t new_capis strictly better. The expression(buf->len + (size_t)SOCK_BUF_SIZE) * 2cannot practically overflow on 64-bit; on 32-bit, realloc would fail beforesize_twraps (~2GB). Not a concern. - UdpSocket_recv_ capacity check: old
(int)buf->capacity < SOCK_BUF_SIZEvs newbuf->capacity < (size_t)SOCK_BUF_SIZE— semantically equivalent for positiveSOCK_BUF_SIZE(4096). The new version is actually safer for capacities exceedingINT_MAX. - Caller handling: all 5 Carp-side wrappers check for -1 and convert to
Result.ErrorwithSystem.error-text. On realloc failure,errnoisENOMEM, so the error message is meaningful. - Scope: the PR correctly limits itself to the realloc sites. Unchecked
CARP_MALLOCcalls elsewhere are a separate concern and consistent with Carp's general approach.
Verdict: merge
Correct, well-scoped fix. All realloc sites covered, callers handle errors properly.
hellerve
left a comment
There was a problem hiding this comment.
let’s reduce duplication here.
|
Addressed @hellerve's feedback to reduce duplication:
All 22 tests pass (tcp: 6, unix: 6, poll: 5, poll2: 5). |
There was a problem hiding this comment.
Build & Tests
CI: pass (ubuntu-latest, macos-latest)
Local build: no Carp compiler available on this machine — relied on CI.
Prior feedback
- First review found the realloc safety fix correct across all 5 sites, recommended merge.
- hellerve requested "let's reduce duplication here" — addressed by extracting
buf_grow_for_readandbuf_ensureintosrc/common.h.
Findings
Reviewed src/common.h (full file), src/tcp_stream.h, src/unix_stream.h, src/udp_socket.h in full. Checked for remaining realloc duplication across all source files.
No issues found.
-
buf_grow_for_readis correct. Capacity checkbuf->capacity - buf->len >= SOCK_BUF_SIZEis safe (capacity >= len is an Array invariant). Growth formula(len + SOCK_BUF_SIZE) * 2guarantees at leastSOCK_BUF_SIZEfree bytes after realloc. NULL check preservesbuf->dataon failure. -
buf_ensureis correct. Simple minimum-capacity guarantee with NULL check. Exact allocation (no growth factor) is appropriate for the UDP use case. -
All 5 callers are consistent. Each checks
!= 0and returns-1on failure before proceeding to the I/O call. -
__attribute__((unused))on static functions in a header follows the existing pattern in this file (sock_error_string,sockaddr_to_string,resolve_address,send_all). Standard approach for Carp FFI headers. -
No remaining realloc duplication. Checked
tcp_bufio.h,unix_bufio.h,tcp_listener.h,unix_listener.h,poll.h— none contain similar realloc patterns. The TLS fix mentioned in the PR description lives in a separate repo.
Verdict: merge
Clean mechanical extraction. Both helpers are correct, all callers consistent, no remaining duplication.
|
|
||
| /* Ensure buf has at least SOCK_BUF_SIZE bytes free for appending. | ||
| Returns 0 on success, -1 on allocation failure. */ | ||
| __attribute__((unused)) |
|
@hellerve — the attribute suppresses |
There was a problem hiding this comment.
Build & Tests
CI: passing on both ubuntu-latest and macos-latest.
Local build: no Carp compiler available — relied on CI. C headers reviewed by inspection.
Prior feedback
- First review: found the realloc safety fix correct, recommended merge.
- hellerve — "let's reduce duplication here": addressed.
buf_grow_for_readandbuf_ensureextracted intosrc/common.h. - Second review: confirmed extraction correct, recommended merge.
- hellerve — "why is this attribute here?": agent explained
__attribute__((unused))is needed becausepoll.hincludescommon.hwithout calling the helpers, which would trigger-Wunused-function. consistent with the 6 other static functions in the same file (sock_error_string,sockaddr_to_string,sockaddr_port,resolve_address,send_all,carp_sock_ignore_sigpipehasconstructorinstead). hellerve left a subsequent empty comment, likely acknowledging.
Findings
re-reviewed all changed files in full after the helper extraction. no issues found.
-
buf_grow_for_read(common.h:87-95): capacity checkbuf->capacity - buf->len >= SOCK_BUF_SIZEis safe (capacity >= len is an Array invariant). growth formula(len + SOCK_BUF_SIZE) * 2guarantees at leastSOCK_BUF_SIZEfree bytes. NULL check preservesbuf->data. correct. -
buf_ensure(common.h:100-107): minimum-capacity guarantee with NULL check. exact allocation (no growth factor) is appropriate for the UDP use case where a single recv needs a known buffer size. correct. -
all 5 call sites are consistent:
TcpStream_read_MINUS_append_(tcp_stream.h:87)TcpStream_read_MINUS_append_MINUS_nb_(tcp_stream.h:126)UnixStream_read_MINUS_append_(unix_stream.h:71)UnixStream_read_MINUS_append_MINUS_nb_(unix_stream.h:131)UdpSocket_recv_(udp_socket.h:46)
each checks
!= 0and returns-1on failure before the I/O call. the Carp-side wrappers convert-1toResult.ErrorwithSystem.error-text, givingENOMEMon realloc failure. -
no remaining duplication. checked tcp_bufio.h, unix_bufio.h, tcp_listener.h, unix_listener.h, poll.h — none contain similar realloc patterns.
Verdict: merge
clean, correct change. CI green, duplication eliminated, all reviewer feedback addressed.
Summary
CARP_REALLOCreturn value before overwritingbuf->datain all read-append functions (TcpStream,UnixStream,UdpSocket). Previously a failed realloc lost the original pointer (memory leak) and the next read crashed via NULL deref.inttosize_tto prevent integer overflow on buffers approaching 2GB.TlsStream_read_MINUS_append_andTlsStream_read_MINUS_append_MINUS_nb_).Affected functions
TcpStream_read_MINUS_append_TcpStream_read_MINUS_append_MINUS_nb_UnixStream_read_MINUS_append_UnixStream_read_MINUS_append_MINUS_nb_UdpSocket_recv_Test plan
Opened by the carpentry-org heartbeat agent (Claude). Veit has not reviewed this yet.