Description
Writing to /sys/class/fpga_manager/fpga*/firmware
when driver's firmware write fails and causes a panic. The std::io::Write
call mishandles an IO error from the system when interfacing with a hardware driver's virtual file in Linux (Ubuntu 24.04). The atypical error codes returned by the driver are mishandled in rust, but work as expected in other languages such as python (and bash).
I tried this code (as sudo):
use std::io::Write;
use std::fs::OpenOptions;
use std::error::Error;
fn main() -> Result<(), Box<dyn Error>> {
let control_path = "/sys/class/fpga_manager/fpga0/firmware";
let f = OpenOptions::new()
.create(false)
.read(false)
.write(true)
.open(control_path)?;
write!(&f, "blank.bit.bin")?;
Ok(())
}
I expected to see this happen:
In this case, the blank.bit.bin is an invalid bitstream for the FPGA and so the write should return an error.
The kernel logs show:
fpga_manager fpga0: writing blank.bit.bin to Xilinx ZynqMP FPGA Manager
fpga_manager fpga0: Error while writing image data to FPGA
as expected, so the write is happening and completes.
In bash, this is done as:
$ echo "blank.bit.bin" | sudo tee -a /sys/class/fpga_manager/fpga0/firmware
which does not panic, but does return error code 1: No such file or directory
(which is not correct, but that's not relevant).
The error code reported by the driver is stored in /sys/class/fpga_manager/fpga0/state
and is
write error: 0xffffe5fe
. I believe that this value is what is mishandled in Rust.
The write should return an Err
of type Err(std::io::Error)
as the Result
when encountering this error code.
I wrote this C application to do the same operation and it may explain the issue:
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
int main(void) {
int fd = open("/sys/class/fpga_manager/fpga0/firmware", O_WRONLY | O_CREAT | O_TRUNC, 0644);
if (fd == -1) {
perror("open");
return 1;
}
const char *data = "blank.bit.bin";
ssize_t res = write(fd, data, strlen(data));
if (res == -1) {
perror("write");
} else {
printf("Wrote %zd bytes\n", res);
}
if (close(fd) == -1) {
perror("close");
}
return 0;
}
will print
Wrote -6658 bytes
which leads me to suspect that to the error code printed:
range start index 18446744073709544958 out of range for slice of length 13
is caused by not handling the negative return value safely.
For your convenience:
18446744073709544958 = 0xFFFFFFFFFFFFE5FE
-6658 = 0xFFFFE5FE
18446744073709544958 + 6658 = 0
Instead, this happened:
The kernel panicked. Instead of returning the error code, the slice is indexed incorrectly, and causes a panic during slice_start_index_len_fail
. The stack traces is included below.
Meta
rustc --version --verbose
:
rustc 1.87.0 (17067e9ac 2025-05-09)
binary: rustc
commit-hash: 17067e9ac6d7ecb70e50f92c1944e545188d2359
commit-date: 2025-05-09
host: aarch64-unknown-linux-gnu
release: 1.87.0
LLVM version: 20.1.1
full strace output: https://pastebin.com/rvaBZZhe
Backtrace
$ sudo RUST_LOG=trace RUST_BACKTRACE=full ~/fpgad/target/debug/fpgad
thread 'main' panicked at /rustc/17067e9ac6d7ecb70e50f92c1944e545188d2359/library/std/src/io/mod.rs:1801:36:
range start index 18446744073709544958 out of range for slice of length 13
stack backtrace:
0: 0xc6788f6b6dd4 - std::backtrace_rs::backtrace::libunwind::trace::h7503a148b50af80e
at /rustc/17067e9ac6d7ecb70e50f92c1944e545188d2359/library/std/src/../../backtrace/src/backtrace/libunwind.rs:117:9
1: 0xc6788f6b6dd4 - std::backtrace_rs::backtrace::trace_unsynchronized::h53bbad37af4a0c96
at /rustc/17067e9ac6d7ecb70e50f92c1944e545188d2359/library/std/src/../../backtrace/src/backtrace/mod.rs:66:14
2: 0xc6788f6b6dd4 - std::sys::backtrace::_print_fmt::hd99d24ec073b8939
at /rustc/17067e9ac6d7ecb70e50f92c1944e545188d2359/library/std/src/sys/backtrace.rs:66:9
3: 0xc6788f6b6dd4 - <std::sys::backtrace::BacktraceLock::print::DisplayBacktrace as core::fmt::Display>::fmt::h1fbe53ada150180d
at /rustc/17067e9ac6d7ecb70e50f92c1944e545188d2359/library/std/src/sys/backtrace.rs:39:26
4: 0xc6788f6d0bcc - core::fmt::rt::Argument::fmt::h049d7aecf8048234
at /rustc/17067e9ac6d7ecb70e50f92c1944e545188d2359/library/core/src/fmt/rt.rs:184:76
5: 0xc6788f6d0bcc - core::fmt::write::h5b94da46a291b015
at /rustc/17067e9ac6d7ecb70e50f92c1944e545188d2359/library/core/src/fmt/mod.rs:1481:21
6: 0xc6788f6b4ee8 - std::io::default_write_fmt::h7afd8f861709d185
at /rustc/17067e9ac6d7ecb70e50f92c1944e545188d2359/library/std/src/io/mod.rs:639:11
7: 0xc6788f6b4ee8 - std::io::Write::write_fmt::hec377ff9de3cf70a
at /rustc/17067e9ac6d7ecb70e50f92c1944e545188d2359/library/std/src/io/mod.rs:1914:13
8: 0xc6788f6b6c88 - std::sys::backtrace::BacktraceLock::print::hb027b8167acd80fb
at /rustc/17067e9ac6d7ecb70e50f92c1944e545188d2359/library/std/src/sys/backtrace.rs:42:9
9: 0xc6788f6b7b04 - std::panicking::default_hook::{{closure}}::hfad582597c3f1f0b
at /rustc/17067e9ac6d7ecb70e50f92c1944e545188d2359/library/std/src/panicking.rs:300:22
10: 0xc6788f6b795c - std::panicking::default_hook::h89691caec866c0de
at /rustc/17067e9ac6d7ecb70e50f92c1944e545188d2359/library/std/src/panicking.rs:327:9
11: 0xc6788f6b83d0 - std::panicking::rust_panic_with_hook::h09fd228ea89637cb
at /rustc/17067e9ac6d7ecb70e50f92c1944e545188d2359/library/std/src/panicking.rs:833:13
12: 0xc6788f6b8230 - std::panicking::begin_panic_handler::{{closure}}::h27e0e6e387396050
at /rustc/17067e9ac6d7ecb70e50f92c1944e545188d2359/library/std/src/panicking.rs:706:13
13: 0xc6788f6b72c8 - std::sys::backtrace::__rust_end_short_backtrace::h1396474dff16dbe5
at /rustc/17067e9ac6d7ecb70e50f92c1944e545188d2359/library/std/src/sys/backtrace.rs:168:18
14: 0xc6788f6b7ee0 - __rustc[95feac21a9532783]::rust_begin_unwind
at /rustc/17067e9ac6d7ecb70e50f92c1944e545188d2359/library/std/src/panicking.rs:697:5
15: 0xc6788f696098 - core::panicking::panic_fmt::h2784ce5f3242d97e
at /rustc/17067e9ac6d7ecb70e50f92c1944e545188d2359/library/core/src/panicking.rs:75:14
16: 0xc6788f6965b8 - core::slice::index::slice_start_index_len_fail::do_panic::runtime::h2ad90a17f7f28956
at /rustc/17067e9ac6d7ecb70e50f92c1944e545188d2359/library/core/src/panic.rs:218:21
17: 0xc6788f6963a4 - core::slice::index::slice_start_index_len_fail::do_panic::hbedd6cc2a2234b45
at /rustc/17067e9ac6d7ecb70e50f92c1944e545188d2359/library/core/src/intrinsics/mod.rs:3241:9
18: 0xc6788f6963a4 - core::slice::index::slice_start_index_len_fail::h0f3d6e6b29b55e99
at /rustc/17067e9ac6d7ecb70e50f92c1944e545188d2359/library/core/src/panic.rs:223:9
19: 0xc6788f699e2c - <core::ops::range::RangeFrom<usize> as core::slice::index::SliceIndex<[T]>>::index::h7a4c4512053479f6
at /rustc/17067e9ac6d7ecb70e50f92c1944e545188d2359/library/core/src/slice/index.rs:561:13
20: 0xc6788f698064 - core::slice::index::<impl core::ops::index::Index<I> for [T]>::index::h99ee48726c6e993f
at /rustc/17067e9ac6d7ecb70e50f92c1944e545188d2359/library/core/src/slice/index.rs:16:9
21: 0xc6788f698064 - std::io::Write::write_all::h25e43cf9727ad31a
at /rustc/17067e9ac6d7ecb70e50f92c1944e545188d2359/library/std/src/io/mod.rs:1801:36
22: 0xc6788f697768 - <std::io::default_write_fmt::Adapter<T> as core::fmt::Write>::write_str::hea1b4cdcf8ad26b9
at /rustc/17067e9ac6d7ecb70e50f92c1944e545188d2359/library/std/src/io/mod.rs:628:19
23: 0xc6788f6d0c18 - core::fmt::write::h5b94da46a291b015
at /rustc/17067e9ac6d7ecb70e50f92c1944e545188d2359/library/core/src/fmt/mod.rs:1506:9
24: 0xc6788f697478 - std::io::default_write_fmt::h488cd17643e5f163
at /rustc/17067e9ac6d7ecb70e50f92c1944e545188d2359/library/std/src/io/mod.rs:639:11
25: 0xc6788f697ba0 - std::io::Write::write_fmt::hef83df5134388249
at /rustc/17067e9ac6d7ecb70e50f92c1944e545188d2359/library/std/src/io/mod.rs:1914:13
26: 0xc6788f6971c4 - fpgad::main::h0196210e57a4dc4e
at /home/ubuntu/fpgad/WS0er33ydC/src/main.rs:59:5
27: 0xc6788f696904 - core::ops::function::FnOnce::call_once::hb22d96a71eaebd12
at /rustc/17067e9ac6d7ecb70e50f92c1944e545188d2359/library/core/src/ops/function.rs:250:5
28: 0xc6788f69686c - std::sys::backtrace::__rust_begin_short_backtrace::hf8c79057dadb648f
at /rustc/17067e9ac6d7ecb70e50f92c1944e545188d2359/library/std/src/sys/backtrace.rs:152:18
29: 0xc6788f696cb8 - std::rt::lang_start::{{closure}}::h40bb393ddedb5aed
at /rustc/17067e9ac6d7ecb70e50f92c1944e545188d2359/library/std/src/rt.rs:199:18
30: 0xc6788f6b2144 - core::ops::function::impls::<impl core::ops::function::FnOnce<A> for &F>::call_once::hd800098b0508cfaa
at /rustc/17067e9ac6d7ecb70e50f92c1944e545188d2359/library/core/src/ops/function.rs:284:13
31: 0xc6788f6b2144 - std::panicking::try::do_call::he73c8963fb5ba50b
at /rustc/17067e9ac6d7ecb70e50f92c1944e545188d2359/library/std/src/panicking.rs:589:40
32: 0xc6788f6b2144 - std::panicking::try::h26b8f5da2b75e384
at /rustc/17067e9ac6d7ecb70e50f92c1944e545188d2359/library/std/src/panicking.rs:552:19
33: 0xc6788f6b2144 - std::panic::catch_unwind::h4efc10d799add92b
at /rustc/17067e9ac6d7ecb70e50f92c1944e545188d2359/library/std/src/panic.rs:359:14
34: 0xc6788f6b2144 - std::rt::lang_start_internal::{{closure}}::ha387644324cd6b0d
at /rustc/17067e9ac6d7ecb70e50f92c1944e545188d2359/library/std/src/rt.rs:168:24
35: 0xc6788f6b2144 - std::panicking::try::do_call::ha3a74b6e4c2f30bd
at /rustc/17067e9ac6d7ecb70e50f92c1944e545188d2359/library/std/src/panicking.rs:589:40
36: 0xc6788f6b2144 - std::panicking::try::h6c715ec6edd04ab6
at /rustc/17067e9ac6d7ecb70e50f92c1944e545188d2359/library/std/src/panicking.rs:552:19
37: 0xc6788f6b2144 - std::panic::catch_unwind::h299af3710728a70e
at /rustc/17067e9ac6d7ecb70e50f92c1944e545188d2359/library/std/src/panic.rs:359:14
38: 0xc6788f6b2144 - std::rt::lang_start_internal::h1e2ca5c47444f14a
at /rustc/17067e9ac6d7ecb70e50f92c1944e545188d2359/library/std/src/rt.rs:164:5
39: 0xc6788f696c94 - std::rt::lang_start::h078039403035c0d9
at /rustc/17067e9ac6d7ecb70e50f92c1944e545188d2359/library/std/src/rt.rs:198:5
40: 0xc6788f697290 - main
41: 0xeb507b1784c4 - <unknown>
42: 0xeb507b178598 - __libc_start_main
43: 0xc6788f6966f0 - _start
44: 0x0 - <unknown>