From nobody Fri Sep 06 04:02:57 2024 X-Original-To: freebsd-hackers@mlmmj.nyi.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2610:1c1:1:606c::19:1]) by mlmmj.nyi.freebsd.org (Postfix) with ESMTP id 4X0Myl6gxMz5VW3J for ; Fri, 06 Sep 2024 04:03:11 +0000 (UTC) (envelope-from kostikbel@gmail.com) Received: from kib.kiev.ua (kib.kiev.ua [IPv6:2001:470:d5e7:1::1]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (Client did not present a certificate) by mx1.freebsd.org (Postfix) with ESMTPS id 4X0Myl2ZGCz54lK; Fri, 6 Sep 2024 04:03:11 +0000 (UTC) (envelope-from kostikbel@gmail.com) Authentication-Results: mx1.freebsd.org; none Received: from tom.home (kib@localhost [127.0.0.1] (may be forged)) by kib.kiev.ua (8.18.1/8.18.1) with ESMTP id 48642vCE083281; Fri, 6 Sep 2024 07:03:00 +0300 (EEST) (envelope-from kostikbel@gmail.com) DKIM-Filter: OpenDKIM Filter v2.10.3 kib.kiev.ua 48642vCE083281 Received: (from kostik@localhost) by tom.home (8.18.1/8.18.1/Submit) id 48642vPn083280; Fri, 6 Sep 2024 07:02:57 +0300 (EEST) (envelope-from kostikbel@gmail.com) X-Authentication-Warning: tom.home: kostik set sender to kostikbel@gmail.com using -f Date: Fri, 6 Sep 2024 07:02:57 +0300 From: Konstantin Belousov To: Alan Somers Cc: ske-89@pkmab.se, freebsd-hackers@freebsd.org Subject: Re: The Case for Rust (in any system) Message-ID: References: <202409052313.aa18097@berenice.pkmab.se> List-Id: Technical discussions relating to FreeBSD List-Archive: https://lists.freebsd.org/archives/freebsd-hackers List-Help: List-Post: List-Subscribe: List-Unsubscribe: Sender: owner-freebsd-hackers@FreeBSD.org MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Disposition: inline Content-Transfer-Encoding: 8bit In-Reply-To: X-Spam-Status: No, score=-1.0 required=5.0 tests=ALL_TRUSTED,BAYES_00, DKIM_ADSP_CUSTOM_MED,FORGED_GMAIL_RCVD,FREEMAIL_FROM, NML_ADSP_CUSTOM_MED autolearn=no autolearn_force=no version=4.0.1 X-Spam-Checker-Version: SpamAssassin 4.0.1 (2024-03-26) on tom.home X-Spamd-Bar: ---- X-Rspamd-Pre-Result: action=no action; module=replies; Message is reply to one we originated X-Spamd-Result: default: False [-4.00 / 15.00]; REPLY(-4.00)[]; ASN(0.00)[asn:6939, ipnet:2001:470::/32, country:US] X-Rspamd-Queue-Id: 4X0Myl2ZGCz54lK On Thu, Sep 05, 2024 at 04:37:27PM -0600, Alan Somers wrote: > On Thu, Sep 5, 2024 at 3:13 PM wrote: > > > > Alan Somers wrote: > > > In fact, of all the C bug fixes that I've been involved with (as > > > either author or reviewer) since May, about three quarters could've > > > been avoided just by using a better language. > > ... > > > To summarize, here's the list of this week's security advisories, and > > > also some other recent C bug fixes of my own involvement: > > > > After checking several of these examples, I'm wondering what the code > > would have looked like in some "better language", where those bugs would > > have been avoided? > > > > E.g for the "use after free" or "unitialized memory" examples. > > > > To me, several of those bugs seem fairly complex, and not just a > > question of having bounds checking for arrays or a borrow checker > > for pointers, or something simple like that. > > > > But maybe the bugs could have been detected and prevented if the > > code would have been forced to be expressed in a completely > > different manner by some other language? Or what is your vision > > of how that would be accomplished? > > > > You seem to be saying that certain examples would be solved by > > a better language, and certain ones would not, so I suppose you > > do have some vision of how that would work. > > > > I'm just curious to learn more, since it is not obvious to me, > > and thus all the more interresting. > > > > /Kristoffer Eriksson > > Excellent question. Here's why a selected sample of those bugs > would've been prevented had the programs been written in Rust. > > 2909ddd17cb4d750852dc04128e584f93f8c5058 > Rust uses RAII wherever possible. Variables are automatically deallocated when > they leave scope. Circular references are almost impossible to create due to > the lifetime borrow checker. So bugs like this really just can't happen in > idiomatic Rust code. This can be expressed in different way. Rust makes it too hard to operate on structures that are richer than acyclic directed graphs. I am very interested in proposals of a reasonable Rust bindings for our VFS interfaces. > > CVE-2024-45063 > Written in idiomatic Rust, the lun->write_buffer would've had a type like > Option>>. The only way to free that would be to remove the > contents of the Option, leaving None in its place. So a subsequent > use-after-free would be impossible. The bug would still be present, but > instead of a use-after-free the READ BUFFER command would have to create and > zero-initialize a new buffer. The bug would be immediately obvious to the user > since READ BUFFER would return the wrong data (all zeroes). > > CVE-2024-8178 > Rust abhors uninitialized data. LLVM doesn't even guarantee that a program > will run correctly when accessing it. So written in idiomatic Rust, > lun->write_buffer would either be zero initialized, or it would be allocated > like `Vec::with_capacity(262144)`. In the latter case, it would be partially > initialized during the WRITE BUFFER command. But given the semantics of SCSI's > READ BUFFER and WRITE BUFFER commands, I think zero-initialization is more > appropriate. > > CVE-2024-6119 > In Rust, initializing a union via one member and then accessing it via another > is actually considered to be the same thing as reading uninitialized memory, > and LLVM abhors it. The idiomatic solution is to use a enum (which is similar > to a Java enum) instead of a union. The enum is basically a tagged union, so > the programs knows at runtime which member is initialized. That makes bugs > like CVE-2024-6119 impossible in idiomatic Rust code. > > CVE-2024-41928 > This bug involved zero-initialing a structure, but with the wrong size. > Idiomatic Rust code never uses anything like bzero. In fact, zero-initializing > a structure is considered unsafe, because an all-zero pattern isn't valid for > all structures. To initialize a structure in Rust, you either need to provide > the value of every member or else use an initializer function. The simplest > intializer is often STRUCT_NAME::default(), which can be automatically derived > and is often equivalent to bzero. But all of those methods know the size of > the structure, so bugs like this aren't possible in idiomatic Rust code. > > CVE-2024-45287 > In debug builds of Rust, integer math operations are by default bounds-checked > at runtime. That catches many bugs like this. For release builds, integer > math operations are wrapping by default, but the programmer can also select > bounds-checking. In this particular case, however, a Rust programmer wouldn't > have attempted to multiply those two integers together. Instead, `value` > would've been a Vec of some type, and it would be initialized like > `Vec::with_capacity(nvp->nvp_nitems)`. Note that Rust would only help there if code actually execute a case which makes the operation overflow. So until somebody notes it, the bug is there, in the form of panic in debug build, or 'whatever' in the release. > > 1f5bf91a85e93afa17bc9c03fe7fade0852da046 > Rust's borrow checker will ensure that a single variable cannot be modified > from two locations at the same time, or modified in one and read from another. > This check happens at compile-time, with 0 runtime cost. For cases whether the > compiler cannot determine whether the access is safe, various runtime options > are available, like Mutex. In this case, the function's author actually > performed a cast to remove "const" from the variable. Rust makes such casts > harder, and it's better type system makes them far less necessary. > > 35f4984343229545881a324a00cdbb3980d675ce and > eced2e2f1e56b54753702da52a88fccbe73b3dcb > In idiomatic Rust, a falliable function returns a `Result` type, and the > compiler is smart enough to know when a programmer ignores the Result. It will > generate a warning, and most projects are configured to treat that type of > warning as an error. So bugs like this don't usually happen. They can, > however. A programmer can deliberately ignore the error, as in eced2e2f1e5 , > or he can "unwrap" it. That means "panic on error", which is not terribly > different from the bug fixed by 35f49843432. So it's possible but far from > certain that a Rust implementation would've prevented these bugs. That's why > in my summary I said "about three quarters" could've been avoided. >