No Time for Chrono
October 2021
TL;DR: Time 0.1 and 0.2 have a security notice about use of the localtime_r
libc function, and Chrono has a related notice, both issued in November 2020.
While Time has a mitigation in place, Chrono doesn’t and doesn’t plan to.
The security issue is very specific and can be mitigated through your own efforts; there’s also some
controversy on if it is an issue at all. The Time crate has evolved a lot in the past few years and
its 0.3 release has a lot of APIs that Chrono is used for; thus it is possible for many, but not
all, Chrono users to switch to Time 0.3; this could have some additional benefit.
The security issue
From the advisory:
Unix-like operating systems may segfault due to dereferencing a dangling pointer in specific circumstances. This requires an environment variable to be set in a different thread than the affected functions. This may occur without the user’s knowledge, notably in a third-party library.
Non-Unix targets (including Windows and wasm) are unaffected.
Advisory notices
- RustSec:
- RUSTSEC-2020-0071 on Time 0.1 and 0.2.7–0.2.22
- RUSTSEC-2020-0159 on Chrono
- Mitre:
- GitHub:
localtime_r
and setenv
From the manpage, edited for length:
The
localtime_r()
function converts the calendar timetimep
to broken-down time representation, expressed relative to the user’s specified timezone. The function acts as if it calledtzset(3)
and provides information about the current timezone, the difference between Coordinated Universal Time (UTC) and local standard time in seconds, and whether daylight savings time rules apply during some part of the year.
Meanwhile, setenv:
…adds the variable
name
to the environment with the valuevalue
…
The issue occurs when the environment of a program is modified (chiefly with setenv
) at the same
time that localtime_r
is used; in those cases, a segfault may be observed.
localtime_r
is a fairly complex beast, which interfaces with the system’s timezone files and
settings to provide localtime information and conversion. It is a very useful function that is used
pretty much everywhere that needs to interact with local/utc time and timezones. Handling timezones
is largely regarded as all programmers’ bane; replacing this function with one that does not have
the behaviour is a potentially massive endeavour. Over on IRLO, Tony Arcieri
provides a summary of the Rust side of this issue.
Time has mitigated the issue by removing the calls to localtime_r
, returning errors in 0.2 and 0.3
unless a custom cfg
is passed to the compiler. That does mean that if you
do want that information, you’re out of luck unless you (as an application) or all your end-users
(as a library) enable that custom configuration.
The counter view
Rich Felker (author of musl) has another view. He argues that the issue is not in
calling the localtime_r
function, but in modifying the environment. The environment ought to be
immutable, and it is somewhat well known in other large projects that the footgun exists:
- In the Julia language, also featuring Felker
- In C#’s CoreCLR
- In OpenBLAS
- In GNOME
- In the POSIX standard
This issue is even known to the Rust project, with a documentation PR in 2015(!) adding cautionary
language to the std::env::set_var
function.
I don’t have nearly the same amount of knowledge in this issue, but for the record, and despite the sections below, I do agree with the view that the environment should be considered read-only. Perhaps a clippy lint could be added.
Replacing Chrono
Regardless of the previous discussion, there are other issues around usage of Chrono.
Chronic pains
Its last release at writing, 0.4.19, was more than a year ago. Issues are piling up. It’s still on edition 2015 (which to be clear isn’t really an issue, but more of an indicator).
It could just be that the crate is considered finished (the docs do describe it as “feature-complete”).
Or it may be that maintainers have mostly checked out. (No fault on the maintainers! I’ve done the same with Notify, once.)
If you’re fine with this, and you’re confident that you (and your dependencies) aren’t writing to the environment, then you can keep on using Chrono. There is, however, a viable alternative now:
Time 0.3
Time’s 0.3 release adds many APIs, which cover a large amount of the surface that Chrono is used for:
- No-alloc mode
- The
Month
type - Calendar/Ordinal/ISO/Julian conversions
- Large dates (beyond +/- 9999 years)
- Parsing and serde support
There are also some features which are only supported by newer Time, not by Chrono:
const
functions- the
datetime!
macro for constructing datetimes at compile-time - Serialising non-ISO8601 representations
- Random dates/times
- QuickCheck support
Therefore, you can now reasonable replace Chrono with Time!
(In the future I hope to provide a “quick migration guide” here. For now, it’s left to the reader!)