No Time for Chrono
TL;DR: Time 0.1 and 0.2 have a security notice about use of the
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
- Replacing Chrono
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.
- RUSTSEC-2020-0071 on Time 0.1 and 0.2.7–0.2.22
- RUSTSEC-2020-0159 on Chrono
From the manpage, edited for length:
localtime_r()function converts the calendar time
timepto broken-down time representation, expressed relative to the user’s specified timezone. The function acts as if it called
tzset(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.
…adds the variable
nameto the environment with the value
The issue occurs when the environment of a program is modified (chiefly with
setenv) at the same
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
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
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.
Regardless of the previous discussion, there are other issues around usage of Chrono.
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’s 0.3 release adds many APIs, which cover a large amount of the surface that Chrono is used for:
- No-alloc mode
- 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:
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!)