Skip to content

Incorrect terminal baud rate on glibc 2.42+ #366

@achirkin

Description

@achirkin

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.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions