|
| 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