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

Need help with heap allocation from user program #1348

Open
sauravdeshpande opened this issue Sep 28, 2024 · 3 comments
Open

Need help with heap allocation from user program #1348

sauravdeshpande opened this issue Sep 28, 2024 · 3 comments

Comments

@sauravdeshpande
Copy link

Hello,

When mapping the heap from the init_heap() I have added the PageTableFlags::User_Accessible. I also created a small user program which runs in ring 3. In that program when I try to use Box::new() I get a page fault error with error code User_Mode and Instruction Fetch. Also the virtual address which the Box::new() is trying to access is where the user program is mapped, instead of the heap address.
I am unable to figure out why Box::new() is not storing the value on heap when used in userspace, when it does correctly in kernel space.

Thank you.

@tsatke
Copy link

tsatke commented Sep 29, 2024

Can you share the code?

Do you have separate address spaces?

@sauravdeshpande
Copy link
Author

@tsatke, Thank you

I was trying to set up a user mode program and test if it runs in ring 3. I set up the gdt. With a simple syscall was testing to print a value created in the user program. As of now i don't have a seperated addresss spaces was trying to figure out how to do it, I do have a Page that is mapped as user accessible. It works well with numbers that are stored on stack.

Created a simple try_userprog function:

pub unsafe extern "C" fn try_userprog() { //let value = "hello MyOs\0"; //let str_ptr = value.as_ptr(); let user_string = Box::new(50); asm!( "\ mov r9, 0x1 mov rdi, cs syscall 2: jmp 2b", // in(reg) user_string_ptr, options(noreturn) ); }
Then found out the virtual address of the try_userprog function and then mapped it:

`/// Maps one page for the user program for testing usermode
pub fn create_prog_mapping(
page: Page,
phys: PhysAddr,
mapper: &mut OffsetPageTable,
frame_allocator: &mut impl FrameAllocator,
) {
use x86_64::structures::paging::PageTableFlags as Flags;

let frame = PhysFrame::containing_address(phys);
let flags = Flags::PRESENT | Flags::USER_ACCESSIBLE | Flags::WRITABLE;

let map_to_result = unsafe {
    //fixme: this is not safe, we do it only for testing
    mapper.map_to(page, frame, flags, frame_allocator)
};
map_to_result.expect("map to failed").flush();

}`

Also created a stack for the user program and mapped it :

`pub fn create_stack_mapping(
page: Page,
phys: PhysAddr,
mapper: &mut OffsetPageTable,
frame_allocator: &mut impl FrameAllocator,
) {
use x86_64::structures::paging::PageTableFlags as Flags;

let frame = PhysFrame::containing_address(phys);
let flags = Flags::PRESENT | Flags::WRITABLE | Flags::USER_ACCESSIBLE;

let map_to_result = unsafe {
    //fixme: this is not safe, we do it only for testing
    mapper.map_to(page, frame, flags, frame_allocator)
};
map_to_result.expect("map to failed").flush();

}`

When initializing heap, made a change - In PageTableFlags::User_Accessible :

`pub fn init_heap(
mapper: &mut impl Mapper,
frame_allocator: &mut impl FrameAllocator,
) -> Result<(), MapToError> {
let page_range = {
let heap_start = VirtAddr::new(HEAP_START as u64);
let heap_end = heap_start + HEAP_SIZE.try_into().unwrap() - 1u64;
let heap_start_page = Page::containing_address(heap_start);
let heap_end_page = Page::containing_address(heap_end);
Page::range_inclusive(heap_start_page, heap_end_page)
};

for page in page_range {
    let frame = frame_allocator
        .allocate_frame()
        .ok_or(MapToError::FrameAllocationFailed)?;
    let flags =
        PageTableFlags::PRESENT | PageTableFlags::WRITABLE | PageTableFlags::USER_ACCESSIBLE;
    unsafe { mapper.map_to(page, frame, flags, frame_allocator)?.flush() };
}
unsafe { ALLOCATOR.lock().init(HEAP_START, HEAP_SIZE) }
Ok(())

}`

When I try to use Box::new() I get the Page Fault error:

Screenshot from 2024-09-29 16-12-23

Thank you.

@tsatke
Copy link

tsatke commented Oct 8, 2024

Is the code of Box::new also user accessible? Your heap is accessible (which is good), and let's say you mapped the program and stack correctly (although a single page will get you in trouble further down the road).

However, the compiled code of Box::new lives in your kernel executable, which probably isn't user accessible.
As a workaround, I would recommend just making that user accessible.

For a more sustainable solution, I'd build a separate executable that has all the code it needs (core + maybe alloc with the mmap syscall for userspace heap), and basic elf loading. Elf loading is the easy part here, since you can just use a library that does the parsing for you.

As a reference, here's my implementation:

I have to add that I haven't made it into usermode yet, so you might adapt some things, like the trampoline (which makes the same mistake that I think you made).

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