Skip to content

privsep: Remove PS_BUFLEN - #642

Merged
rsmarples merged 3 commits into
masterfrom
psbuf
Jun 18, 2026
Merged

privsep: Remove PS_BUFLEN#642
rsmarples merged 3 commits into
masterfrom
psbuf

Conversation

@rsmarples

Copy link
Copy Markdown
Member

We always send a header with the expected lengths. So use malloced buffers to send and receive using this knowledge.

We always send a header with the expected lengths.
So use malloced buffers to send and receive using this
knowledge.
@coderabbitai

coderabbitai Bot commented Jun 18, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: e9d36ed2-d2b5-41cb-8632-09a20639e753

📥 Commits

Reviewing files that changed from the base of the PR and between 7be2f51 and 74e5961.

📒 Files selected for processing (2)
  • src/common.c
  • src/privsep-root.c
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/common.c
  • src/privsep-root.c

Walkthrough

readfile() and get_line() in common.c/h are redesigned to use out-parameter dynamic allocation (void **data, size_t *len) instead of caller-provided fixed buffers. dhcp_readfile() and ps_root_readfile() adopt the same convention. struct dhcpcd_ctx gains io_buf/ps_buf reusable buffer fields. ps_bufalloc() replaces the removed ps_setbuf_fdpair/struct ps_msg infrastructure. All DHCP lease readers, config parsing, sysctl, and privsep IPC handlers are updated to use the new APIs.

Changes

Buffer API Redesign and Context-Managed Allocation

Layer / File(s) Summary
Core readfile and get_line API redesign
src/common.h, src/common.c, src/dhcp-common.h, src/privsep-root.h
readfile() uses fstat-driven realloc into void **data/size_t *len and null-terminates; get_line() parameter changes from ssize_t * to size_t * with updated memchr bookkeeping. Public headers for common, dhcp-common, and privsep-root updated to reflect pointer-based output parameters.
Context buffer storage and privsep infrastructure
src/dhcpcd.h, src/privsep.h, src/privsep.c
struct dhcpcd_ctx gains io_buf/io_buflen and (under PRIVSEP) ps_buf/ps_buflen as per-context reusable buffer slots. ps_bufalloc(ctx, len) is added to grow buffers via realloc; ps_start() pre-allocates BUFSIZ; ps_recvpsmsg() switches to ctx->ps_buf. struct ps_msg, ps_setbuf_fdpair, and socket buffer tuning helpers are removed.
Privsep root IPC handlers and sysctl
src/privsep-root.c, src/privsep-bsd.c
ps_root_readerrorcb resizes via realloc; ps_root_readfile signature changes to (void **data, size_t *len) delegating to ps_root_mreaderror; ps_root_writefile uses iovec/msghdr; PS_READFILE/PS_GETHOSTNAME handlers and ps_root_sysctl switch to ctx->ps_buf; ps_root_start removes socket buffer tuning and ps_root_stop frees ctx->ps_buf.
DHCP readers and config parsing
src/dhcp-common.c, src/dhcp.c, src/dhcp6.c, src/if-options.c
dhcp_readfile() accepts void **data/size_t *len and gains dynamic stdin reading when file is NULL. read_lease() and dhcp6_readlease() remove local union/stack buffers and read directly into dynamically allocated pointers; state->new is assigned directly. read_config loads embedded and main config into ctx->io_buf/ctx->io_buflen via dhcp_readfile.
Linux sysctl helpers and context cleanup
src/if-linux.c, src/dhcpcd.c
check_proc_int(), check_proc_uint(), and if_bridge() remove local fixed buffers and read into ctx->io_buf/ctx->io_buflen. main() cleanup adds free(ctx.io_buf) before context resource disposal.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 3.13% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main change: removing PS_BUFLEN and replacing fixed buffers with dynamic allocation in privsep code.
Description check ✅ Passed The description is related to the changeset, explaining the motivation for removing PS_BUFLEN and using malloced buffers instead.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch psbuf

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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 6

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
src/dhcp.c (1)

1266-1268: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Memory leak on early return paths.

When dhcp_readfile() succeeds but the lease is truncated (line 1266-1268), authentication fails (line 1286-1288), or authentication is now required (line 1296-1298), the function returns 0 without freeing the buffer allocated by dhcp_readfile() and stored in *bootp.

🐛 Proposed fix
 	/* Ensure the packet is at lease BOOTP sized
 	 * with a vendor area of 4 octets
 	 * (it should be more, and our read packet enforces this so this
 	 * code should not be needed, but of course people could
 	 * scribble whatever in the stored lease file. */
 	if (bytes < DHCP_MIN_LEN) {
 		logerrx("%s: %s: truncated lease", ifp->name, __func__);
+		free(*bootp);
+		*bootp = NULL;
 		return 0;
 	}

