BaudRate pattern synonyms (B9600 etc.) are baked in by hsc2hs at GHC build time and become wrong when the runtime glibc differs from the build-time glibc, because glibc 2.42 changed B9600 from 13 to 9600.
Your environment
Which OS do you use:
Ubuntu 25.10 (glibc 2.42), GHC 9.6.7 (built against glibc <= 2.41), unix-2.8.6.0
Describe your project (alternative: link to the project):
Programming a small TTY USB device (serial port at 9600 baud)
Steps to reproduce
Use withOutputSpeed attrs B9600 to configure a serial port. The B9600 pattern synonym resolves to BaudRate 13 (the old glibc kernel-encoding constant baked in at GHC build time), but the runtime glibc 2.42 cfsetospeed expects 9600. It interprets 13 as a custom baud rate via BOTHER, configuring the port for 13 baud.
Expected behaviour
B9600 should set the serial port to 9600 baud. cfsetospeed should receive a value that the runtime glibc interprets as 9600 baud.
Actual behaviour
The port is configured for 13 baud. The device does not respond. Visible via strace: ioctl(TCSETS2, {c_cflag=BOTHER, c_ospeed=13}) instead of c_cflag=B9600, c_ospeed=9600. Workaround: use BaudRate 9600 instead of B9600.
Include debug information
The BaudRate pattern synonyms are defined via hsc2hs #const (e.g. pattern B9600 = BaudRate (#const B9600)), capturing glibc's B9600 macro at GHC build time. In glibc <= 2.41, B9600 = 13 (kernel encoding). In glibc >= 2.42, B9600 = 9600 ("sane speed_t"). When GHC is built against old glibc but runs on new glibc, the stale constant 13 is passed to the new versioned cfsetospeed@GLIBC_2.42, which treats it as an arbitrary speed.
glibc change: commit 5cf101a85aae and relevant changeset. Same bug in Rust: rust-lang/libc#4692.
API breaking changes
The BaudRate pattern synonyms are now potentially wrong on any system where the build-time and runtime glibc versions straddle the 2.41/2.42 boundary.
Posix compliance
POSIX specifies speed_t as opaque and the Bxxx constants as implementation-defined. The issue is that glibc changed their values, but the unix package bakes them in at compile time rather than querying them at runtime.
BaudRate pattern synonyms (B9600 etc.) are baked in by hsc2hs at GHC build time and become wrong when the runtime glibc differs from the build-time glibc, because glibc 2.42 changed B9600 from 13 to 9600.
Your environment
Which OS do you use:
Ubuntu 25.10 (glibc 2.42), GHC 9.6.7 (built against glibc <= 2.41), unix-2.8.6.0
Describe your project (alternative: link to the project):
Programming a small TTY USB device (serial port at 9600 baud)
Steps to reproduce
Use
withOutputSpeed attrs B9600to configure a serial port. TheB9600pattern synonym resolves toBaudRate 13(the old glibc kernel-encoding constant baked in at GHC build time), but the runtime glibc 2.42cfsetospeedexpects 9600. It interprets 13 as a custom baud rate viaBOTHER, configuring the port for 13 baud.Expected behaviour
B9600should set the serial port to 9600 baud.cfsetospeedshould receive a value that the runtime glibc interprets as 9600 baud.Actual behaviour
The port is configured for 13 baud. The device does not respond. Visible via
strace:ioctl(TCSETS2, {c_cflag=BOTHER, c_ospeed=13})instead ofc_cflag=B9600, c_ospeed=9600. Workaround: useBaudRate 9600instead ofB9600.Include debug information
The
BaudRatepattern synonyms are defined viahsc2hs#const(e.g.pattern B9600 = BaudRate (#const B9600)), capturing glibc'sB9600macro at GHC build time. In glibc <= 2.41,B9600 = 13(kernel encoding). In glibc >= 2.42,B9600 = 9600("sane speed_t"). When GHC is built against old glibc but runs on new glibc, the stale constant 13 is passed to the new versionedcfsetospeed@GLIBC_2.42, which treats it as an arbitrary speed.glibc change: commit 5cf101a85aae and relevant changeset. Same bug in Rust: rust-lang/libc#4692.
API breaking changes
The
BaudRatepattern synonyms are now potentially wrong on any system where the build-time and runtime glibc versions straddle the 2.41/2.42 boundary.Posix compliance
POSIX specifies
speed_tas opaque and theBxxxconstants as implementation-defined. The issue is that glibc changed their values, but theunixpackage bakes them in at compile time rather than querying them at runtime.