Skip to content

Conversation

@rsmarples
Copy link
Member

Adjust the code flow so that the same errors would be caught after the final recv.
This ensures we read what is really meant for us and not something silly.

Should fix #555.

Adjust the code flow so that the same errors would be caught
after the final recv.
This ensures we read what is really meant for us and not
something silly.

Should fix #555.
@coderabbitai
Copy link

coderabbitai bot commented Nov 17, 2025

Walkthrough

Replaced direct recv with recvmsg using msghdr/iovec and a two-phase peek + full-read (MSG_PEEK then MSG_WAITALL) flow for PSR messages; added centralized final recv path, MSG_TRUNC/ENOBUFS validation, and adjusted dynamic data allocation and iovec length semantics.

Changes

Cohort / File(s) Summary
Privilege Separation Message Receiving
src/privsep-root.c
Replaced recv with recvmsg using msghdr and iovec; added an initial MSG_PEEK read to size messages and a centralized MSG_WAITALL final read path; added checks for MSG_TRUNC and exact total length; changed dynamic data allocation (realloc only applied on success) and adjusted iov[1].iov_len to respect psr_mdatalen vs psr_datalen; preserved explicit consume/discard path on allocation failure or truncation and updated error signaling (including ENOBUFS).

Sequence Diagram(s)

sequenceDiagram
    participant Peer as Peer process
    participant Root as privsep-root
    participant Validator as Validator

    Peer->>Root: UDP/TCP PSR message arrives
    note right of Root `#e6f2ff`: 1) recvmsg(MSG_PEEK) reads header only
    Root->>Root: determine psr_datalen, attempt realloc if mdata used
    alt enough buffer / proceed
        Peer->>Root: recvmsg(MSG_WAITALL, iov[header,data])
        Root->>Validator: validate total size == header + psr_datalen
        alt MSG_TRUNC or size mismatch
            Root->>Validator: signal truncation / ENOBUFS
        else exact match
            Root->>Validator: deliver message
        end
    else insufficient buffer / realloc failed
        Peer->>Root: recvmsg to consume/truncate remaining data
        Root->>Validator: report ENOBUFS and discard
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

  • Focus review on:
    • The peek + final recvmsg(MSG_WAITALL) control flow and the goto recv consolidation.
    • Correct handling of MSG_TRUNC and when to report ENOBUFS.
    • realloc semantics and updating psr_mdatalen only on success; iov[1].iov_len calculations when mdata is present.

Possibly related PRs

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title directly and specifically describes the main change: ensuring a real recv occurs after a MSG_PEEK, which is the core of the modifications made to src/privsep-root.c.
Description check ✅ Passed The description is related to the changeset, explaining the rationale for ensuring errors are caught after the final recv, which aligns with the code changes and the linked issue #555.
Linked Issues check ✅ Passed The changes implement a fix for issue #555 by restructuring the recv flow to validate data after a real recv following MSG_PEEK, directly addressing the buffer space error reported in the linked issue.
Out of Scope Changes check ✅ Passed All changes in src/privsep-root.c are scoped to fixing the recv/MSG_PEEK flow and error handling, directly aligned with resolving the ENOBUFS issue from #555.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch privsep-recvmsg

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 834a322 and 595c305.

