ChanServ changed the topic of #rust-embedded to: Welcome to the Rust Embedded IRC channel! Bridged to #rust-embedded:matrix.org and logged at https://libera.irclog.whitequark.org/rust-embedded, code of conduct at https://www.rust-lang.org/conduct.html
loki_val has joined #rust-embedded
crabbedhaloablut has quit [Ping timeout: 268 seconds]
fabic has joined #rust-embedded
starblue has quit [Ping timeout: 255 seconds]
starblue has joined #rust-embedded
<re_irc> <GrantM11235> henrikssn: I have looked in to this before,and based on my understanding, using "read_volatile" and "write_volatile" like this is technically UB
<re_irc> > In particular, a race between a read_volatile and any write operation to the same location is undefined behavior.
cr1901_ is now known as cr1901
<re_irc> <GrantM11235> The only way that I can think of to get around that is to use asm
<re_irc> <Lachlan> I think you can absolutely use read_volatile/write_volatile here
<re_irc> <GrantM11235> I think it would probably work fine in this case, but it is still technically UB
<re_irc> <Lachlan> Why would it be ub?
<re_irc> <GrantM11235> Because a dma operation could race with the read, which the docs say is UB
<re_irc> <Lachlan> Ah
<re_irc> <thalesfragoso> GrantM11235: Well, you just don't touch the buffer until the DMA is done
<re_irc> <thalesfragoso> And being sure to use fences to avoid having to read_volatile the entire buffer, which is sad
<re_irc> <GrantM11235> thalesfragoso: Ah, I didn't realize that that was an option. In that case it is fine
<re_irc> <GrantM11235> When I was looking in to it for my use case, it was to read data from a buffer while a circular dma was writing to it continously
<re_irc> <thalesfragoso> It's all very finicky though and has to be done with care
<re_irc> <thalesfragoso> GrantM11235: You can still split that borrow or use an array with two buffers. But it's still hard to ensure that the user is faster than the DMA
<re_irc> <thalesfragoso> You can panic if you detect a overrun, it's still technically UB if the overrun happens though
<re_irc> <thalesfragoso> But I don't see how the compiler could exploit that UB in any meaningful way, specially if you panic right away
<re_irc> <thalesfragoso> But yeah, stick to double buffering DMA if you can, it's more flexible than circular and easy to work with
<re_irc> <thalesfragoso> * easier
<re_irc> <Lachlan> Yeah, this is runtime ub which is much better than compile time ub
<re_irc> <thalesfragoso> Don't quote me on that though, I don't recommend doing it :)
<re_irc> <GrantM11235> Yeah I really don't like the idea of trying to detect that something went wrong _after_ I hit UB
<re_irc> <GrantM11235> Even though in my case, I don't think that the Abstract Machine :tm: would have any way of knowing that a "read_volatile" raced with a dma write
<re_irc> <9names (@9names:matrix.org)> I wonder if the wording there is intensionally incorrect to forcefully dissuade folks from trying to sync cores via volatile accesses
<re_irc> <GrantM11235> It is definitely possible in general to get some juicy UB if your "read_volatile" races with a write
<re_irc> <GrantM11235> For example if you have a NonZeroU64 that you try to read while something else is changing it from "0xFFFFFFFF00000000" to "0x00000000FFFFFFFF", you could read a zero, which would be bad
<re_irc> <Lachlan> Yeah, you’d want to make sure you don’t read larger than the cache size I guess
<re_irc> <Lachlan> +line
<re_irc> <GrantM11235> Even if it was just a u8, I don't think there is any guarantee that "x = 0x0F;" is atomic
crabbedhaloablut has joined #rust-embedded
loki_val has quit [Ping timeout: 268 seconds]
crabbedhaloablut has quit [Remote host closed the connection]
crabbedhaloablut has joined #rust-embedded
emerent has quit [Ping timeout: 255 seconds]
emerent has joined #rust-embedded
bpye has quit [Quit: Ping timeout (120 seconds)]
bpye7 has joined #rust-embedded
<re_irc> <henrikssn> GrantM11235: You can configure the DMA write unit to what you want (so that this case cannot happen). I guess this still makes it UB though
<re_irc> <henrikssn> thalesfragoso: I don't think I can handle the runtime and memory overhead of having two buffers unfortunately
<re_irc> <henrikssn> The buffer contains zerocopy types so that I can update them in place without a memcpy
<re_irc> <henrikssn> It's all really low level but I hoped there were a way to stay in rust (vs doing it in asm) where the compiler isn't gonna screw me over
<re_irc> <GrantM11235> You don't need very much asm, just a "super_volatile_read" function that can read a "u32" or whatever. Everything else can be in rust
fabic has quit [Ping timeout: 246 seconds]
<re_irc> <henrikssn> Btw, doesn't this also affect soundness of embedded-dma?
<re_irc> <henrikssn> I.e. that is UB
<re_irc> fn transfer_dma<BUF: ReadBuffer>(buf: BUF) -> BUF {
<re_irc> <henrikssn> My understanding is that typical use case looks something like this:
<re_irc> let (ptr, len) = buf.read_buffer();
<re_irc> start_dma();
<re_irc> <henrikssn> My understanding is that typical use case looks something like this:
<re_irc> let (ptr, len) = buf.read_buffer();
<re_irc> fn transfer_dma<BUF: ReadBuffer>(buf: BUF) -> BUF {
<re_irc> start_dma();
<re_irc> <henrikssn> the compiler is allowed to insert a spurious read from "buf" between "start_dma" and "wait_for_dma", which makes this UB
<re_irc> <henrikssn> My understanding is that typical use case looks something like this:
<re_irc> fn transfer_dma<BUF: ReadBuffer>(buf: BUF) -> BUF {
<re_irc> start_dma();
<re_irc> let (ptr, len) = buf.read_buffer();
fabic has joined #rust-embedded
<re_irc> <henrikssn> My understanding is that typical use case looks something like this:
<re_irc> fn transfer_dma<BUF: ReadBuffer>(buf: BUF) -> BUF {
<re_irc> let (ptr, len) = buf.read_buffer();
<re_irc> start_dma(ptr, len);
<re_irc> <henrikssn> My understanding is that typical use case looks something like this:
<re_irc> fn transfer_dma<BUF: WriteBuffer>(buf: BUF) -> BUF {
<re_irc> let (ptr, len) = buf.write_buffer();
<re_irc> start_dma(ptr, len);
<re_irc> <GrantM11235> Once you start doing stuff with the raw pointer, rust doesn't make any assumptions about whether the underlying data changes or not
<re_irc> <MathiasKoch> jamesmunns: Would it be possible/feasible to write a CBOR flavor for postcard? "serde_cbor" is no longer maintained, and it seems like postcard produces much leaner codesizes in general..
<re_irc> <GrantM11235> It's basically the same rules as when interacting with an FFI
<re_irc> <henrikssn> GrantM11235: I think the problem here is that the raw pointer breaks aliasing rules afaiu
<re_irc> <GrantM11235> That's not a problem, that's what raw pointers are for
<re_irc> <GrantM11235> I'm not sure I can explain it very well, but it has to do with reborrows
<re_irc> <henrikssn> hmm
<re_irc> <henrikssn> isn't the compiler allowed to do something like this?
<re_irc> start_dma(ptr, len);
<re_irc> let _ = buf; // This is UB
<re_irc> wait_for_dma();
<re_irc> <henrikssn> * &buf;
<re_irc> <GrantM11235> For example, here we have two &muts to the same place, but it is okay because the borrowchecker limits how we can use them https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=f6e414276b6d0fbbd2a77751c6f1eace
<re_irc> <GrantM11235> With raw pointers, we have to follow some of the same rules, but the borrowchecker doesn't check it for us
<re_irc> <GrantM11235> This can probably explain the rules better that I can https://plv.mpi-sws.org/rustbelt/stacked-borrows/
<re_irc> <henrikssn> GrantM11235: I think the point is that in this case it isn't okay, because the borrowchecker assumes that the mutable borrow in "WriteBuffer::write_buffer" respects the aliasing rules, which it doesn't by converting the reference to a raw pointer and handing that to DMA
<re_irc> <henrikssn> Docs say that:
<re_irc> > Once this method has been called, it is unsafe to call any &mut self methods, except for write_buffer, on this object as long as the returned value is in use (by DMA).
<re_irc> <henrikssn> I don't think that can be achieved in safe or unsafe rust because the compiler is free to introduce extra spurious reads even in the presence of compiler fences
<re_irc> <GrantM11235> It's not allowed to do that because that would invalidate the pointer. Once you cast a reference to a pointer, the compiler won't do anything with the reference until you are done with the pointer. It knows you are done with the pointer when _you_ do something with the reference
<re_irc> <henrikssn> thanks, I think that made it clear
<re_irc> <henrikssn> Sorry, I find this to be a really complex subject
<re_irc> <GrantM11235> Yeah, it's definitely complex. The only reason that I understand any of it at all is because someone on here explained it to me 😁
fabic has quit [Ping timeout: 246 seconds]
fabic has joined #rust-embedded
fabic has quit [Ping timeout: 246 seconds]
explore has quit [Quit: Connection closed for inactivity]
starblue has quit [Ping timeout: 268 seconds]
starblue has joined #rust-embedded
fabic has joined #rust-embedded
<re_irc> <James Munns> MathiasKoch: so, flavors are more for "customizing the postcard protocol", rather than "writing another protocol".
<re_irc> <James Munns> flavors mostly let you modify the stream of bytes in/out for ser/de, but it doesn't let you do "extra fancy" things per-item.
<re_irc> <MathiasKoch> makes sense :+1:
<re_irc> I thought of CBOR as comparable to COBS, but i guess it really isn't?
<re_irc> <James Munns> COBS is a framing technique
<re_irc> <James Munns> CBOR is a whole wire protocol :)
<re_irc> <MathiasKoch> Yeah, that is what i just realized ;)
berkowski has joined #rust-embedded
explore has joined #rust-embedded
dc740 has joined #rust-embedded
fabic_ has joined #rust-embedded
fabic has quit [Ping timeout: 260 seconds]
fabic_ has quit [Ping timeout: 268 seconds]
explore has quit [Quit: Connection closed for inactivity]
dc740 has quit [Remote host closed the connection]
dc740 has joined #rust-embedded
bjc has left #rust-embedded [ERC 5.4 (IRC client for GNU Emacs 28.1)]
explore has joined #rust-embedded
dc740 has quit [Remote host closed the connection]
<re_irc> <thalesfragoso> henrikssn: I mean, use a DMA that supports double buffering instead of circular mode, there isn't an overhead with that, you don't copy stuff from a buffer to another
<re_irc> <thalesfragoso> It's the DMA engine that switches its buffer when one is full
<re_irc> <thalesfragoso> henrikssn: Producing spurious reads between the fences would be the same as moving up an operation from after the fence, which isn't allowed