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

Crash when using RefCell with large array #128

Closed
alwin-joshy opened this issue Apr 22, 2024 · 4 comments
Closed

Crash when using RefCell with large array #128

alwin-joshy opened this issue Apr 22, 2024 · 4 comments

Comments

@alwin-joshy
Copy link
Contributor

alwin-joshy commented Apr 22, 2024

I was experimenting with the root server example and ran into an issue when using a large array within a RefCell.

A minimal example can be seen below

#![no_std]
#![no_main]

use sel4_root_task::{root_task, Never};
use core::cell::RefCell;

const TEST : RefCell<[i32; 100_000]> = RefCell::new([0; 100_000]);

fn test_fn(_a: &RefCell<[i32]>) {
    sel4::debug_println!("I am in test_fn :)");
}


#[root_task]
fn main(_bootinfo: &sel4::BootInfoPtr) -> sel4::Result<Never> {
    sel4::debug_println!("Hello, World!");

    test_fn(&TEST);

    sel4::debug_println!("TEST_PASS");
    sel4::init_thread::suspend_self()
}

which produces the following output:

/deps/bin/sel4-kernel-loader-add-payload \
	--loader /deps/bin/sel4-kernel-loader \
	--sel4-prefix /deps/seL4/install \
	--app build/example.elf \
	-o build/image.elf
qemu-system-aarch64 -machine virt,virtualization=on -cpu cortex-a57 -m 1024 -nographic -serial mon:stdio -kernel build/image.elf
seL4 kernel loader | INFO   Starting loader
seL4 kernel loader | DEBUG  Platform info: PlatformInfo {
    memory: [
        0x60000000..0x80000000,
    ],
    devices: [
        0x0..0x8000000,
        0x8001000..0x8010000,
        0x8011000..0x8030000,
        0x8031000..0x60000000,
        0x80000000..0x100000000000,
    ],
}
seL4 kernel loader | DEBUG  Loader footprint: 0x6027f000..0x60379b03
seL4 kernel loader | DEBUG  Payload info: PayloadInfo {
    kernel_image: ImageInfo {
        phys_addr_range: 0x60000000..0x6023f000,
        phys_to_virt_offset: 0x8000000000,
        virt_entry: 0x8060000000,
    },
    user_image: ImageInfo {
        phys_addr_range: 0x7ff42000..0x80000000,
        phys_to_virt_offset: 0xffffffff802be000,
        virt_entry: 0x27c600,
    },
    fdt_phys_addr_range: Some(
        0x7ff40000..0x7ff41ddc,
    ),
}
seL4 kernel loader | DEBUG  Payload regions:
seL4 kernel loader | DEBUG      0x60000000..60031da0 true
seL4 kernel loader | DEBUG      0x60031da0..6023f000 false
seL4 kernel loader | DEBUG      0x7ff42000..7ffbc400 true
seL4 kernel loader | DEBUG      0x7ffbd400..7ffeed1c true
seL4 kernel loader | DEBUG      0x7ffefd20..7ffefd38 true
seL4 kernel loader | DEBUG      0x7ffefd38..7ffffd7c false
seL4 kernel loader | DEBUG      0x7ff40000..7ff41ddc true
seL4 kernel loader | DEBUG  Copying payload data
seL4 kernel loader | INFO   Entering kernel
Bootstrapping kernel
Warning: Could not infer GIC interrupt target ID, assuming 0.
available phys memory regions: 1
  [60000000..80000000]
reserved virt address space regions: 3
  [8060000000..806023f000]
  [807ff40000..807ff41ddc]
  [807ff42000..8080000000]
Booting all finished, dropped to user space
Caught cap fault in send phase at address 0
while trying to handle:
user exception 0x2000000 code 0
in thread 0x807ff29400 "rootserver" at address 0x281580
With stack:
0x25b3b0: 0x2a892c
0x25b3b8: 0x0
0x25b3c0: 0x34
0x25b3c8: 0x0
0x25b3d0: 0x34
0x25b3d8: 0x0
0x25b3e0: 0x34
0x25b3e8: 0x25b3c8
0x25b3f0: 0x2816a4
0x25b3f8: 0x10000000040
0x25b400: 0x40
0x25b408: 0x0
0x25b410: 0x0
0x25b418: 0x34
0x25b420: 0x0
0x25b428: 0x34

Examining the mentioned address doesn't reveal anything obvious. Using a smaller array (10_000) inside the RefCell stops it from crashing. Changing the code to use a raw array ([i32; 100_000]) instead of a RefCell also fixes the problem, and its size can be further increased to 1_000_000 without any issues. The problem also occurs when using a Cell instead of a RefCell, but does not occur when using the custom struct below.

struct TestStruct<T: Sized> {
    value: T
}

impl<T> TestStruct<T> {
    pub const fn new(val: T) -> Self {
        return Self {
            value: val
        };
    }
}

const TEST : TestStruct<[i32; 100_000]> = TestStruct::new([0; 100_000]);
@nspin
Copy link
Member

nspin commented Apr 22, 2024

This appears to be a stack overflow. Replacing #[root_task] with #[root_task(stack_size = 1 << 20)] allows the program to run without crashing.

As for your observations about these different types, my guess is that, even without --release, rustc optimizes some types in this position (such as [i32; N] and your struct) but not others. RefCell and Cell are based on UnsafeCell, whose purpose is to block certain optimizations for the sake of enabling interior mutability. Interestingly though, it appears that rustc does optimize the UnsafeCell-backed types in this position when --release is specified.

@alwin-joshy
Copy link
Contributor Author

So the RefCell is probably being allocated on the stack and not in the data region of the executable?

@nspin
Copy link
Member

nspin commented Apr 22, 2024

Yes, my understanding is that the const value is only being used as an initial value on the stack for the argument to test_fn, because RefCell allows for interior mutability.

@alwin-joshy
Copy link
Contributor Author

Interesting, I see a similar issue here (rust-lang/rust#99111), although this did the same thing for normal arrays too. Thanks for your help!

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