Skip to content

Use the glibc 64bit time APIs to mitigate the Y2038 problem on 32bit platforms #3223

Open
@snogge

Description

@snogge
Contributor

The GNU libc (glibc) provides alternative 64bit entry points for many of the file and time related functions on 32bit platforms. For C programs these are activated by setting the -D_TIME_BITS=64 and -D_FILE_OFFSET_BITS=64 preprocessor flags. What happens is that all the relevant types (time_t, blksize_t, ...) are defined as 64bit integers and the called symbols are redirected from the standard 32-bit variant like gmtime to a 64bit variant like __gmtime64.

In many ways this is the same as -D_LARGEFILE_SOURCE flag except that this will not add new types and symbols but replace the existing ones. Note that it is possible to use -D_LARGEFILE_SOURCE and -D_FILE_OFFSET_BITS=64 at the same time.

For full 64bit time support on llnux you also need a kernel newer than 5.6, but the 64bit glibc functions should be safe to use anyway and no worse than the 32bit versions.

The 64bit time support is needed when building embedded linux systems on 32bit platforms that are expected to live for 10 to 15 years.

Activity

snogge

snogge commented on Apr 25, 2023

@snogge
ContributorAuthor

I've submitted #3175 as a possible solution.

safinaskar

safinaskar commented on May 10, 2023

@safinaskar

If somebody needs something like crate libc, but without time32 and lfs problems, I suggest using crate rustix. You can read its code and see that rustix put a lot of effort to deal with time32 and lfs problems

snogge

snogge commented on May 10, 2023

@snogge
ContributorAuthor

I can see how that would work if I control which crates I want to use. But I'm not designing these projects from scratch, I'm trying to build existing projects with plenty of dependencies that use the libc crate.
But if there is a way to drop rustix in as a replacement I'm all ears.

If it is not obvious: I don't know much about rust, I'm approaching this from a distro building perspective using the Yocto project.

nekopsykose

nekopsykose commented on May 10, 2023

@nekopsykose
Contributor

regardless of whether rustix works or not, libc has to be fixed anyway (i.e. this is lacking platform support and a goal of libc), so that is more of an offtopic suggestion, even if it's what somebody wants to use for a usecase.

lvella

lvella commented on Jun 20, 2023

@lvella

I think the sensible solution here is to simply use 64 bits version of these types in every platform that supports them. In this time and age, I think it makes no sense for Rust's libc to support 32 bits off_t, time_t, etc.

It is not like we have to worry about not breaking a huge ecosystem of 30 years old code that assumes these types to be 32 bits, like they have to in C.

snogge

snogge commented on Jun 21, 2023

@snogge
ContributorAuthor

Will this be part of the 0.3 release? I see a lot of talk about the same problem on musl.

JohnTitor

JohnTitor commented on Jun 21, 2023

@JohnTitor
Member

cc @joshtriplett as you facilitate the libc 0.3 here: #3248

Amanieu

Amanieu commented on Jun 21, 2023

@Amanieu
Member

The main concern here is compatibility with C code that we link to using FFI. If some C code uses time_t, we want to ensure that our FFI bindings in Rust use the same definition of time_t as the C code. The general policy for the libc crate is to match the behavior of C with no preprocessor options defined. In the case of musl 1.2 this would default to 64-bit time but for glibc it would default to 32-bit time.

This is similar to the situation with 64-bit off_t: we default to 32-bit off_t but also expose LFS types and functions such as stat64. This won't work for 64-bit time since there are no alternate type/function names, but one possible solution would be to expose the 64-bit time_t types and functions under a time64 module.

snogge

snogge commented on Jun 22, 2023

@snogge
ContributorAuthor

I understand your concern. But won't that be just as big a problem if the library has been built with the 64-bit flags set even if that is not the default for that library?
I suspect that these flags will be set on a distro level on Linux. How would Rust applications that use libc handle that?

Note that as long as the C library does not use any of the affected types such as time_t in their ABI, there is no conflict.

My usecase is an embedded linux distribution created with Yocto. Everything is built from source and I control all the compiler settings. I need to make any Rust app build against 64-bit libs, and right now it seems my only option is to patch all uses of libc. For this, I would be happy with a switch I could set globally but I can see how that is not usable by everyone.

Amanieu

Amanieu commented on Jun 23, 2023

@Amanieu
Member

Unfortunately there is just no way for Rust to know what flags any particular C code was compiled with. My recommendation would be to expose the time64 APIs in a time64 module, and only for glibc. Then Rust programs using the libc crate can choose which API to use depending on the flags use to compile the C code (e.g. using the cc crate from build scripts).

Most Rust programs will not interact with the libc crate directly though: most will use the standard library's SystemTime instead. This already supports 64-bit time APIs on glibc by calling symbols such as __clock_gettime64 directly.

kanavin

kanavin commented on Aug 25, 2023

@kanavin

