Skip to content

fix(system): reject partially-numeric .ipc.open ports - #440

Merged
singaraiona merged 1 commit into
RayforceDB:devfrom
belowzeroff:fix/ipc-open-strict-port
Aug 31, 2026
Merged

fix(system): reject partially-numeric .ipc.open ports#440
singaraiona merged 1 commit into
RayforceDB:devfrom
belowzeroff:fix/ipc-open-strict-port

Conversation

@belowzeroff

Copy link
Copy Markdown
Contributor

How it looks from the user's side

A user opens an IPC connection but the port field has a typo, or trailing garbage — 1abc instead of 1234:

(.ipc.open "127.0.0.1:1abc" 1)

Beforeatoi("1abc") stops at the first non-digit and returns 1, so the client silently targets the wrong port. The user gets a confusing error naming a port they never typed:

error: io: connection refused: 127.0.0.1:1

After — the malformed field is rejected up front, before any socket work, with a message that names the actual input:

error: domain: .ipc.open port must be numeric 1..65535, got "1abc"

("abc" and "70000" already errored — atoi("abc") == 0 fails the <= 0 check and 70000 fails > 65535 — so only the numeric-prefix case slipped through.)

Fix

.ipc.open parsed the port field with atoi(), which stops at the first non-digit. Parse it with strtol instead and require the whole field to be digits (reject any trailing characters), keeping the 1..65535 range check plus ERANGE. A malformed port is now a domain error at parse time, before any connection attempt.

Tests

Adds "1abc" / "19876x" cases to test/rfl/system/system_branch_cov2.rfl, alongside the existing abc / 70000 / -1 / 0 port-validation checks.

Full ASan+UBSan suite green: 3712 of 3713 passed (1 skipped, 0 failed).

.ipc.open parsed the port field with atoi(), which stops at the first
non-digit — so "127.0.0.1:1abc" was accepted as port 1 and a connection was
attempted instead of rejecting the malformed field. ("abc" and "70000"
already errored: atoi("abc")==0 fails the <=0 check and 70000 fails >65535, so
only the numeric-prefix case slipped through.)

Parse with strtol and require the whole field to be digits (no trailing
characters), keeping the 1..65535 range check plus ERANGE. Malformed ports now
return a domain error at parse time, before any socket work.

Adds "1abc" / "19876x" coverage to system/system_branch_cov2.rfl.

@singaraiona singaraiona left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed: strict strtol parse is correct — empty/partial fields hit the *endp != '\0' check, ERANGE and range are both covered, and the buffer is NUL-terminated before parsing. Only nit (pre-existing, not a regression): strtol still accepts leading whitespace and an explicit + sign ("localhost: 80", "+80"), so the field isn't literally digits-only as the comment says. Not blocking. CI green — merging.

@singaraiona
singaraiona merged commit 057f519 into RayforceDB:dev Aug 31, 2026
9 checks passed
@singaraiona

Copy link
Copy Markdown
Collaborator

Follow-up worth filing: the strict parse landed only in .ipc.open. The CLI still does port = (uint16_t)atoi(argv[++i]) in src/app/main.c and .z.x re-parses the same argv string with atoll in src/ops/system.c — so rayforce -p 65536x silently listens on a truncated port while .z.x reports 65536, yet a client's (.ipc.open "host:65536x") now correctly errors. A shared ray_parse_port(const char*, uint16_t*) used by all three sites would close the gap.

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