How embedded Rust would manage this?

Hi,

Today, I faced a bug in a firmware and I was wondering if it would be avoided in Rust or not.

We have these functions:

void write_to_usb(unsigned char* data, unsigned int data_size)
{
  // code to setup registers
  // start DMA access
  USB->IN = value;
}

void send_ack()
{
  std::vector<unsigned char> v;
  // fill the vector
  write_to_usb(v.data(), v.size());
}

void caller()
{
   while (true)
   { 
      // received usb message

      send_ack();
      auto response = process_usb_command();
      send_response(response);
      // more code
   }
}

On this homemade device, we receive usb command and acknowledge the reception before processing the command and send the response back.

My bug is that for some commands, the acknowledge message I receive is not properly formatted: I got jumble mumble back.

After investigation and tests, it seems that the vector (not really a vector, but for this example let’s say it is) is cleared too quickly because write_to_usb, provided by the chipset manufacturer, is not blocking. So we leave send_ack and the vector is freed from the stack.

It’s a kind of race condition but as it involved some hardware direct access, I don’t know if Rust would save us from this kind of bugs?

It sounds like Rust’s borrowing and ownership system would’ve prevented this particular bug, yes, if your write_usb function properly integrates with it.

1 Like

Brief, handwavy answer: there’s no single “rust embedded” answer here, but in general (and in at least one of the frameworks under development) this is solved by ownership: the type signatures of DMA functions have to be given ownership of DMA buffers, and they can be handed back by the completion interrupt return.

Edit: intended to come back later and add more when I had the chance, but Vitaly has already linked the main blog post I had in mind

1 Like

You might enjoy this (and others there) blog post by @japaric.

2 Likes

Thank you guys!