cygwin: fix off-by-one in utsname.sysname length#5099
Conversation
Cygwin's <sys/utsname.h> defines _UTSNAME_LENGTH as 65, and all
six fields of struct utsname are 65 bytes:
struct utsname {
char sysname [65];
char nodename [65];
char release [65];
char version [65];
char machine [65];
char domainname[65];
};
Verified by C-side struct probe on a Cygwin x86_64 host:
sizeof(struct utsname) = 390 (= 6 * 65)
offsetof(sysname) = 0
offsetof(nodename) = 65
offsetof(release) = 130
offsetof(version) = 195
offsetof(machine) = 260
The libc crate declares sysname as 66 bytes, which shifts every
other field one byte forward in Rust's view of the struct.
Reading the fields with CStr::from_ptr therefore drops the first
character of nodename, release, version, and machine. sysname
itself is unaffected because field 0 starts at offset 0 either
way.
Empirically, on a host where `uname -a` reports
CYGWIN_NT-10.0-26200 xps-ne 3.6.7-1.x86_64 ...
a Rust caller of libc::uname() sees:
sysname = "CYGWIN_NT-10.0-26200" (correct)
nodename = "ps-ne" (lost 'x')
release = ".6.7-1.x86_64" (lost '3')
machine = "86_64" (lost 'x')
Downstream consequences include silently mangled hostnames in
Rust-based logging/telemetry on Cygwin, broken `uname -m` output
in `uutils/coreutils`, and malformed wheel platform tags
produced by `maturin` (cygwin_86_64 instead of cygwin_x86_64)
which Cygwin Python's `pip` then refuses with
"is not a supported wheel on this platform."
Fix: declare sysname at the correct length of 65 bytes, matching
the other five fields. After the fix, all five public fields sit
at the correct C offsets and CStr reads return the full strings.
Adds a runtime test that calls libc::uname() and asserts:
1. nodename matches gethostname() — guards against the field
shifting back to a misaligned offset.
2. every variable-length field reads as a non-empty string.
The C-side struct layout cross-validation in libc-test/ctest
already catches a regression of the [c_char; 65] length at
compile time. This test is a runtime safety net in case
utsname is ever moved to ctest's skip list for Cygwin.
There was a problem hiding this comment.
Could you drop this test? We don't have concrete testing structure for this type and I'd figure out it before actually adding anything.
There was a problem hiding this comment.
On that note @phdye were you able to run libc-test on this target? It should have caught things like this
| pub struct utsname { | ||
| pub sysname: [c_char; 66], | ||
| pub sysname: [c_char; 65], | ||
| pub nodename: [c_char; 65], | ||
| pub release: [c_char; 65], | ||
| pub version: [c_char; 65], | ||
| pub machine: [c_char; 65], | ||
| pub domainname: [c_char; 65], | ||
| } |
There was a problem hiding this comment.
Honestly you can just define a nonpublic _SYS_UTSNAME_H and use that for all the fields, to match the source.
This part of the change seems fine but cc maintainer @Berrysoft in case
|
rust-lang does not have a policy against AI use but please handwrite all PR descriptions and commit messages. The amount of text is overwhelming and not necessary for a simple length fix, all you need to do is say what changed and link the source. |
|
😢That's... I have already found that the length of |
Description
libcdeclares the Cygwinutsnamestruct'ssysnamefield as[c_char; 66], but Cygwin's<sys/utsname.h>declares all six fields as_UTSNAME_LENGTH= 65 bytes. The over-declaration shifts every subsequent field one byte forward in Rust's view of the struct, soCStr::from_ptrreadsnodename,release,version,machine, anddomainnamestarting at byte 1 instead of byte 0 — silently dropping the first character of each.This PR changes a single character (
66→65) and adds a runtime regression test.Empirical effect on a Cygwin x86_64 host where
uname -areportsCYGWIN_NT-10.0-26200 xps-ne 3.6.7-1.x86_64 ...:CYGWIN_NT-10.0-26200CYGWIN_NT-10.0-26200ps-nexps-ne.6.7-1.x86_643.6.7-1.x86_6486_64x86_64sysnamereads correctly because field 0 starts at offset 0 either way; subsequent fields each begin one byte too late.Downstream this propagates to
platform-info,uutils/coreutils'suname -m, andmaturin's wheel-tag formatter — which produces*-cygwin_86_64.whl(instead ofcygwin_x86_64) that Cygwin Python'spipthen refuses withis not a supported wheel on this platform.Sources
Cygwin's
<sys/utsname.h>in the newlib-cygwin tree:https://sourceware.org/git/?p=newlib-cygwin.git;a=blob;f=winsup/cygwin/include/sys/utsname.h
Checklist
libc-test/semverhave been updated — N/A; correcting layout of an existing struct field, not adding/removing symbols.*LASTor*MAXare includedcargo test --target x86_64-pc-cygwinpasses onlibc-0.2with this fix; passes onmainwith this fix plus the cpuset_t typo fix from companion PR cygwin: fix cpuset_t typo in CPU_ZERO #5098. The added regression test verifiesuname.nodename == gethostname()and that all variable-length fields read non-empty.Note: this PR is independent but
maindoes not currently build onx86_64-pc-cygwindue to a separatecpuset_ttypo, addressed in companion PR #5098. The fix here was verified end-to-end on both thelibc-0.2branch (which builds cleanly) and onmainwith both fixes stacked.@rustbot label +stable-nominated