Skip to content

fix(q): validate handles and wire protocol frames - #10

Merged
protocolstardust merged 3 commits into
RayforceDB:masterfrom
belowzeroff:fix/q-connect-argument-validation
Sep 25, 2026
Merged

protocolstardust merged 3 commits into
RayforceDB:masterfrom
belowzeroff:fix/q-connect-argument-validation

Conversation

@belowzeroff

Copy link
Copy Markdown
Contributor

What changes for users

Invalid Q client arguments now fail before touching the socket: .q.connect rejects malformed credential/timeout/port values, .q.send and .q.close reject values that are not live non-negative i64 handles, and invalid close handles return an error instead of silently succeeding.

Malformed or hostile wire input is rejected safely: invalid table markers, unsupported handshake capability bytes, oversized compressed frames, and unknown message types no longer get accepted or evaluated.

Validation

  • make recheck RAYFORCE_LOCAL_PATH=/home/athuser/rayforce-build/rayforce-core
  • codec and exchange self-tests
  • malformed-frame and unknown-message-type tests
  • real-q interoperability (30 assertions)
  • client suites (8 files)
  • poll/push client suite
  • strict C compilation with -Wall -Wextra -Werror

All checks pass.

@protocolstardust
protocolstardust merged commit e08ab96 into RayforceDB:master Sep 25, 2026
1 check passed
protocolstardust added a commit that referenced this pull request Sep 25, 2026
…warning

After a timed-out sync send the event-loop path deregisters the handle
(#8), and .q.close on a handle that is not open is now an error (#10),
so 06_errors.rfl closes the slow connection under try: still open on the
blocking path, already gone on the --poll path.

The codec selftest's handshake_done label preceded a declaration, a C23
extension that clang warns about; end the label with an empty statement.
@protocolstardust

Copy link
Copy Markdown
Collaborator

Reviewed and merged (e08ab96). This also carries #9.

Wire hardening, all sound

  • XT must be followed by attrs 0 + XD; a decode error now reaches the caller instead of being masked as "trailing bytes".
  • q_decompress caps the declared uncompressed size at Q_MAX_BODY, so a hostile header can no longer ask for a 4 GiB malloc.
  • A capability byte above 3 fails the handshake; a real q always answers with min(client, server), so this only rejects non-q peers.
  • Unknown message types drop the connection instead of being evaluated as sync; run.sh covers it against the live server.

Builtin validation
Bool and temporal atoms are no longer integers, credentials must be strings, the timeout must fit an int, closing an unknown handle is a handle error. One thing to keep in mind: README documents timeout_ms <= 0 blocks for the C q_connect; the builtin now rejects negatives with range. Fine as a stricter surface, but worth one line in README/INTEGRATING so the two are not read as the same contract.

Fixed in a follow-up (2593c94)
handshake_done: directly before a declaration is a C23 extension: clang warns, older gcc rejects it. Ended the label with ;.

Pre-existing, not from this PR, but the new ray_poll_get check made it visible
On the poll path .q.close <id> and .q.send <id> accept any selector id, not only q connections: the q listener, a timer, or a native IPC connection all pass ray_poll_get != NULL. A tag on q_conn_t (or checking close_fn == q_on_close) would close that hole. Separate PR.

Conflicted with #7 in test/driver.c; resolved by keeping both selftest blocks. Locally: full suite green, codec selftest including the forked bad-capability peer.

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