Similar fixes needed at lines 1286-1288 and 1296-1298.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/dhcp.c` around lines 1266 - 1268, The function returns 0 on three early
return paths (truncated lease, authentication failure, and when authentication
is now required) without freeing the buffer allocated by dhcp_readfile() that is
stored in *bootp, causing memory leaks. Before each return 0 statement in the
truncated lease check, the authentication failure check, and the authentication
requirement check, free the buffer stored in *bootp using the appropriate memory
deallocation function to prevent memory leaks on these early exit paths.
src/dhcp6.c (1)

2757-2764: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Memory leak: dhcp6 not freed in error path.

When dhcp_readfile succeeds (allocates dhcp6) but a subsequent check fails (e.g., dhcp6_validatelease at line 2714 or auth validation at line 2733), the code jumps to ex: where state->new is freed but the locally allocated dhcp6 is leaked.

Proposed fix
 ex:
 	dhcp6_freedrop_addrs(ifp, 0, IPV6_AF_DELEGATED, NULL);
 	dhcp_unlink(ifp->ctx, state->leasefile);
+	free(dhcp6);
 	free(state->new);
 	state->new = NULL;
 	state->new_len = 0;
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/dhcp6.c` around lines 2757 - 2764, The error path at the `ex:` label
frees `state->new` but fails to free the `dhcp6` variable that was allocated
earlier when `dhcp_readfile` succeeded. When subsequent validation checks like
`dhcp6_validatelease` or authentication validation fail, the code jumps to `ex:`
causing a memory leak of `dhcp6`. Add a `free(dhcp6)` call in the `ex:` error
handling block, ensuring the locally allocated `dhcp6` variable is properly
deallocated alongside the existing `state->new` cleanup.
🧹 Nitpick comments (1)
src/dhcp.c (1)

1247-1251: 💤 Low value

Duplicate *bootp = NULL assignment.

Line 1251 duplicates the assignment already done at line 1245.

♻️ Remove duplicate assignment
 	if (state->leasefile[0] == '\0')
 		logdebugx("reading standard input");
 	else
 		logdebugx("%s: reading lease: %s", ifp->name, state->leasefile);
-	*bootp = NULL;
 	sbytes = dhcp_readfile(ifp->ctx, state->leasefile, (void **)bootp,
 	    NULL);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/dhcp.c` around lines 1247 - 1251, Remove the duplicate assignment of
`*bootp = NULL;` at the end of the conditional block in the dhcp.c file. The
`*bootp = NULL` assignment already exists earlier in the function (at line 1245)
and should not be repeated after the logdebugx calls. Simply delete the
duplicate `*bootp = NULL;` line that appears after the else branch in the diff.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/common.c`:
- Around line 122-139: The readfile function has three resource management and
bounds-checking issues that need to be fixed. First, add a close(fd) call before
the return statement when fstat() fails to prevent fd leakage. Second, similarly
close fd before returning when realloc() fails to prevent fd leakage. Third, add
an error check after the read() call to ensure it did not return -1 (error
condition) before attempting to write the null terminator to buf[bytes], as
writing to a negative index would cause undefined behavior. These fixes should
be applied around the fstat, realloc, and read operations in sequence within the
readfile function.

In `@src/dhcp-common.c`:
- Around line 1020-1046: The issue is that after realloc expands the buffer, the
variable blen is not updated to reflect the newly available space. When len is
initially 0, blen becomes 0, and read() is called with 0 bytes to read, causing
immediate return. After reallocating the buffer and updating p to the new buffer
position (in the realloc block where nbuf is assigned to *data and p), calculate
blen as the remaining available space in the newly allocated buffer by setting
it to needed minus bytes, so that read() reads the correct amount of data on the
next iteration.

In `@src/privsep-bsd.c`:
- Around line 392-399: The pointer variable `p` is assigned from `ctx->ps_buf`
before the `ps_bufalloc()` call, but since this function may reallocate the
buffer, the stored pointer `p` can become invalid if the buffer is moved to a
new memory location. Move the assignment of `p` from `ctx->ps_buf` to occur
after the `ps_bufalloc(ctx, buflen)` check succeeds and returns, ensuring that
`p` points to the current valid buffer address before it is used in subsequent
memcpy operations.
- Around line 422-423: In the ps_root_sysctl function, the variable buf
referenced in the ps_root_readerror call on line 422 is undefined and should be
replaced with ctx->ps_buf. Change the ps_root_readerror invocation to use
ctx->ps_buf as the buffer argument instead of the undefined buf variable.
Additionally, verify that sizeof(ctx->ps_buf) is the correct size to pass, as
other similar calls in the function use more explicit sizes like sizeof(*time)
or sizeof(*rdm) rather than sizeof of the entire buffer structure.

In `@src/privsep-root.c`:
- Around line 569-575: In the PS_GETHOSTNAME case block, there is a typo on the
line calculating rlen where cts->ps_buf is referenced instead of ctx->ps_buf.
The variable name is ctx, not cts, so the strlen call needs to be corrected to
use ctx->ps_buf to match the variable used elsewhere in the same block (where
rdata is assigned and gethostname is called). Fix this typo by replacing cts
with ctx in the strlen function call.