📒 Files selected for processing (1)
  • src/privsep-root.c (2 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (14)
  • GitHub Check: netbsd (--disable-dhcp6)
  • GitHub Check: netbsd (--disable-dhcp6, -DSMALL)
  • GitHub Check: netbsd (--disable-ipv4ll, -DSMALL)
  • GitHub Check: netbsd (--disable-arp)
  • GitHub Check: netbsd (--disable-ipv6)
  • GitHub Check: netbsd (-DSMALL)
  • GitHub Check: netbsd (--disable-arp, -DSMALL)
  • GitHub Check: netbsd (--disable-ipv6, -DSMALL)
  • GitHub Check: netbsd (--disable-ipv4ll)
  • GitHub Check: netbsd (--disable-ipv4, -DSMALL)
  • GitHub Check: netbsd
  • GitHub Check: netbsd (--disable-ipv4)
  • GitHub Check: freebsd
  • GitHub Check: openbsd
🔇 Additional comments (3)
src/privsep-root.c (3)

129-140: LGTM! MIN() clamp correctly prevents MSG_WAITALL from blocking.

The use of MIN(psr_ctx->psr_mdatalen, psr_error->psr_datalen) at lines 133-134 and lines 138-139 properly addresses the past review concern. This ensures that when a previous message caused the buffer to grow, and a subsequent smaller message arrives, we only request the actual message size rather than the larger buffer size. This prevents MSG_WAITALL from blocking indefinitely waiting for bytes that don't exist.

The logic correctly handles all cases:

  • Buffer sufficient: reads exact message size
  • Buffer insufficient: reads what fits, MSG_TRUNC signals truncation at line 152
  • Buffer oversized from previous message: clamps to current message size (prevents blocking)

Based on past review comments


143-156: LGTM! Centralized recv path with comprehensive validation.

The final recv label and validation logic correctly implement the peek-then-recv pattern:

  1. Line 147: The real recvmsg with MSG_WAITALL properly consumes the message from the queue
  2. Lines 150-151: Catches truncated headers (protocol violation)
  3. Lines 152-153: Catches buffer overflow via MSG_TRUNC and returns ENOBUFS (consistent with malloc failure handling at lines 118-122)
  4. Lines 154-155: Validates exact length match (catches protocol errors)

The comment at lines 144-146 correctly explains why MSG_WAITALL won't stall: SOCK_SEQPACKET with MSG_EOR boundaries ensures recvmsg respects record limits and won't wait for more data than exists in the current message.


79-157: Excellent implementation of the peek-then-recv pattern!

The refactored ps_root_readerrorcb correctly implements the two-phase message receipt as described in the PR objectives:

  1. Phase 1 (lines 102-111): MSG_PEEK to inspect the header and determine buffer requirements without consuming the message
  2. Phase 2 (lines 143-156): Real recvmsg to consume the message with proper validation

Key improvements:

  • The MIN() clamp at lines 133-134 and 138-139 fixes the potential indefinite blocking when buffer size exceeds message size
  • Ensures message is consumed from the queue even on error (lines 108-109)
  • Comprehensive error detection: short reads, truncation, and length mismatches
  • Proper handling of allocation failures via MSG_TRUNC signaling

This should resolve issue #555 by ensuring robust message handling in the privilege separation IPC.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 385ff24 and f4446d5.

📒 Files selected for processing (1)
  • src/privsep-root.c (2 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (14)
  • GitHub Check: netbsd (--disable-ipv6)
  • GitHub Check: netbsd (--disable-ipv6, -DSMALL)
  • GitHub Check: netbsd (-DSMALL)
  • GitHub Check: netbsd (--disable-dhcp6, -DSMALL)
  • GitHub Check: netbsd (--disable-dhcp6)
  • GitHub Check: netbsd (--disable-ipv4ll)
  • GitHub Check: netbsd (--disable-ipv4ll, -DSMALL)
  • GitHub Check: netbsd (--disable-ipv4)
  • GitHub Check: netbsd (--disable-arp, -DSMALL)
  • GitHub Check: netbsd (--disable-ipv4, -DSMALL)
  • GitHub Check: netbsd (--disable-arp)
  • GitHub Check: netbsd
  • GitHub Check: freebsd
  • GitHub Check: openbsd

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

♻️ Duplicate comments (1)
src/privsep-root.c (1)

129-138: Critical: Fix MSG_WAITALL hang by using incoming message size.

Lines 134 and 137 set iov_len from the context buffer sizes (psr_ctx->psr_mdatalen, psr_ctx->psr_datalen), which can be larger than the actual incoming message size psr_error->psr_datalen. When MSG_WAITALL is used with an iov_len larger than available data, recvmsg blocks indefinitely waiting for bytes that will never arrive, wedging the privileged proxy and causing cascading failures like "No buffer space available."

This is the same critical issue identified in the past review and must be fixed.

Apply this diff to clamp iov_len to the actual incoming message size:

 	if (psr_error->psr_datalen != 0) {
 		if (psr_ctx->psr_usemdata) {
 			iov[1].iov_base = psr_ctx->psr_mdata;
-			/* psr_mdatalen could be smaller then psr_datalen
-			 * if the above malloc failed. */
-			iov[1].iov_len =
-			    MIN(psr_ctx->psr_mdatalen, psr_ctx->psr_datalen);
+			/* Clamp to actual incoming size to avoid MSG_WAITALL hang.
+			 * If buffer is too small, MSG_TRUNC will catch it. */
+			iov[1].iov_len = psr_error->psr_datalen;
+			if (iov[1].iov_len > psr_ctx->psr_mdatalen)
+				iov[1].iov_len = psr_ctx->psr_mdatalen;
 		} else {
 			iov[1].iov_base = psr_ctx->psr_data;
-			iov[1].iov_len = psr_ctx->psr_datalen;
+			iov[1].iov_len = psr_error->psr_datalen;
+			if (iov[1].iov_len > psr_ctx->psr_datalen)
+				iov[1].iov_len = psr_ctx->psr_datalen;
 		}
 	}
🧹 Nitpick comments (1)
src/privsep-root.c (1)

102-111: Consider removing MSG_WAITALL from the MSG_PEEK operation.

Using MSG_WAITALL with MSG_PEEK may cause unnecessary blocking when the message header hasn't fully arrived yet. Since this is just a peek operation to determine buffer size, and you're checking for short reads immediately after (line 110), MSG_PEEK alone should suffice.

Apply this diff:

-	len = recvmsg(fd, &msg, MSG_PEEK | MSG_WAITALL);
+	len = recvmsg(fd, &msg, MSG_PEEK);
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f4446d5 and 834a322.

📒 Files selected for processing (1)
  • src/privsep-root.c (2 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (14)
  • GitHub Check: netbsd (--disable-dhcp6, -DSMALL)
  • GitHub Check: netbsd (--disable-ipv6, -DSMALL)
  • GitHub Check: netbsd (--disable-dhcp6)
  • GitHub Check: netbsd (--disable-ipv6)
  • GitHub Check: netbsd (--disable-ipv4ll)
  • GitHub Check: netbsd (--disable-ipv4ll, -DSMALL)
  • GitHub Check: netbsd (--disable-arp)
  • GitHub Check: netbsd (-DSMALL)
  • GitHub Check: netbsd (--disable-ipv4)
  • GitHub Check: netbsd (--disable-arp, -DSMALL)
  • GitHub Check: netbsd
  • GitHub Check: netbsd (--disable-ipv4, -DSMALL)
  • GitHub Check: freebsd
  • GitHub Check: openbsd
🔇 Additional comments (2)
src/privsep-root.c (2)

113-127: LGTM! Proper handling of allocation failure.

The allocation failure handling is correct: when realloc fails, the code leaves the smaller buffer in place and relies on MSG_TRUNC detection in the subsequent recvmsg to signal ENOBUFS. This ensures the message is properly discarded from the queue.


141-151: LGTM! Comprehensive validation of received message.

The final receive path correctly:

  • Centralizes the actual message consumption for both early failures and normal flow
  • Validates minimum header size
  • Detects truncation via MSG_TRUNC and returns ENOBUFS
  • Ensures exact size match between received data and expected header + payload

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.

SIOCSPFXFLUSH_IN6: No buffer space available after updating to 10.3.0 (FreeBSD 14.3)

2 participants