SystemTime and libc crate aren't using musl correctly either. I got the following on a 32 bit musl system after setting the system clock to 2040.

I didn't yet look into details of what is happening, but please take this seriously: rust simply can't be used on 32 bit systems as of today, not if you want them to keep working after 2038:

root@qemux86:~/hello# RUST_BACKTRACE=full cargo build
thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: Os { code: 75, kind: Uncategorized, message: "Value too large for data type" }', library/std/src/sys/unix/time.rs:404:72
stack backtrace:
   0:  0x103b76e - <std::sys_common::backtrace::_print::DisplayBacktrace as core::fmt::Display>::fmt::h298f061022fcd35f
   1:  0x10932f0 - core::fmt::write::h35b33882d9394d02
   2:  0x1062721 - std::io::Write::write_fmt::haa08496d0a09f55a
   3:  0x103b534 - std::sys_common::backtrace::print::hdd15c84bc6d639a0
   4:  0x1040e22 - std::panicking::default_hook::{{closure}}::h5a904d4c613603d1
   5:  0x1040a8e - std::panicking::default_hook::h151573c2103f9e1d
   6:  0x10419cd - std::panicking::rust_panic_with_hook::h253407a2d246e6f4
   7:  0x103bacd - std::panicking::begin_panic_handler::{{closure}}::hc37c74e9bd7e9112
   8:  0x103b8a9 - std::sys_common::backtrace::__rust_end_short_backtrace::ha4ef9ac410a9a589
   9:  0x1041512 - rust_begin_unwind
  10:   0x51ea15 - core::panicking::panic_fmt::h2d2cbb4e20fa6b71
  11:   0x51e912 - core::result::unwrap_failed::h8814d485edf5763b
  12:  0x1049f1d - std::sys::unix::time::inner::<impl std::sys::unix::time::Timespec>::now::h558a463fd73b0e89
  13:  0x10707ac - std::time::SystemTime::now::h6c0b823bbb84a570
  14:   0x5fba60 - cargo::core::compiler::timings::Timings::new::h120d8638ea051b42
  15:   0x8e2106 - cargo::core::compiler::job_queue::JobQueue::new::h9433228ad310e0d9
  16:   0x751aa7 - cargo::core::compiler::context::Context::compile::hc66564059807438c
  17:   0xa4b65f - cargo::ops::cargo_compile::compile_ws::hc837eacdf826dd77
  18:   0xa4b352 - cargo::ops::cargo_compile::compile::h7008d233c91a9342
  19:   0x536221 - cargo::commands::build::exec::h451c313ba133f7a0
  20:   0x5465c3 - cargo::cli::execute_subcommand::h10bbc4a45aa39bf7
  21:   0x543e2f - cargo::cli::main::h90c338691b998293
  22:   0x5a6231 - cargo::main::ha655f162b7ead5e5
  23:   0x524634 - std::sys_common::backtrace::__rust_begin_short_backtrace::h80d0852799ad4e70
  24:   0x59387e - std::rt::lang_start::{{closure}}::h1188bc695e7dc705
  25:  0x10412c7 - std::panicking::try::hdc84a4a49c4e71bf
  26:  0x1061b8e - std::rt::lang_start_internal::hac224e5bce96cd89
  27:   0x5a8c38 - main
  28: 0xb7efc1bf - libc_start_main_stage2
                       at /usr/src/debug/musl/1.2.4+gitAUTOINC+83b858f83b-r0/src/env/__libc_start_main.c:95:2
snogge

snogge commented on Aug 25, 2023

@snogge
ContributorAuthor

Wouldn't using a time64 module require source code changes to all users of the libc crate?
Is there any way we can use a configuration option to the libc crate and make it use only 64bit time?

kanavin

kanavin commented on Aug 25, 2023

@kanavin
kanavin

kanavin commented on Aug 25, 2023

@kanavin

After some poking at musl code, I think it has the same ABI as as glibc: clock_gettime() is the 32 bit version, and if you want 64 bits, you must call into __clock_gettime64(). C headers would take care of this automatically, but rust won't use them, so 🤷

musl's version of _TIME_BITS define is _REDIR_TIME64, and it's set to 1 by default in its headers (unlike glibc where you need to opt into 64 bit time via externally set -DTIME_BITS=64).

20 remaining items

snogge

snogge commented on Apr 22, 2024

@snogge
ContributorAuthor

I could update the PR to check the CFLAGS environment variable, but wouldn't that only work in distro-like builders? Most users wont have CFLAGS set in their environments. And the correct flags might not be in CFLAGS anyway. The Yocto Projects puts them in the CC variable.

kanavin

kanavin commented on Apr 22, 2024

@kanavin

I could update the PR to check the CFLAGS environment variable, but wouldn't that only work in distro-like builders? Most users wont have CFLAGS set in their environments. And the correct flags might not be in CFLAGS anyway. The Yocto Projects puts them in the CC variable.

