Skip to content

Declare the sysctl(3) prototype, check errors and retry grown values - #22

Merged
gronke merged 6 commits into
mainfrom
fix/libc-sysctl-calls
Jul 6, 2026
Merged

gronke merged 6 commits into
mainfrom
fix/libc-sysctl-calls

Conversation

@gronke

@gronke gronke commented Jul 6, 2026

Copy link
Copy Markdown
Owner

All libc sysctl(3) calls went through ctypes without a declared prototype.
The confirmed signature is int sysctl(const int *, u_int, void *, size_t *, const void *, size_t), but every call site passed oldlenp as POINTER(c_int), so the kernel wrote 8 bytes through a pointer to a 4-byte object on amd64.
query_size() passed only five of the six arguments, leaving newlen to whatever was in the sixth argument register.
Return codes were ignored, so a nonexistent sysctl name silently produced a 24-zero OID.

Changes:

  • freebsd_sysctl.libc declares argtypes/restype (resolved lazily, so the module stays importable on Linux where the build tooling runs) and wraps sysctl(3) in a helper that raises OSError from errno on failure.
  • All seven call sites use the wrapper with c_size_t lengths; query_size() passes the previously missing newlen.
  • query_value() grows the buffer and retries on ENOMEM, like sysctl(8), for values that grow between the size query and the read.
  • Iterating children stops cleanly at the end of the sysctl tree, where the kernel reports ENOENT.

Behavior change: unknown sysctl names and OIDs raise OSError with ENOENT instead of returning garbage; tests cover both.

default added 6 commits July 6, 2026 23:01
The script boots an official BASIC-CLOUDINIT VM image headless under OVMF,
authorizes an ephemeral ssh key through a NoCloud seed ISO, installs pytest
from packages and runs the suite over ssh.
It uses KVM when available and falls back to TCG emulation.
Readiness is detected by the absence of the firstboot sentinel, which avoids
racing the reboot at the end of the first boot.
The 14.x images apply base-system patches on first boot before sshd becomes
reachable, which delays the first connection by a few minutes.
Images are cached outside the repository and are never committed.
The workflow only uses github-owned actions, so it works under restrictive
organization action allowlists.
The FreeBSD job runs the QEMU harness for each supported release.
Lengths are size_t, so passing pointers to c_int let the kernel write
8 bytes into 4-byte objects on 64-bit architectures.
The symbol is resolved lazily to keep the module importable on Linux,
where the build tooling runs.
All call sites go through one wrapper that checks the return code and
raises OSError from errno.
The wrapper passes all six arguments, so query_size() no longer leaks
garbage into newlen.
Unknown sysctl names now raise OSError with ENOENT instead of returning
a zero-filled OID, and iterating children stops cleanly at the end of
the sysctl tree.
sysctl(3) reports ENOMEM when the value grew between the size query
and the read, which sysctl(8) handles the same way.
Unknown names raise ENOENT; unknown OIDs echo numerically from the
kernel's name lookup, so the value query carries the check.
Bulk value reads skip NODE sysctls without handlers, which report
EISDIR, and description lookups skip sysctls that vanish between
listing and query.
@gronke
gronke merged commit 5624a3f into main Jul 6, 2026
3 checks passed
@gronke
gronke deleted the fix/libc-sysctl-calls branch July 6, 2026 21:27
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