Skip to content

Commit 427e767

Browse files
committed
1.24.1 announcement
1 parent a2eed96 commit 427e767

File tree

1 file changed

+257
-0
lines changed

1 file changed

+257
-0
lines changed

_posts/2018-03-01-Rust-1.24.1.md

Lines changed: 257 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,257 @@
1+
---
2+
layout: post
3+
title: "Announcing Rust 1.24.1"
4+
author: The Rust Core Team
5+
---
6+
7+
The Rust team is happy to announce a new version of Rust, 1.24.1. Rust is a
8+
systems programming language focused on safety, speed, and concurrency.
9+
10+
If you have a previous version of Rust installed via rustup, getting Rust
11+
1.24.1 is as easy as:
12+
13+
```bash
14+
$ rustup update stable
15+
```
16+
17+
If you don't have it already, you can [get `rustup`][install] from the
18+
appropriate page on our website, and check out the [detailed release notes for
19+
1.24.1][notes] on GitHub.
20+
21+
[install]: https://www.rust-lang.org/install.html
22+
[notes]: https://github.com/rust-lang/rust/blob/stable/RELEASES.md#version-1241-2018-03-01
23+
24+
## What's in 1.24.1 stable
25+
26+
As you know, we tend not to release point releases. In this case, we had a few regressions in 1.24.0.
27+
Each of them individually was not a "hair on fire" moment, but there's two reasons we decided to
28+
produce a point release. First, collectively, they were big enough to consider as a whole. Second, we
29+
want our attitude towards regressions to be "if we're not sure, default to reverting." Given that
30+
it was not clear that we *shouldn't* release a point release, we decided to err on the side of
31+
cautuion.
32+
33+
A quick summary of the changes: there are four.
34+
35+
* Do not abort when unwinding through FFI
36+
* Emit UTF-16 files for linker arguments on Windows
37+
* Make the error index generator work again
38+
* Cargo will warn on Windows 7 if an update is needed.
39+
40+
In general, if you haven't run into any build issues, the "do not abort when unwinding through FFI"
41+
change is the only one you should care about. The others are also important, but if 1.24.0 has been
42+
working well for you, this is the only change that affects you. We plan on bringing this behavior back
43+
in 1.25 or 1.26, depending on how smoothly the new strategy goes.
44+
45+
With that, let's dig into the details!
46+
47+
### Do not abort when unwinding through FFI
48+
49+
Quoting [the 1.24 annoucement](https://blog.rust-lang.org/2018/02/15/Rust-1.24.html):
50+
51+
> There’s one other change we’d like to talk about here: undefined behavior.
52+
> Rust generally strives to minimize undefined behavior, having none of it in
53+
> safe code, and as little as possible in unsafe code. One area where you could
54+
> invoke UB is when a panic! goes across an FFI boundary. In other words, this:
55+
56+
```rust
57+
extern "C" fn panic_in_ffi() {
58+
panic!("Test");
59+
}
60+
```
61+
62+
> This cannot work, as the exact mechanism of how panics work would have to
63+
> be reconciled with how the "C" ABI works, in this example, or any other ABI
64+
> in other examples.
65+
>
66+
> In Rust 1.24, this code will now abort instead of producing undefined behavior.
67+
68+
We have reverted this behavior, and 1.24.1 will reintroduce the undefined behavior.
69+
Fundamentally, this is a soundness fix, and we still plan on reintroducing the
70+
abort, but are going to be rolling it out a bit more slowly, and with a different
71+
implementation.
72+
73+
It started with [a bug filed against the `rlua`
74+
crate](https://github.com/chucklefish/rlua/issues/71). `rlua` is a package
75+
that provides high level bindings between Rust and the [Lua programming
76+
language](https://www.lua.org/).
77+
78+
> Side note: `rlua` is maintained by [Chucklefish](https://chucklefish.org/),
79+
> a game development studio from London that's using Rust. Lua is a very
80+
> popular language to use for extending and scripting games. We care deeply about
81+
> production Rust users, and so handling this was a very high priority for the
82+
> Rust team.
83+
84+
In short, `rlua` error handling just... broke. On Windows, and only on Windows,
85+
any attempt to handle errors from Lua would simply abort. This makes `rlua`
86+
unusable, as any error of any kind within Lua causes your program to die.
87+
88+
After digging in, the culpurit was found: `setjmp`/`longjmp`. These functions
89+
are provided by the C standard library as a means of handling errors. You
90+
first call `setjmp`, and then, at some later point in time, call `longjmp`.
91+
When you do, control flow returns to where you had previously called
92+
`setjmp`. This is often used as a way to implement exceptions, and sometimes,
93+
even coroutines. Lua's implementation uses `setjmp`/`longjmp` [to implement
94+
exceptions](https://www.lua.org/pil/24.3.html):
95+
96+
> Unlike C++ or Java, the C language does not offer an exception handling
97+
> mechanism. To ameliorate this difficulty, Lua uses the setjmp facility from
98+
> C, which results in a mechanism similar to exception handling. (If you
99+
> compile Lua with C++, it is not difficult to change the code so that it uses
100+
> real exceptions instead.)
101+
102+
The issue is this: what happens when some C code `setjmp`/`longjmp`'s through
103+
Rust stack frames? Because drop checking and borrow checking know nothing
104+
about this style of control flow, if you `longjmp` across a Rust stack
105+
frame that has any type that's not `Copy` on its stack, undefined
106+
behavior will result. However, if the jump happens entirely in C, this
107+
should work just fine. This is how `rlua` was managing it: every call
108+
into Lua is [wrapped with `lua_pcall`](https://www.lua.org/pil/24.3.2.html):
109+
110+
> When you write library functions for Lua, however, there is a standard way
111+
> to handle errors. Whenever a C function detects an error, it simply calls
112+
> `lua_error`, (or better yet `luaL_error`, which formats the error message and
113+
> then calls `lua_error`). The `lua_error` function clears whatever needs to be
114+
> cleared in Lua and jumps back to the `lua_pcall` that originated that
115+
> execution, passing along the error message.
116+
117+
So, the question becomes: Why does this break? And why does it break on
118+
Windows?
119+
120+
When we talked about `setjmp`/`longjmp` inititally, a key phrase here wasn't
121+
highlighted. Here it is:
122+
123+
> After digging in, the culpurit was found: `setjmp`/`longjmp`. These functions
124+
> are *provided by the C standard library* as a means of handling errors. You
125+
126+
These functions aren't part of the C language, but part of the standard
127+
library. That means that platform authors implement these functions, and
128+
their implementations may differ.
129+
130+
Windows has a concept called SEH, short for ["Structured Exception
131+
Handling"](https://msdn.microsoft.com/en-us/library/windows/desktop/ms680657(v=vs.85).aspx).
132+
Windows uses SEH to implement `setjmp`/`longjmp`, as the whole idea of SEH
133+
is to have uniform error handling. For similar reasons, C++ exceptions use
134+
SEH, as do Rust panics.
135+
136+
Before we can sort the exact details of what's happening, let's look at how `rlua`
137+
works. `rlua` has an internal function, `protect_lua_call`, used to call into
138+
Lua. Using it looks like this:
139+
140+
```rust
141+
protect_lua_call(self.state, 0, 1, |state| {
142+
ffi::lua_newtable(state);
143+
})?;
144+
```
145+
146+
That is, `protect_lua_call` takes some arguments, one of which is a closure. This
147+
closure is passed to `lua_pcall`, which catches any `longjmp`s that may be thrown
148+
by the code passed to it, aka, that closure.
149+
150+
Consider the code above, and imagine that `lua_newtable` here could call
151+
`longjmp`. Here's what should happen:
152+
153+
1. `protect_lua_call` takes our closure, and passes it to `lua_pcall`.
154+
2. `lua_pcall` calls `setjmp` to handle any errors, and invokes our closure.
155+
2. Inside our closure, `lua_newtable` has an error, and calls `longjmp`.
156+
3. The initial `lua_pcall` catches the `longjmp` with the `setjmp` it called earlier.
157+
4. Everyone is happy.
158+
159+
However, `lua_newtable` is an `extern fn` to Rust. So, with the chances in
160+
1.24.1, it sets up a panic handler that will cause an abort. In other words,
161+
the code sorta looks something like this psuedo code now:
162+
163+
```rust
164+
protect_lua_call(self.state, 0, 1, |state| {
165+
let result = panic::catch_unwind(|| {
166+
ffi::lua_newtable(state);
167+
});
168+
169+
if result.is_err() {
170+
process::abort();
171+
}
172+
})?;
173+
```
174+
175+
Earlier, when discussing `setjmp`/`longjmp`, we said that the issue with it in
176+
Rust code is that it doesn't handle Rust destructors. So, on every platform but
177+
Windows, the above `catch_unwind` shenanigans is effectively ignored, so
178+
everything works. However, on Windows, since both `setjmp`/`longjmp` and Rust
179+
panics use SEH, the `longjmp` gets "caught", and runs the new abort code!
180+
181+
The [solution here](https://github.com/rust-lang/rust/pull/48572) is to
182+
generate the abort handler, but in a way that `longjmp` won't trigger. Eventually,
183+
it's likely we'll have to register our own personality functions and such, but
184+
this is good enough for now. It's not 100% clear if this will make it into Rust 1.25;
185+
if the landing is smooth, we may backport, otherwise, this functionality will
186+
be back in 1.26.
187+
188+
### Emit UTF-16 files for linker arguments on Windows
189+
190+
In constrast with the previous bug, which is very complex and tough to understand,
191+
this bug's impact is simple: if you have non-ASCII paths in the directory where
192+
you invoke `rustc`, in 1.24, it would incorrectly error with a message like
193+
194+
> fatal error LNK1181: cannot open input file
195+
196+
The PR that caused it, [#47507](https://github.com/rust-lang/rust/pull/47507),
197+
has a good explanation of the behavior that ended up causing the problem:
198+
199+
> When spawning a linker rustc has historically been known to blow OS limits
200+
> for the command line being too large, notably on Windows. This is especially
201+
> true of incremental compilation where there can be dozens of object files per
202+
> compilation. The compiler currently has logic for detecting a failure to
203+
> spawn and instead passing arguments via a file instead, but this failure
204+
> detection only triggers if a process actually fails to spawn.
205+
206+
However, when generating that file, we were doing it incorrectly. As [the
207+
docs state](https://docs.microsoft.com/en-gb/cpp/build/reference/unicode-support-in-the-compiler-and-linker#linker-response-files-and-def-files):
208+
209+
> Response files and DEF files can be either UTF-16 with a BOM, or ANSI.
210+
211+
We were providing a UTF-8 encoded file, with no
212+
[BOM](https://en.wikipedia.org/wiki/Byte_order_mark). The fix is therefore
213+
straightforward: produce a UTF-16 file with a BOM.
214+
215+
### Make the error index generator work again
216+
217+
When packaging Rust for various Linux distros, it was found that [building
218+
1.24 with 1.24 fails](https://github.com/rust-lang/rust/issues/48308).
219+
This was caused by an incorrect path, causing certain metadata to not
220+
be generated properly.
221+
222+
### Cargo will warn on Windows 7 if an update is needed.
223+
224+
In February of 2017, [GitHub announced that they were dropping support for
225+
weak cryptographic
226+
standards](https://githubengineering.com/crypto-deprecation-notice/). One
227+
year later, in February of 2018, [the deprecation period is over, and support
228+
is
229+
removed](https://blog.github.com/2018-02-23-weak-cryptographic-standards-removed/).
230+
In general, this is a great thing.
231+
232+
Cargo uses GitHub to store the index of Crates.io, our package repository.
233+
It also uses `libgit2` for `git` operations. `libgit2` uses
234+
[WinHTTP](https://msdn.microsoft.com/en-us/library/windows/desktop/aa382925(v=vs.85).aspx)
235+
for making HTTP calls. As part of the OS, its feature set depends on the OS you're using.
236+
237+
> This section uses "Windows 7" to mean "Windows 7, Windows Server 2018, and Windows Server 2012",
238+
> because it's much shorter. The following applies to all three of these editions of Windows,
239+
> however.
240+
241+
Windows 7 [received an update](https://support.microsoft.com/en-us/help/3140245/update-to-enable-tls-1-1-and-tls-1-2-as-a-default-secure-protocols-in)
242+
in June of 2016 regarding TLS. Before the patch, Windows 7 would use TLS 1.0 by default. This update
243+
allows for applications to use TLS 1.1 or 1.2 natively instead.
244+
245+
If your system has not received that update, then you'd still be using TLS 1.0. This means
246+
that accessing GitHub would start to fail.
247+
248+
`libgit2` [created a fix](https://github.com/libgit2/libgit2/pull/4550), using the `WinHTTP` API
249+
to request TLS 1.2. On master, we've [updated to fix this](https://github.com/rust-lang/cargo/pull/5091),
250+
but for 1.24.1 stable, we're [issuing a warning](https://github.com/rust-lang/cargo/pull/5069),
251+
suggesting that they upgrade their Windows version. There's a balance here: we could have backported
252+
the `libgit2` fixes, but that'd require updating a lot of code, which feels incorrect for a point
253+
release, especially since the issue does not affect patched systems.
254+
255+
## Contributors to 1.24.1
256+
257+
[Thanks!](https://thanks.rust-lang.org/rust/1.24.1)

0 commit comments

Comments
 (0)