riscv32gc-unknown-linux-gnu: `timex` uses the 32-bit `c_long` layout (144 bytes), glibc's riscv32 `struct timex` is 208 bytes

Aperta Adatta ai principianti
#5,697 1 commento 0 reazioni 0 assegnatari Vedi su GitHub

I maintainer di solito rispondono entro 1 giorno

@compiler-segfault ci sta già lavorando.

Dal 5/11/2026.

  • #5717 di @compiler-segfault — aperta

Valutazione

Difficoltà
2/5
Tempo stimato
1-3 ore
Idoneità per principianti
76/100
Tipo di issue
Bug
Chiarezza
Specificata chiaramente
Stato di attività
Attiva
Stack tecnologico
rust

Direzione di ricerca

Leggi src/unix/linux_like/linux/gnu/b32/mod.rs e la configurazione della destinazione in build.rs descritta nell’issue. Confronta le disposizioni di timex con bits/timex.h di glibc per riscv32, poi controlla la disposizione di riscv32 e assicurati che i686 e armv7 mantengano le dimensioni attuali. Il lavoro è completato quando timex corrisponde alla disposizione di 208 byte di glibc su riscv32 senza scrivere oltre la sua fine.

Scritto dal modello di indicizzazione a partire dal testo della issue.

Descrizione

C-bug

Target: riscv32gc-unknown-linux-gnu (glibc; 64-bit time_t is this target's only ABI)

Versions: libc 0.2.186, 0.2.191 and 1.0.0-alpha.5 (the declarations quoted below are unchanged on main); glibc 2.44; rustc 1.98.1.

src/unix/linux_like/linux/gnu/b32/mod.rs declares timex twice: the c_longlong layout under #[cfg(gnu_time_bits64)] (0.2.191 lines 191-227) and the c_long layout under #[cfg(not(gnu_time_bits64))] (lines 229-262). The build script never sets gnu_time_bits64 on riscv32, because its time_t is 64-bit already (build.rs:329-345):

    // 32-bit arches with 64-bit time_t by default. rv32 is 64-only, `x86_64` covers the x32 arch,
    // `!(linux|windows|hurd)` covers vxworks and future platforms.
    let gnu32_already_time64 = target_arch == "riscv32"
        || target_arch == "x86_64"
        || !matches!(target_os.as_str(), "linux" | "windows" | "hurd");
    let gnu = target_env == "gnu";
    let gnu32_time64 = gnu && target_ptr_width == "32" && (gnu32_timebits == "64" || time64);

    if gnu32_time64 && !gnu32_already_time64 {

so riscv32 gets the time32 layout, with 32-bit long fields:

    #[cfg(not(gnu_time_bits64))]
    pub struct timex {
        pub modes: c_uint,
        pub offset: c_long,
        pub freq: c_long,
        pub maxerror: c_long,
        pub esterror: c_long,
        pub status: c_int,
        pub constant: c_long,
        pub precision: c_long,
        pub tolerance: c_long,
        pub time: crate::timeval,
        ...

glibc's riscv32 struct timex takes the long long layout whenever __TIMESIZE == 64 && __WORDSIZE == 32 (glibc 2.44, bits/timex.h:26-36; riscv32's bits/timesize.h defines __TIMESIZE as 64):

struct timex
{
# if defined __USE_TIME64_REDIRECTS || (__TIMESIZE == 64 && __WORDSIZE == 32)
  unsigned int modes;          /* mode selector */
  int :32;                     /* pad */
  long long offset;            /* time offset (usec) */
  long long freq;              /* frequency offset (scaled ppm) */
  long long maxerror;          /* maximum error (usec) */
  long long esterror;          /* estimated error (usec) */
  int status;                  /* clock command/status */
  int :32;                     /* pad */

Layouts, measured

libc timex glibc struct timex
size 144 208
offset of freq 8 16
offset of status 20 40
offset of time 40 72
offset of tai 92 160

The libc column: const assertions under cargo check --target riscv32gc-unknown-linux-gnu, the same on 0.2.186, 0.2.191 and 1.0.0-alpha.5. The glibc column: _Static_asserts compiled by riscv32-unknown-linux-gnu-gcc 16.2.1 against glibc 2.44's headers.

At run time (qemu-riscv32 10.2.3, glibc 2.44, libc 0.2.191): adjtimex() with modes = 0, called on a libc::timex at the head of a canary-filled 256-byte buffer, changes the 20 bytes at offsets 144-163 (glibc's errcnt, stbcnt and tai): it writes past the end of the Rust struct. And status reads 0x0 (the upper half of glibc's freq) while glibc's status holds 0x40, STA_UNSYNC, so an unsynchronized clock reads as synchronized.

Minimal example (no dependency but libc):

#![no_std]
const _: [(); 208] = [(); core::mem::size_of::<libc::timex>()];

cargo check --target riscv32gc-unknown-linux-gnu fails with expected an array with a size of 208, found one with a size of 144.

A possible fix: give riscv32 the c_longlong layout too, #[cfg(any(gnu_time_bits64, target_arch = "riscv32"))] on the first declaration and #[cfg(not(any(gnu_time_bits64, target_arch = "riscv32")))] on the second. With that change on 0.2.191: riscv32 208 bytes, adjtimex() changes nothing past the struct, status reads 0x40; i686 and armv7 keep their 128 bytes.

Related: the time64 tracking issue #5507.

Lingua principale
Rust
Stelle
2.6k
Fork
1.3k
Merge medio
2g 8h
PR unite (30g)
27

Preparare l'ambiente

Come iniziare

  1. Leggi tutta la issue e poi la guida ai contributi del progetto.
  2. Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
  3. Fai un fork del repository e lavora su un branch.
  4. Apri una pull request che faccia riferimento al numero della issue.

Altre issue di rust-lang/libc

Tutte le issue di rust-lang/libc

Issue simili

Altre issue su Rust

Ricevi le nuove issue nella tua casella

Un breve riepilogo di issue GitHub adatte ai principianti.