9 ms·
Rust devs push back as Serde project ships precompiled binaries
- Icathian 3y agoI'm curious whether those with more professional exposure to rust are concerned about this or think it's noisy for no reason? I'd also be curious if anyone has any good explanations as to why a choice like this would be made?
- adastra22 3y agoIt’s all in the article.
- Icathian 3y agoI read the article, and the linked GitHub issue, and posted it. I'm curious if people who use this for a living and aren't some reporter pasting snippets agree with this framing. The variety of responses here now suggests many do not.
- adastra22 3y agoOk, well I'm a professional rust developer and I agree with the other Rust developers directly quoted in the article, of which there were many. But this isn't a Rust developer issue so much as a distro and security issue. Most Rust developers don't have to concern themselves with supply chain attacks or security audibility, so to them this probably seems like a weird thing to focus on. But as someone who does have to worry about such things, trading off the ability to audit your supply chain for a small, one-time compilation boost on your first compile is a _terrible_ trade-off.
- Icathian 3y agoThanks for your perspective. I appreciate it.
- dcan 3y agoThe tinfoil hat explanation is that the developer is breaking all projects that depend on the default package manager cargo, forcing them to move to buck or bazel - projects that the developer of serde-derive has invested work into. This is not noisy for no reason - the expectation is all dependencies are compiled from source on your computer, and here the developer forces the use of a binary program, with no alternative.
- philbo 3y agoAs someone who hasn't used Rust seriously since 2018, I'm tangentially curious what are the improvements (real or perceived) that buck and bazel offer over cargo? I didn't know there were alternative package managers, and cargo always seemed pretty nice to me.
- philbo 3y agoAnswering my own question, sorry, looks like I may have got the wrong end of the stick here. Bazel and buck seem like language-agnostic build tools, rather than rust-specific package managers?
- monocasa 3y ago> Bazel and buck seem like language-agnostic build tools, rather than rust-specific package managers? Yeah. Bazel is the sanitized version of Google's internal build system for most of their projects (known internally as blaze). Buck is Facebook's equivalent.
- MrJohz 3y agoThe previous comment is pretty paranoid thinking, so I wouldn't take it seriously. That said, Bazel and Buck are good for handling projects "at scale" (e.g. company wide monorepos at mid-to-large companies), or with a lot of different needs (e.g. sources in C++, Rust, Python, and Javascript that need to be combined together). They're not really an alternative to Cargo, and more the next step up for when Cargo isn't sufficient any more. But because they're dealing with much more complicated problems, they're also more complicated to set up and use, so if you're not running into issues with Cargo, you probably don't need to go in that direction (and possibly never will).
- Ygg2 3y agoNot a Rust developer by trade, but from the offending pull request (https://github.com/serde-rs/serde/pull/2514#issue-1810688422 https://github.com/serde-rs/serde/pull/2514#issue-1810688422) it seems to offer 10x compile performance on first compile with small 3ms overhead on incremental compile. Is it worth it? Maybe. Downloading blobs isn't ideal but could be made easier in the future. Ideally a hack like this wouldn't be needed and we'd have comptime serde.
- joshka 3y ago10x sounds great until you look at this as an absolute value. It's 10 seconds. That would be huge if it were a a runtime cost, but this is a one time cost at compile time, and this build will be cached unless you're upgrading serde (or its dependencies). The value of that 10 seconds compared to the cost is a faustian bargain.
- Nullabillity 3y agoAnd even that 10s is assuming that you use no other proc macros that would bring in the same dependencies anyway (very unlikely).
- devjab 3y agoWe mainly use high level languages like TypeScript or Python because they work well for us. We do use C/C++ from time to time though, both for high performance requiring bottlenecks and for embedded since we do work with solar plant technology. A couple of years ago we looked into Rust and found it a combination of nice and wanting. It’s sort of great for low level programming and not really worth your time for anything else. Or at least that’s what we decided. We did adopt it and build a few solar inverter applications with Rust where we might’ve used C/C++ before, and it’s a decent language and nice to work with. But then a few months back, or maybe it was longer, some drama hit the ecosystem. Again it was so unimportant to us that I can’t even begin to remember what it was about, but similar to this Serde “drama” it speaks about an immature ecosystem and community. From a purely risk management offset we decided to stop using Rust for the time being. Which probably sounds more dramatic than it is, because we don’t really care about a lot of these issues and we’re probably never even going to be affected by them. But why would we want to spend time and energy on a technology which might change a lot? Well, the quick answer is that we won’t. Take this Serde issue as an example, it’s a very used package. I’m not sure exactly what people use it for, I assume that it’s for things we would likely build in Python, but even with 170 million users it’s main maintainer seems to be an issue. I even agree with the maintainer, but if the Rust ecosystem doesn’t have someone who can fork it and maintain a non-binary version to meet the wants and needs or all these noise makers, well then that’s not exactly a mature ecosystem. So I think the answer is that it’s mostly just noise, but it’s noise you probably don’t want to have to deal with unless you’re invested in Rust itself.
- duped 3y ago> I'd also be curious if anyone has any good explanations as to why a choice like this would be made? It has to do with how Rust builds are structured. The compilation unit is the crate, and the result of compiling it depends on the source code, the version of hte compiler, the compiler options, the target triple, and the set of features (think #ifdef FEATURE_FLAG). Since there's exponential blowup of possible combinations of a single crate, the largest package registry (crates.io) does not cache compiled crates. It only caches their metadata, and when Cargo (the package manager) does dependency resolution, it uses that metadata alone - and your build system (also Cargo, most of the time) will need to actually build all those artifacts. Seems pretty reasonable, right? There's a special kind of crate called a proc_macro crate (procedural macros). When you use these crates you're not adding their code to your compiled output - you're creating a compiler extension - the compiled crate is loaded by the compiler, and then used to generate code in your crate, which is then compiled by the compiler. One of the reasons Rust build times (particularly clean builds) is that they first have to compile all these compiler extensions and then use them to generate the code that the compiler compiles. These crates are super useful however, like serde, which generates serializers/deserializers that can be used for a wide variety of encodings (XML, JSON, TOML, various binary encodings, and so on). It would seem that the same rules apply for normal crates - there's exponential blowup of compiler version, target triple, and features - but that's not really true. proc_macro crates usually don't have features, the compiler ABI is stable enough that they probably won't break across versions, and almost everyone and their mother is compiling on a handful of host systems (x86_64-linux being the most important one). So while it's true that crates-io probably shouldn't cache every crate for every permutation of compiler/target/feature/etc, a few crates are so pervasive and used in such a sparing manner that it should be possible for the registry to host those In fact some build systems can do this automatically. But Cargo can't. But it does have a pre-build feature (build.rs) that works like proc macros, and can pull down a pre-compiled proc macro crate, so widely used ones can use this hack to get around limitations of cargo. My personal take is that the reason that crates-io and Cargo haven't been upgraded to do this, despite years of people complaining, and known solutions existing, is mostly personal/political. There aren't many people willing to put in the work to do it, and the teams haven't been open to contribution of this kind for a long time. For example, they do not believe that Cargo is a build system, and a lot of this is "out of scope" for cargo.
- LispSporks22 3y agoPlenty of projects ship binaries without a problem. Nearly every Linux distribution, for example. What’s special about Rust in that binaries for dependencies are a problem? Also, for the “why” - is it just the very slow build times for Rust?
- monocasa 3y ago> Nearly every Linux distribution, for example. What’s special about Rust in that binaries for dependencies are a problem? The inability to opt out of running a non-reproducible binary library just to build code. This isn't just devs complaining, it's against several large orgs security policies. > Also, for the “why” - is it just the very slow build times for Rust? It's an initial build time thing. It makes no difference except after a full clean or fresh build.
- dlisboa 3y agoCan’t you “vendor” the crate and compile it yourself? Much like cargo does with workspaces, setting the local path to it. I’m still learning the Rust environment but usually vendoring is what you do when you want full control.
- monocasa 3y agoThe binary is in the package you would be vendoring. That's half the problem; the maintainer put binaries in something consumers treat as a source archive.
- dcan 3y agoPlenty of projects ship a) reproducible and b) application binaries. serde-derive is a macro that is used in the compilation of projects. The way the developers have implemented it, when you build a project _from source_, it pulls and executes a binary blob against your code. The binary in serde-derive is not reproducible (at least, there is no published hash and other users haven't been able to compile the same binary on their computers), and saves a very minor amount of time - serde-derive has to be compiled once per project per compiler version, unless you delete your dependencies.
- earthnail 3y agoI dislike some of the framing of the article. In particular: > Valentini further inquired to the project maintainers, how were these new binaries "actually produced," and if it would be possible for him to recreate the binaries, as opposed to consuming precompiled versions. David Tolnay, who is the primary Serde maintainer, responded with potential workarounds at the time. The article links to the response [1] as well, which is very good. However, the response doesn't provide workarounds. It provides clear instructions: >> how is the x86_64-unknown-linux-gnu binary actually produced? Would it be possible for us to re-create the binary ourselves so we can actually ship it? > By https://github.com/serde-rs/serde/blob/v1.0.177/precompiled/build.sh https://github.com/serde-rs/serde/blob/v1.0.177/precompiled/.... Yes. When the article states that the library author only provided "potential workarounds", it sounded really shady to me, and I believe this is mischaracterizing the real response. [1] https://github.com/serde-rs/serde/issues/2538#issuecomment-1654783988 https://github.com/serde-rs/serde/issues/2538#issuecomment-1...
- monocasa 3y agoThe issue is that build.sh doesn't create reproducible binaries. You can run that, but there's no guarantee you'll build the same binary that ships, so you can't automate proof that there hasn't been a supply chain attack.
- earthnail 3y agoThat's fair. Thanks for explaining.
- fnordpiglet 3y agoBut I would assume you would build it in your own hermetic build system and use those artifacts as the reproducible artifacts, since you don’t trust the precompiled distribution. Instead you would start your chain of trust with your build.
- monocasa 3y agoThe whole problem is that binaries are in the regular source distribution unfortunately with no way to turn off their usage.
- 38 3y agoserde is bloated anyway. see for yourself: 146 kB: https://crates.io/crates/serde_json https://crates.io/crates/serde_json 43 kB: https://crates.io/crates/sj https://crates.io/crates/sj 29 kB: https://crates.io/crates/humphrey_json https://crates.io/crates/humphrey_json 7 kB: https://crates.io/crates/rust_json https://crates.io/crates/rust_json
- dralley 3y agoI think "bloated" has to be understood in terms of features provided. The serde ecosystem is far more feature rich than any of those options.
- agubelu 3y agoI don't know why a library has to be necessarily small in size. Having micro packages that only do one tiny thing would only lead to a npm-like hellscape, and Serde is the most complete option in the Rust ecosystem. In my opinion it's better to have simple interfaces for complex features, rather than simple interfaces for simple features. You could code those yourself.
- watusername 3y agoAgreed. I'd even go further and say that apart from humphrey_json, the alternatives GP presented provide complex interfaces for simple features. They provide no easy way to convert to/from a strongly-typed struct, which is the reason for serde_derive.
- 3y ago
- mmastrac 3y agodupe of https://news.ycombinator.com/item?id=37183361 https://news.ycombinator.com/item?id=37183361
- 0xDEF 3y agoWill this be considered a breaking change according to the SemVer conventions?
- CodesInChaos 3y agoThis change happened in a patch release, so the serde author either did not consider it breaking, or doesn't follow semver in the first place. Most crates do not raise their major version when increasing the minimum supported rust version. So there is precedence for treating which changes can break for some people, but which don't have a breaking API changes, as non-breaking for semver purposes.
- __jem 3y agoCorrect, serde does not follow semver.
- Ygg2 3y agoWhat rule of semver does it break.
- __jem 3y agoI'm not sure if he's made an official statement about his versioning policy, but if you look at https://crates.io/users/dtolnay?sort=downloads https://crates.io/users/dtolnay?sort=downloads, you can see that most of his crates follow the incremental patch versioning style.
- dcan 3y agoNo - the functionality of the library is identical between this version and the previous one - just how it's built has changed.
- __jem 3y agoPeople have complained about the build time of proc macros for ages in the community. This might be a misguided hack, but the response to this is bordering on a witch hunt, particularly when there is a glaring security hole (build.rs) that most people likely use without second thought every single day. I simply do not believe that most people commenting on this issue are auditing the builds of all their transitive dependencies.
- mvolfik 3y agoI'm quite ambivalent on this issue overall, but let me just point out: build.rs absolutely is a glaring security hole in the sense you say, but compared to that, this is much worse. You can verify the build.rs code that you download (at least in theory, and some people in banks or distro packages probably actually do), but binaries are orders of magnitude more difficult to inspect, and with the current Rust build system pretty much irreproducible.
- Ygg2 3y ago> build.rs absolutely is a glaring security hole in the sense you say, but compared to that, this is much worse. You can verify the build.rs code that you download In theory you can compile your own blob, but you'll need musl and whatnot to make a universal Linux build. Code for making the blob is there in the repo. build.rs is at best equal. It can access your locally available DB, and transmit your data.
- WirelessGigabit 3y agoThe problem here is that nothing in that build is pinned. It builds with nightly, but doesn't define which version / date. We also don't know how it's build. Ideally there is a Docker container out there that does just an import of source code and then builds. No apk install or apt install (you'd do that in a base published layer). Referenced with an SHA256. We then use this Docker container to pull in the source code AND its dependencies based on a Cargo.lock. Which... isn't there. So we don't know the exact dependencies that went in. (Even if there were a Cargo.lock, we need to make sure we actually respect it. I believe cargo install by default ignores the lock file and tries to get the latest version that matches).
- lars_francke 3y agoThe issue has now been locked and no further conversation can take place on it. This is without a further comment. I found the discussion to be mostly very civilized and focused on finding solutions for those affected. https://github.com/serde-rs/serde/issues/2538 https://github.com/serde-rs/serde/issues/2538 It's all the right of the author. And he's done so before. Unfortunately, I'm afraid this could lead to a more fragmented ecosystem which is a pity. It takes a long time to build trust but it can be destroyed quickly. I'm not sure if the author cares about that though. From my reading of the comments he doesn't. "If there is implementation work needed in some build tools to accommodate it, someone should feel free to do that work (as I have done for Buck and Bazel, which are tools I use and contribute significantly to) or publish your own fork of the source code under a different name." https://github.com/serde-rs/serde/issues/2538#issuecomment-1682519944 https://github.com/serde-rs/serde/issues/2538#issuecomment-1... This is so sad.
- monocasa 3y agoYeah, just like 'people don't leave jobs, they leave managers', 'developers don't fork source, they fork maintainers' feels equally true.
- Ygg2 3y agoTo quote a funny YouTuber: Rust community doesn't report bugs, we complain about in GitHub issues for two years until project owner quits.
- simonw 3y ago> It's all the right of the author. And he's done so before. What do you mean by "he's done so before"?
- lars_francke 3y agoLocked issues with a comment similar to "works for me" I'm not complaining. It's his project, it's his right. He will be overwhelmed by notifications and this is probably the only way he can stay on top of things. I don't have to like it either though.
- jmull 3y agoWhat problem does using precompiled binaries solve for serde?
- CodesInChaos 3y agoIt reduces the build time of a clean build by about 10 seconds. Recompiling after changes to your code is not faster, unless the version of serde the code depends on changes.
- justahuman74 3y ago10 seconds isn't nearly enough of a gain for this level of hack, I'll just wait for a from-source compile
- VWWHFSfQ 3y agoCompile times. It's the same as using a binary distribution or a source-based distribution. Only very few people want to "recompile the world" when they install new software. Which is why mainstream distributions pretty much always just distribute binaries via apt, yum, etc. However, for the people that do need source distribution, it's very important for them. And it's almost always required in high-assurance environments where supply-chain attacks are a real concern. And those people can't rely on the upstream security of crates.io, or this developer's laptop.
- Arnavion 3y agohttps://github.com/serde-rs/serde/pull/2514#issuecomment-1643186527 https://github.com/serde-rs/serde/pull/2514#issuecomment-164...
- mistrial9 3y agochrome-on-linux visit to that bleepingcomputer site, prompted for an xdg-open of an external app?
- photonbeam 3y agoRegardless of the circumstances, non-optional binary dependencies doesn’t pass the smell test
- wallmountedtv 3y agoAlso locking the entire issue from further reactions and comments since "a workaround exists" is a really bad way to handle the PR for this.
- lucasyvas 3y agoAs far as I'm concerned pre-compiled binaries should only be offered as potential default in scripting language ecosystems - there is an expectation that things should "just work". I don't mind it for when I work in Node - honestly I prefer it. But in Rust? The entire toolchain is designed to compile from source. It may be a bit slow for certain crates still, but that's much weaker justification to make pre-compiled the default. Crates should compile from source by default as long as source actually exists (ex. has not been lost somehow, or wide distribution of the crate is unlikely). I don't buy any of the malicious theories since the author is well appreciated and respected. It's likely just a misguided choice in this case. People have rightly mentioned build.rs flexibility - I think there is nuance here and maybe it's good to discuss what default behavior should be expected by users, since sometimes build.rs does wild and useful things.
- andrewflnr 3y agoOn the argument of "you're not reading build.rs anyway": yes, but you can, and that's a critical difference. "No one" is validating all their transitive dependencies for weird binaries, except a few people, and that's enough to get us all here talking about it. In the internet age it only takes one. But if you make it require more work to inspect a blob, you reduce the chances that you'll get even that one person who sounds the alarm. At minimum malicious code will survive longer that way.
- pshc 3y agoCould be neat if serde (and other libraries with proc macros) were able to ship WebAssembly blobs. Wasm blobs are portable and run with restricted capabilities. Rustc could expose an embedded API to allow the wasm to generate code and expand macros without hitting the network.
- tamimio 3y agoThe sketchy part is it was changed and shipped silently and in a sneaky way, and if it wasn’t for some developer to notice it, he wouldn’t have mentioned it.. However, no need to pushback or anything, consider the maintainer got hit by a bus, fork it, and take it from there. Also, the “security” guy claiming no one is going to read it, do you read linux kernel every update? No, not you, but other developers do, or whenever someone is interested/auditing they can, that kind of arguments are stupid.
- mwcampbell 3y agoWe must, must, must stop assuming the worst in others and jumping to react accordingly. Please, please don't fork serde. I'm sure this situation can be worked out without resorting to such measures.
- evilduck 3y agoI’m just part of the peanut gallery, but how can you work something out when the maintainer can and has already just locked the issue and walked away? I don’t see a problem with the community forking as motivation to get the maintainer to come back the conversation or to provide a path for the community to resolve the issue for themselves. A fork was literally one of dtolnay’s own suggestions, bluff or not.
- bacon_waffle 3y ago> I’m just part of the peanut gallery Me too. As a Rust user and small-scale evangelist, I find this whole thing a bit interesting and also somewhat sad. At least, it seems like increased awareness, and perhaps sandboxing, of proc macros are good outcomes that may result. > how can you work something out when the maintainer can and has already just locked the issue and walked away? That's not what happened here. dtolnay has not walked away, and the discussion is progressing. For instance, this pre-RFC [0]. > I don’t see a problem with the community forking Serde, to the limited extent that I understand it, works because it is a single shared interface - multiple forks would be counter to the purpose of the project. That said, here we're talking about serde-derive specifically, so it's not quite a fork of the serde interface that we're talking about. But, I can understand why people might be upset that such an important a piece of the Rust ecosystem is such a flashpoint. [0] https://internals.rust-lang.org/t/pre-rfc-sandboxed-deterministic-reproducible-efficient-wasm-compilation-of-proc-macros/19359 https://internals.rust-lang.org/t/pre-rfc-sandboxed-determin...
- SnowProblem 3y agoI don't understand why this is so complex? Just make it a feature you put in Cargo.toml: serde = { version = "1.0", features = ["serde_derive", "fast_compile"] } Either opt-in or opt-out is fine. It's surely useful during development but it shouldn't be on for production builds. Any transitivity issues should be considered problems with the dependency.
- duped 3y agoThis doesn't fix the problem, since features don't apply transitively. The correct workaround is to use [patch]. There's also the problem that the build may fail because it will still download the binary, which you may want to forbid entirely.
- SnowProblem 3y agoSure features are transitive. A crate’s features at build are the superset of what every package defines. If I define “fast_compile” (opt-in) or “no_binaries” (opt-out) it’ll get applied across the board, will it not?
- duped 3y agoIt depends. Features are not unified with resolver = 2 for proc macros. https://doc.rust-lang.org/cargo/reference/resolver.html#features https://doc.rust-lang.org/cargo/reference/resolver.html#feat... That said I'm not sure how that applies transitively, I could have sworn I've seen conflicting features not cause transitive build failures.
- lamontcg 3y agoRubygems have been doing this for about a decade now to provide precompiled binaries for Windows systems in particular (generally while compiling native extensions on Linux at install time, although sometimes shipping precompiled binaries for most every target for speed + decoupling the build environment).
- leoh 3y agoJava ships everything with JARs, though? Binary.
- xena 3y agoIt's also fairly trivial to go from Java byte code to Java source code. This same property doesn't exist with amd64 and Rust.
- Nullabillity 3y agoYes. We should learn from that mistake.
- frankjr 3y agoUpdate: Removed in v1.0.184 (https://github.com/serde-rs/serde/releases/tag/v1.0.184 https://github.com/serde-rs/serde/releases/tag/v1.0.184)