In `@src/privsep.c`:
- Around line 145-148: The unconditional return statement immediately after the
chdir("/") and logerr call is causing the function to exit prematurely, skipping
all subsequent privilege-dropping operations including setgroups, setgid,
setuid, and setrlimit calls. Remove this errant return statement so that
execution continues through the privilege-dropping code. The return statement
should only be present conditionally for the __sun platform where privilege
dropping is intentionally skipped, not unconditionally after the chdir
operation.

---

Outside diff comments:
In `@src/dhcp.c`:
- Around line 1266-1268: The function returns 0 on three early return paths
(truncated lease, authentication failure, and when authentication is now
required) without freeing the buffer allocated by dhcp_readfile() that is stored
in *bootp, causing memory leaks. Before each return 0 statement in the truncated
lease check, the authentication failure check, and the authentication
requirement check, free the buffer stored in *bootp using the appropriate memory
deallocation function to prevent memory leaks on these early exit paths.

In `@src/dhcp6.c`:
- Around line 2757-2764: The error path at the `ex:` label frees `state->new`
but fails to free the `dhcp6` variable that was allocated earlier when
`dhcp_readfile` succeeded. When subsequent validation checks like
`dhcp6_validatelease` or authentication validation fail, the code jumps to `ex:`
causing a memory leak of `dhcp6`. Add a `free(dhcp6)` call in the `ex:` error
handling block, ensuring the locally allocated `dhcp6` variable is properly
deallocated alongside the existing `state->new` cleanup.

---

Nitpick comments:
In `@src/dhcp.c`:
- Around line 1247-1251: Remove the duplicate assignment of `*bootp = NULL;` at
the end of the conditional block in the dhcp.c file. The `*bootp = NULL`
assignment already exists earlier in the function (at line 1245) and should not
be repeated after the logdebugx calls. Simply delete the duplicate `*bootp =
NULL;` line that appears after the else branch in the diff.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: bdda112a-985d-4b50-9d0b-c72695992444

📥 Commits

Reviewing files that changed from the base of the PR and between ddc57fe and f63387f.

📒 Files selected for processing (13)
  • src/common.c
  • src/common.h
  • src/dhcp-common.c
  • src/dhcp-common.h
  • src/dhcp.c
  • src/dhcp6.c
  • src/dhcpcd.h
  • src/if-options.c
  • src/privsep-bsd.c
  • src/privsep-root.c
  • src/privsep-root.h
  • src/privsep.c
  • src/privsep.h

Comment thread src/common.c
Comment thread src/dhcp-common.c
Comment thread src/privsep-bsd.c Outdated
Comment thread src/privsep-bsd.c Outdated
Comment thread src/privsep-root.c
Comment thread src/privsep.c Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/privsep-root.c (1)

569-574: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Bound hostname length before computing reply size.

Line 573 uses strlen on ctx->ps_buf right after gethostname. If the hostname is truncated and not NUL-terminated, this can read past the allocated buffer and leak memory through rlen.

🐛 Proposed fix
 case PS_GETHOSTNAME:
 	err = gethostname((char *)ctx->ps_buf, ctx->ps_buflen);
 	if (err != -1) {
+		((char *)ctx->ps_buf)[ctx->ps_buflen - 1] = '\0';
 		rdata = ctx->ps_buf;
-		rlen = strlen((char *)ctx->ps_buf) + 1;
+		rlen = strnlen((char *)ctx->ps_buf, ctx->ps_buflen) + 1;
 	}
 	break;
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/privsep-root.c` around lines 569 - 574, In the PS_GETHOSTNAME case block,
the strlen call on ctx->ps_buf after gethostname can read past the allocated
buffer if the hostname fills the entire buffer without null-termination. Bound
the hostname length calculation to not exceed ctx->ps_buflen by either manually
null-terminating the buffer after the gethostname call and then limiting the
strlen result, or by using a bounded string length function like strnlen that
respects the ctx->ps_buflen limit to safely compute rlen.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@src/privsep-root.c`:
- Around line 569-574: In the PS_GETHOSTNAME case block, the strlen call on
ctx->ps_buf after gethostname can read past the allocated buffer if the hostname
fills the entire buffer without null-termination. Bound the hostname length
calculation to not exceed ctx->ps_buflen by either manually null-terminating the
buffer after the gethostname call and then limiting the strlen result, or by
using a bounded string length function like strnlen that respects the
ctx->ps_buflen limit to safely compute rlen.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 1510deaf-1ad3-4058-9818-a13349bfe333

📥 Commits

Reviewing files that changed from the base of the PR and between f63387f and 7be2f51.

📒 Files selected for processing (7)
  • src/common.c
  • src/dhcp-common.c
  • src/dhcpcd.c
  • src/if-linux.c
  • src/privsep-bsd.c
  • src/privsep-root.c
  • src/privsep.c
💤 Files with no reviewable changes (1)
  • src/privsep.c
🚧 Files skipped from review as they are similar to previous changes (3)
  • src/privsep-bsd.c
  • src/dhcp-common.c
  • src/common.c

@rsmarples
rsmarples merged commit cfac52d into master Jun 18, 2026
6 checks passed
@rsmarples
rsmarples deleted the psbuf branch June 18, 2026 14:39
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