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