This is correct, CFLAGS is not the canonical place to put these, and I was confused about where yocto puts them. There are various way to prescribe what ends up in C preprocessor, e.g. environment variables with command line flags, Makefiles, per-project config includes etc.

That said, I would really appreciate at least having some environment variable that can be set by distro builders to force 64 bit time in rust libc in a complete system build. You can name it RUST_LIBC_USE_64BIT_TIME. We (the yocto project) willl take care of setting it as appropriate.

sdroege

sdroege commented on Apr 22, 2024

@sdroege

AFAICS the most user-friendly way would be to check the target triplet, and Linux distro version from build.rs and based on that and a table auto-detect the variant of time_t (and off_t) that should be used. Plus an override for the auto-detection.

That probably won't work for Yocto though as there it's actually a configuration (can that be retrieved somehow easily?) and not a simple matter of checking a version.

snogge

snogge commented on Apr 22, 2024

@snogge
ContributorAuthor

Isn't that what cargo is doing by passing the various CARGO_CFG variables? See the is_gnu_time64_abi function in build.rs.
I don't think it is reasonable for the libc crate to carry a list of all distro/triplet combinations that should configure things one way or another. Auto-detect by cargo with a possibility to override sounds OK.

kanavin

kanavin commented on Apr 22, 2024

@kanavin

Isn't that what cargo is doing by passing the various CARGO_CFG variables? See the is_gnu_time64_abi function in build.rs. I don't think it is reasonable for the libc crate to carry a list of all distro/triplet combinations that should configure things one way or another. Auto-detect by cargo with a possibility to override sounds OK.

I'm also not keen on the hardcoded distro/version table idea. As far as I understand, the only remaining binary distro that has any interest at all in 32 bit computing (and fixing y2038 in it) is Debian. For yocto such auto-detection is not useful as it is a generator of custom distros, and especially if build.rs would try to read things in /etc to determine 'distro (and its version) it would make completely incorrect decisions.

is_gnu_time64_abi() as it is has two problems:

  • riscv32 has had 64 bit time from the beginning. There may be other targets like that.
  • no ability to override its decision from an environment variable

I'd consider not trying to guess this at all, and just setting from environment variable only, as an opt-in.

snogge

snogge commented on Apr 23, 2024

@snogge
ContributorAuthor

The riscv32 variant does not look at the gnu_time64 config option, or does so in a safe way, so is_gnu_time_64 does not have to handle it.
An override to force either variant seems like a good idea to me.

snogge

snogge commented on May 8, 2024

@snogge
ContributorAuthor

I've updated #3175 to handle the cases listed above.

  • The environment variable RUST_LIBC_TIME_BITS can be set to 32 to force 32-bit time_t
  • gnu_time64_abi is not set for riscv32.
added this to the 1.0 milestone on Aug 29, 2024
safinaskar

safinaskar commented on Dec 8, 2024

@safinaskar

Possible solution to "how to distinguish 32 and 64" problem: rust-lang/rfcs#3716 ("[RFC] Target Modifiers")

tgross35

tgross35 commented on Dec 13, 2024

@tgross35
Contributor

Possible solution to "how to distinguish 32 and 64" problem: rust-lang/rfcs#3716 ("[RFC] Target Modifiers")

As proposed, isn't this only for things that rustc is aware of via flags? I don't think it will know which time ABI is in use.

safinaskar

safinaskar commented on Apr 11, 2025

@safinaskar
martinetd

martinetd commented on Jun 13, 2025

@martinetd

Hi all, thank you for working on this issue.

I'm coming in late to the party and have read what I could find, but I'm not quite clear on the status of 64 bit time_t on 32bit arches.
On the libc 1.0 tracking I'm reading between the lines that it'd be ok to break the ABI for 1.0, so finishing the musl upgrade in #3068 and merging into the main branch would be acceptable (even if it won't actually yield any result until the actual libc 1.0 release)?

But the last bit of the zulipchat link said we might not want to wait for 1.0 because distros have been doing a lot of work around 64-bit time_t on 32bit arches, and concluded with adding libc 1.0 to the all-hands agenda, but as far as I understand the all-hands is over and I've not seen any recap or movement since (but then again it's only been a few weeks).
Has there been any sort of consensus?

My work isn't giving me quite enough time to be highly active on this, but our customers are asking about y2038 regularly and I'll be happy to lend a hand where I can reach -- alternatively if there's a known workaround I'd be more than happy to try, but I couldn't find any navigating this and related issues (for an axum web server running on armhf alpine -- this is our last component that hasn't supported 64 bit time for a while...)

Cheers

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

      Development

      No branches or pull requests

        Participants

        @snogge@Amanieu@sdroege@safinaskar@kanavin

        Issue actions

          Use the glibc 64bit time APIs to mitigate the Y2038 problem on 32bit platforms · Issue #3223 · rust-lang/libc