Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Type system related to BufferedCan is unsound #3874

Open
blaa opened this issue Feb 12, 2025 · 2 comments
Open

Type system related to BufferedCan is unsound #3874

blaa opened this issue Feb 12, 2025 · 2 comments

Comments

@blaa
Copy link

blaa commented Feb 12, 2025

Tested on embassy f09277b

Problem: Types allow to drop and deinitialize (?) CAN while holding a broken reader/writer types.

My ultimate goal: I want to send CAN frames and not BLOCK if CAN fails and receives no Acks from other devices. Normal interface available via can.split() doesn't seem to publish any len/try/wouldblock methods. BufferedCan does though.

My simplified CAN setup code:

static TX_BUF: StaticCell<can::TxBuf<4>> = StaticCell::new();   
static RX_BUF: StaticCell<can::RxBuf<4>> = StaticCell::new(); 
// I only keep this around so that CAN keeps working. 
static BUFFERED_CAN: StaticCell<embassy_stm32::can::BufferedCan<'static, 4, 4>> = StaticCell::new();

// (...)

let can = can::CanConfigurator::new(p.FDCAN1, p.PB8, p.PB9, CanIrqs);
can.properties().set_extended_filter(  
    can::filter::ExtendedFilterSlot::_0, 
    can::filter::ExtendedFilter::accept_all_into_fifo1(),
);  
can.set_bitrate(250_000); 
let can = can.start(can::OperatingMode::NormalOperationMode); 

let tx_buf = TX_BUF.init(can::TxBuf::<4>::new());
let rx_buf = RX_BUF.init(can::RxBuf::<4>::new());

let buffered = can.buffered(tx_buf, rx_buf);
let writer = buffered.writer();  
let reader = buffered.reader();  
BUFFERED_CAN.init(buffered); // <- This is required, but not enforced
   
Self { 
    can_tx: Mutex::new(writer),  
    can_rx: reader,  
}

// I then await reader in one task, and call try_write (with no awaiting) in other tasks on the writer.

I think problem is that buffered writer()/reader() don't borrow from BufferedCan, relevant part of fdcan.rs:480:

/// Returns a sender that can be used for sending CAN frames.                          
pub fn writer(&self) -> BufferedCanSender {                                 
    BufferedCanSender {                                               
        tx_buf: self.tx_buf.sender().into(),                                  
        waker: self.info.tx_waker,                                               
    }                                                                   
}                                                                            

/// Returns a receiver that can be used for receiving CAN frames. Note, each CAN frame will only be received by one receiver.
pub fn reader(&self) -> BufferedCanReceiver {        
    self.rx_buf.receiver().into()              
}

I believe either documenting this behaviour (albeit not really Rusty) or changing the lifetime dependency would be a solution.

@blaa
Copy link
Author

blaa commented Feb 12, 2025

I haven't thoroughly checked but #3610 maybe related.

@vpochapuis
Copy link

I have also encountered this issue while initializing the BufferedCan driver.

Steps to reproduce:

  • Initialize the CAN driver
  • Use buffered()
  • Splitting it into sender and receiver using: writer() and reader()
  • Drop the original driver and only keep the sender and receiver
  • Try to send frames over the sender and receive frames from the receiver

The observed behavior is that the buffered receiver will work properly, but the sender will never send frames on the CAN Bus.

Thanks @blaa for finding the origin of the problem and providing a fix!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants