Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

Is the pure existance of a `&[u32]` and `&mut [u32]` to the same data already considered undefined behaviour?

I am in the process of implementing an SPI driver.

Now I have the following driver code (MRE-ified):

use tokio::{join, sync::mpsc};

async fn spi_transmit(write_buf: &[u32], read_buf: &mut [u32]) {
    assert_eq!(read_buf.len(), write_buf.len());
    let (write_fifo, mut read_fifo) = mpsc::channel(2);

    let write_task = async {
        // Simulate an SPI bus that respondes with the sent data + 20,
        // just for demo purposes
        for val in write_buf {
            write_fifo.send(*val + 20).await.unwrap();
        }
    };

    let read_task = async {
        for val in read_buf {
            *val = read_fifo.recv().await.unwrap();
        }
    };

    join!(write_task, read_task);
}

#[tokio::main]
async fn main() {
    let buf_out = [1, 2, 3, 4];
    let mut buf_in = [0, 0, 0, 0];

    spi_transmit(&buf_out, &mut buf_in).await;

    println!("{:?}", buf_in);
}
[21, 22, 23, 24]

The central API is async fn spi_transmit(write_buf: &[u32], read_buf: &mut [u32]).

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

Now I need to implement an async fn spi_transmit_in_place(read_write_buf: &mut [u32]) that transmits and receives from the same buffer, which in the end contains the read data.

I know for a fact that read and write access to the buffer will never overlap, a byte will always be read first and then at a later, non-overlapping time it will be written.

With that knowledge, is it considered sound if I implement it like this, or is this considered undefined behaviour?

async fn spi_transmit_in_place(read_write_buf: &mut [u32]) {
    let write_buf = unsafe {
        let data = read_write_buf.as_ptr();
        let len = read_write_buf.len();
        std::slice::from_raw_parts(data, len)
    };

    spi_transmit(write_buf, read_write_buf).await
}

#[tokio::main]
async fn main() {
    let mut buf = [1, 2, 3, 4];
    spi_transmit_in_place(&mut buf).await;
    println!("{:?}", buf);
}
[21, 22, 23, 24]

It works, but miri is not happy.

If this is undefined behavior, does this mean I’m forced to re-implement spi_transmit using raw pointers? Or how else could I solve this problem?

>Solution :

Yes, about having both a reference and an exclusive reference Behavior considered undefined tells us:

breaking the pointer aliasing rules

is UB if both references are live, specifically

Such ranges shall not overlap with any ranges of addresses allocated by mechanisms provided by LLVM

and

Each time a reference or box is passed to or returned from a function, it is considered live.

creating them and then sequentially accessing them one after the other also makes them live from creation to access and thus you don’t even need to call a function to make it UB.

Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading