From nobody Thu Sep 5 22:37:27 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 4X0Dl86Q3gz5V9md for ; Thu, 05 Sep 2024 22:37:40 +0000 (UTC) (envelope-from asomers@gmail.com) Received: from mail-vs1-f52.google.com (mail-vs1-f52.google.com [209.85.217.52]) (using TLSv1.3 with cipher TLS_AES_128_GCM_SHA256 (128/128 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256 client-signature RSA-PSS (2048 bits) client-digest SHA256) (Client CN "smtp.gmail.com", Issuer "WR4" (verified OK)) by mx1.freebsd.org (Postfix) with ESMTPS id 4X0Dl84kGJz4DLv for ; Thu, 5 Sep 2024 22:37:40 +0000 (UTC) (envelope-from asomers@gmail.com) Authentication-Results: mx1.freebsd.org; none Received: by mail-vs1-f52.google.com with SMTP id ada2fe7eead31-49bd3bf3b4cso429598137.3 for ; Thu, 05 Sep 2024 15:37:40 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1725575860; x=1726180660; h=content-transfer-encoding:cc:to:subject:message-id:date:from :in-reply-to:references:mime-version:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=QgWmZ2wcG3svf7DvwM1EAJv0CaUPt1ZxAk3zPWU7/4g=; b=c3cvFgJCiT1TXvaTAaVJEgK+9+qCN0sPcuH7d32JuyXny4l4sw12gdS/U48KPM+nTK SBFXdV2LyHCjk0S7CeOPo1mbn7A6YjH9lcGvnfadTvf177FPloieSaCtq0earVRbqr+J AxbR0R+/34xs3nWOs5fwMD4H0/zoa4pBXRlv2w8ZA3qgv2XoYcsFogFQFTLALCKUrt24 YPxKcwBiFp1FW07mVjNrOSlzd406/hvD4cK/6/C05cGjn5sH0uD/SW8FW8q2ENo1g2Br /ob9lu6rvH6lPkq5cQnd+1tHq02Yfs7vUL/sPuJ3EsjtF4AEp8zkF3prJ5I/OIfGkZFS fgLg== X-Gm-Message-State: AOJu0YxcjjQ/J+ig9gVTXCVUTZBewit8lJOTRwqeWxSrs0V5TvDa2R94 w+GW8TyjHTMX2RzicrZFfXtQmuPONsmjifA5fNitzfTI8AaejRXsPgB91E3uXSP5abZrUmRdlRh ujuoiLVXE9dRqAFKlHa1Nm3MdZsw= X-Google-Smtp-Source: AGHT+IF2cUktS3jXFDtEFLMPzx/QlwV69moBZyaqAlm6B9OxSUph8idJ15GgZ3i4XL9duHHkEYiatUdDx7ufJy4H8g4= X-Received: by 2002:a05:6102:50a0:b0:497:6a9b:36bd with SMTP id ada2fe7eead31-49a5af816a6mr30929409137.29.1725575859515; Thu, 05 Sep 2024 15:37:39 -0700 (PDT) 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 References: <202409052313.aa18097@berenice.pkmab.se> In-Reply-To: <202409052313.aa18097@berenice.pkmab.se> From: Alan Somers Date: Thu, 5 Sep 2024 16:37:27 -0600 Message-ID: Subject: Re: The Case for Rust (in any system) To: ske-89@pkmab.se Cc: freebsd-hackers@freebsd.org Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: quoted-printable 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:15169, ipnet:209.85.128.0/17, country:US] X-Rspamd-Queue-Id: 4X0Dl84kGJz4DLv On Thu, Sep 5, 2024 at 3:13=E2=80=AFPM 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. 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 an= d 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 allocate= d like `Vec::with_capacity(262144)`. In the latter case, it would be partial= ly initialized during the WRITE BUFFER command. But given the semantics of SC= SI'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 anot= her 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 simi= lar 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-initiali= zing a structure is considered unsafe, because an all-zero pattern isn't valid f= or all structures. To initialize a structure in Rust, you either need to prov= ide the value of every member or else use an initializer function. The simples= t intializer is often STRUCT_NAME::default(), which can be automatically deri= ved and is often equivalent to bzero. But all of those methods know the size o= f 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-chec= ked 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 would= n'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)`. 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 anoth= er. This check happens at compile-time, with 0 runtime cost. For cases whether= the compiler cannot determine whether the access is safe, various runtime optio= ns are available, like Mutex. In this case, the function's author actually performed a cast to remove "const" from the variable. Rust makes such cast= s 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 w= hy in my summary I said "about three quarters" could've been avoided.