<re_irc>
<newam> > However: Comments are not a concurrency strategy.
<re_irc>
> Relying on the programmer to always read, comprehend, and remember the documentation – and then do everything right, every time – is how we get bugs.
<re_irc>
I always feel called out on these. This is most of embedded - embedded rust is better, but it is still very easy to get yourself in trouble when it comes to clocking/resets/power
<re_irc>
<firefrommoonlight> Comments are one part of defense-in-depth
explore has quit [Quit: Connection closed for inactivity]
<bjc>
my hot/bad take is that it's a category error to try and generalize the way you program with std rust to how nostd rust works
<bjc>
the model is very different when you do things like mmio when interrupts are involved, and what you have to do to protect yourself -- std rust won't help you here
<bjc>
and there are so many more rough edges in nostd
<bjc>
i find, personally, that programming in std rust is fairly straightforward and when the compiler complains it makes sense. but in nostd i'm actively fighting the compiler a large chunk of the time because it's expectations of the world can't be met in this environment
<re_irc>
<ryan-summers> There's a lot of things where no-std kind of redefines std-rust concepts to aid in compile-time checking of logic, e.g. using Sync/Send to indicate whether a resource is sharable in multiple contexts
<re_irc>
<ryan-summers> But you can still make very nice, safe abstractions in no-std land as well
<re_irc>
<ryan-summers> It's just a tad different
<bjc>
you can, but it's a struggle a lot of the time
<re_irc>
<ryan-summers> Embedded in general is hard, leveraging the compiler to make less mistakes is always a good idea
<re_irc>
<ryan-summers> But yeah, it's undoubtedly harder and more time consuming to make good no-std abstractions
<bjc>
the biggest issue is ensuring safety. rust isn't much better than c once you invoke `unsafe`, and you have to do that a lot
<re_irc>
<ryan-summers> Depends on what you're doing. If you use something like RTIC you save yourself from most of the unsafe-ness
<re_irc>
<ryan-summers> A lot of my apps nowadays only use unsafe to conjure register addresses for fault handling etc.
<re_irc>
<ryan-summers> But if you compare it to C, literally _everything_ in C is using the "unsafe" keyword. Anytime you're _not_ using it is already a win
<bjc>
everything in c is unsafe, but you also don't have to spend a ton of time trying to do the thing you want to do that you know should work
<bjc>
it's a trade-off. in std rust it's pretty clear that the compiler is a pretty un-alloyed good
<re_irc>
<ryan-summers> Having done many years in both languages, doing it in C just lets you do the thing without thinking about why it's valid imo. You get better with Rust over time
<re_irc>
<ryan-summers> +and can be more productive in it without fighting the compiler as much with practice
<bjc>
in nostd it's not always so clear, specifically because you end up writing so much unsafe stuff, that i'm not sure it's always a win
<re_irc>
<ryan-summers> Experientially, the firmware I've written in C (albeit generally more complex) has very often had many more bugs than Rust firmware I've done
<re_irc>
<ryan-summers> Like I mentioned previously, many of my apps actually sparingly use unsafe
<bjc>
looking through my latest project, i only really use it for mmio and assembler
<bjc>
maybe my complaint is more with over-optimizing compilers more than anything. it's certainly caused issues in c/c++ too
<bjc>
i feel like i can never guess what the compiler might do to my code, and that's all well and good as long as you're never unsafe, because it's a bug if the compiler optimizations break it. but as soon as you use unsafe all bets are off
<bjc>
and rust just seems more aggressive than c
<bjc>
regardless, it definitely feels like rust, as a language, is made for std environments, and while it's nice that it has nostd, it's a second class citizen
<Shell>
I'd suggest it's a problem with languages that try to abstract over many different machines, since by definition they define an abstract machine that does not map cleanly to what you're writing against.
<bjc>
you may well be right
<XMPPwocky>
yeah, this is the reason people used to call C a high level language- and the reason the good parts of C inevitably died
<XMPPwocky>
GCC and clang/llvm are also aggressive around optimizing C code- but frankly attempting to write correct C is so hopeless everybody has learned helplessness over it
<re_irc>
<K900> I think the important distinction here isn't std vs no_std
<re_irc>
<K900> It's safe vs unsafe
<re_irc>
<K900> And more specifically actually being certain about what is or isn't allowed in unsafe contexts
<bjc>
it's, currently, almost impossible to write non-trivial nostd code without unsafe, imho
<bjc>
so nostd is relevant
<re_irc>
<K900> Which currently mostly comes down to an intuition of "whatever looks sufficiently like C that LLVM won't horribly mangle"
<re_irc>
<K900> But people _are_ working on that
<bjc>
that's why i said currently
<XMPPwocky>
it's HAL vs no-HAL in some sense
<re_irc>
<K900> cf miri and Gankra's recent work on provenance
<bjc>
and that matters to me for the projects i'm currently working on
<re_irc>
<K900> bjc: It is absolutely possible as long as you allow unsafe in dependencies
<bjc>
i tried miri a bit ago and its broken in nightly. i think it has been for a while
<re_irc>
<K900> And not allowing any unsafe anywhere at all is not really achievable, on embedded or otherwise
<bjc>
i try not to use too many dependencies. it's a personal preference, maybe, but i find rust's crates tend to pull in a *lot* of stuff. it feels nodejs-y
<re_irc>
<K900> It feels less like C, that's for sure
<re_irc>
<K900> But that is what usually happens when you have good tooling
<XMPPwocky>
re_irc: sure it is, you just push the unsafe / correctness a critical stuff somewhere else. Hiding it in a compiler or runtime is the classic choice
<re_irc>
<K900> Like, the only reason you're not seeing the same situation on the C side is that it's _absolutely fucking impossible_ to install C libraries reliably and I'm writing this from a NixOS system
<XMPPwocky>
you can go full Lisp Machine if you want and hide it in CPU microcode, too
<re_irc>
<K900> And NixOS is a thing that only exists out of spite for C
<bjc>
it is much harder to have super-deep dependency trees in c, sure, but that doesn't mean they're a good idea just because they're possible elsewhere
<re_irc>
<K900> Well, primarily, at least
<re_irc>
<K900> bjc: It's not about it being a good idea or not, it's about the social dynamics
<re_irc>
<K900> People don't like repeating work
<XMPPwocky>
bjc: that doesn't mean they're not a good idea, either.
<XMPPwocky>
that means yoj need Good Tools To Think About Trust
<re_irc>
<K900> The only reason they do in C is, again, because repeating work is _less painful than not repeating_
<bjc>
my preference is to repeat some amount of work over installing things that install a thousand things
<re_irc>
<K900> I've yet to see a Rust crate that installs anywhere close to a thousand of anything
<bjc>
i'm ever reminded of leftpad, recently repeated on purpose to protest the war in ukraine
<XMPPwocky>
re_irc: I have, but not a thousand *developers*
<XMPPwocky>
a thousand *crates* in some cursed things
<bjc>
pulling in a hal crate pulls in at least a dozen other things, i'd say
<re_irc>
<K900> A dozen is not a thousand
<XMPPwocky>
but you don't trust a crate, you have to trust a person
<bjc>
grant me some hyperbole. it feels like a lot
<re_irc>
<K900> And most of that dozen would just be in the HAL in C-land
<re_irc>
<K900> Or more realistically in the vendor toolchain
<bjc>
yeah, that's less fragile to me
<re_irc>
<K900> That's built from an Eclipse from 2005 running a GCC from 1993
<re_irc>
<K900> And you have no idea who touched any of _those_ things either
<XMPPwocky>
the problem about having tools to think about who you trust is that thinking about who you trust freaks people out
<re_irc>
<K900> I guess you can treat the vendor as a source of trust, but the vendor does not know either
<XMPPwocky>
*gestures in the direction of ken Thompson's Reflections*
<bjc>
trust isn't binary, and it's easier to trust a few sources than a lot of sources
<XMPPwocky>
"wat, I can't just automatically trust the vendor's binaries"
<re_irc>
<K900> In that case you can easily extend this back to the HALs
<XMPPwocky>
bjc: yes, thus proper threat modelling - over *humans*, not crates
<re_irc>
<K900> If you trust everything in the vendor toolchain because you trust the vendor, why not trust everything in the HAL because you trust the maintainers of the HAL?
<re_irc>
<K900> Also, notably, Cargo enforces hashes in lockfiles, and crates.io does not ever delete things
<re_irc>
<K900> So a left-pad "incident" is not actually possible
<bjc>
because the maintainers of the hal didn't write all that stuff, and they may have vetted the immediate dependencies (although probably not to the same degree as their own writing) but they didn't vet the dependencies of those dependencies, or the dependencies of *those*
<re_irc>
<K900> You think your vendor vetted GCC?
<re_irc>
<K900> (spoiler: no, they didn't, and they probably _can't_)
<bjc>
the leftpad incident wasn't enlightening to me because everyone's stuff broke, it was because such a *simple* thing was so critical
<bjc>
systems are more robust with redundancy and heterogeny. that's a fact of life. the question is where the trade-off is for you
<re_irc>
<K900> Like, I would accept this logic if your vendor wrote 100% custom code that they presumably trust
<re_irc>
<K900> But they don't
<re_irc>
<K900> No one does
<bjc>
i tend to err much more on the side of redundancy than hereogeny than your average js developer, i'd wager
<re_irc>
<K900> Cargo gives you redundancy though?
<re_irc>
<K900> Like, literally, it's impossible to delete anything from crates.io
<re_irc>
<K900> And you can run your own mirror
<bjc>
it's not redundant - it's a copy of someone else's code
<re_irc>
<K900> Then what do you mean by "redundant"?
<bjc>
redundancy is literally other code doing the similar things
<XMPPwocky>
bjc: i mean imagine if glibc removed, say, atoi
<XMPPwocky>
"but they'd never do that, what"
<XMPPwocky>
exactly!
<bjc>
yeah, it's scary
<bjc>
but the issue there is whether you can come reasonably close to matching their atoi in the time you have to make it worth doing it yourself
<bjc>
leftpad is an easy choice: that's trivially easy to write for most people, i'd wager, so it's elucidating
<bjc>
atoi is trickier
<XMPPwocky>
yeah, thus the Batteries Included python-esque approach
<XMPPwocky>
you already trust the language devs for stuff, so they can add extra basic functionality without it being too scary
<re_irc>
<K900> Maybe I should keep count of how many broken left-pads I've found over all my experience
<re_irc>
<K900> (it's more than ten, at least)
<XMPPwocky>
in security terms, the language and stdlib is already Part Of Your Trusted Computing Base
<bjc>
there's a certain amount of mandatory trust for almost every project, and the developers of your programming language probably fall into that circle
<bjc>
there is the l3 kernel if you want to go hard into minimal trust computing, i guess, but then you have to trust that you can understand the proofs ;)
<XMPPwocky>
exactly exactly
<XMPPwocky>
okay, so there's this Trusted Computing Group (lmao) thing called the DRTM
<XMPPwocky>
normal Secure Boot is called (by TCG dorks) the SRTM - you start from a powered-off system, then the CPU boot ROM or whatever measures/verifies your bootloader, bootloader measures/verifies your kernel, kernel measures/verifies your initrd, etc etc etc
<XMPPwocky>
this is basically how most ARM SOCs think of secure boot stuff, right?
<XMPPwocky>
e.g. NXP HAB - boot ROM checks your uboot, uboot (in theory, it's uboot...) checks your kernel, etc...
<XMPPwocky>
but why do you have to trust u-boot? like, the kernel+initrd are already signed, by definition, or else secure boot is hopeless. why can't the CPU just verify those signatures, regardless of what u-boot does?
<XMPPwocky>
and it can- it just adds complexity to the process, and you have to think about a bunch of weird attacks (e.g. an evil u-boot setting up a DMA engine to DMA over your kernel code in a few seconds, before booting it)
<XMPPwocky>
now, you need to trust the boot ROM / CPU microcode here to actually measure the kernel, and to correctly mitigate those weird attacks, and frankly given SOC vendors' general code quality (not to mention Intel/AMD's!)...
<XMPPwocky>
plus, what if they're backdoored? it's paranoid but not outside the realm of possibility, so for some threat models...
<bjc>
isn't u-boot used because the cpu is too dumb to do anything but jump to a known address on boot?
<re_irc>
<K900> Modern CPUs are way smarter than that
<bjc>
how does the cpu know where the bios ends in order to check its signature?
<XMPPwocky>
bjc: on intel platforms, using what's basically a header in the SPI flash called the FIT
<XMPPwocky>
on AMD it's probably a similar idea; on most random ARM SOCs (e.g. i.MX) it's similar
<XMPPwocky>
ofc, this header also has to be signed :)
<bjc>
natch
<XMPPwocky>
the escape from this trap of "oh hm but i don't want to trust Intel/AMD/NXP" is to realize that you are *already* trusting the boot ROM/microcode not to be *sneakily malicious*, because it comes from the same place as your CPU
<XMPPwocky>
and because, uh, who verified u-boot in the first place?
<bjc>
its crazy to me that the cpu has enough junk in it now to directly talk to spi, let alone verify bios signatures
<bjc>
actually, it's probably a microcontroller or something embedded in the cpu, isn't it
<XMPPwocky>
fun fact- on intel platforms, that's not actually in the CPU proper, that's on the chipset
<XMPPwocky>
and the chipset is just memory-mapped
<bjc>
right
<XMPPwocky>
over a weird protocol, DMI that's sorta Spicy PCIe
<XMPPwocky>
AMD's solution is actually really cute here, because while Intel has you load a magic Intel-signed opaque blob (like microcode blobs) called an ACM
<XMPPwocky>
to verify your kernel image after u-boot has loaded it
<XMPPwocky>
AMD lets you just load... whatever, signed by any key you like... but it measures the signing key into the TPM, too
<XMPPwocky>
i.e. your policy for Verified Boot sorta explicitly says "okay, continue booting iff the Magic Blob The CPU Verified was signed by this key, and then the Magic Blob said the kernel image was signed by *this* key or whatever..."
<re_irc>
<K900> AMD also runs that thing on an old ARM7 core
<re_irc>
<K900> Which is pretty neat
<XMPPwocky>
re_irc: y'know they run linux on there for a vnc server
<XMPPwocky>
this runs on the PSP for remote management, i don't uhh
<XMPPwocky>
i don't know what amd is trying to do with that one
<XMPPwocky>
they call it 'KVMOS', as in "keyboard, video, mouse"
<XMPPwocky>
aaanyways
<re_irc>
<K900> It's basically an on-die BMC for modern machines
<re_irc>
<K900> As in anything Zen
<XMPPwocky>
don't trust remote management/BMCs for anything truly critical unless you're a big enough corporation to scare your BMC vendor
<XMPPwocky>
but do (not blindly! carefully!) trust that CPUs aren't actively malicious, because otherwise *you should not be using CPUs for anything critical*
<XMPPwocky>
and *that*'s why I can trust e.g. the `rand` crate regardless of whether its in the stdlib or not
<XMPPwocky>
no_std/std is not the distinction, it's "is this something *I already have to trust*, because if not I have to actually think about how I'm expanding my trust
<XMPPwocky>
thus the general idea of https://github.com/rust-lang-nursery - things there 1. should not magically disappear like left-pad because one developer got mad and 2. are at least commonly-used enough that any actively malicious/backdoor code will hopefully be spotted
<XMPPwocky>
if leftpad was a crate under there i wouldn't bat an eye
<XMPPwocky>
if leftpad was a crate from some anonymous single developer, otoh...