Skip to content

Reachable unwrap() when mmap under high memory pressure #224

@nuczyc

Description

@nuczyc

Describe the bug

The kernel panics with called Option::unwrap() on a None value. This occurs when running a user-space program that performs aggressive memory allocation (allocating 1GB chunks), pushing the system into a low-memory state.

let offset = offset_get.unwrap();

To Reproduce

  1. Compile the program and run.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/mman.h>

/*
 * PoC for potential arithmetic overflow in RuxOS get_meminfo()
 * 
 * The crash occurs in ruxfs/src/arch/mod.rs:37 when calculating:
 * freeram = (allocator.available_bytes() + allocator.available_pages() * memory_addr::PAGE_SIZE_4K)
 * 
 * This can overflow if:
 * 1. available_pages() is very large
 * 2. The multiplication available_pages * 4096 overflows 64-bit integer
 * 3. Adding available_bytes() to the result causes overflow
 * 
 * User-space reachability analysis:
 * - get_meminfo() is likely exposed through /proc/meminfo or similar interface
 * - Reading /proc/meminfo triggers the vulnerable calculation
 * - We can influence memory state by allocating/freeing large amounts of memory
 * 
 * This PoC attempts to trigger the overflow by:
 * 1. Allocating memory to manipulate available_pages count
 * 2. Reading /proc/meminfo to trigger the vulnerable calculation
 * 3. Repeating with different allocation patterns
 */

int main() {
    FILE *fp;
    char buffer[1024];
    size_t alloc_size = 1024 * 1024 * 1024; // 1GB chunks
    void *ptrs[100];
    int i, iterations = 0;
    
    printf("PoC: Attempting to trigger arithmetic overflow in get_meminfo()\n");
    printf("Strategy: Allocate large memory chunks and read /proc/meminfo\n\n");
    
    // Try different allocation patterns to manipulate memory state
    while (iterations < 10) {
        printf("Iteration %d: Allocating memory...\n", iterations + 1);
        
        // Allocate memory chunks to influence available_pages
        for (i = 0; i < 50; i++) {
            ptrs[i] = malloc(alloc_size);
            if (!ptrs[i]) {
                printf("malloc failed at chunk %d\n", i);
                break;
            }
            memset(ptrs[i], 0x41, alloc_size); // Touch the pages
        }
        
        printf("Allocated %d chunks of %zu bytes\n", i, alloc_size);
        
        // Read /proc/meminfo to trigger the vulnerable calculation
        printf("Reading /proc/meminfo to trigger get_meminfo()...\n");
        fp = fopen("/proc/meminfo", "r");
        if (fp) {
            while (fgets(buffer, sizeof(buffer), fp)) {
                printf("%s", buffer);
            }
            fclose(fp);
        } else {
            printf("Could not open /proc/meminfo\n");
            // Try alternative paths
            fp = fopen("/sys/fs/meminfo", "r");
            if (fp) {
                while (fgets(buffer, sizeof(buffer), fp)) {
                    printf("%s", buffer);
                }
                fclose(fp);
            }
        }
        
        // Free some memory to change state
        for (i = 0; i < 25; i++) {
            if (ptrs[i]) {
                free(ptrs[i]);
                ptrs[i] = NULL;
            }
        }
        
        printf("Freed half the allocations, reading /proc/meminfo again...\n");
        
        // Trigger again with different memory state
        fp = fopen("/proc/meminfo", "r");
        if (fp) {
            while (fgets(buffer, sizeof(buffer), fp)) {
                printf("%s", buffer);
            }
            fclose(fp);
        }
        
        // Clean up remaining allocations
        for (i = 25; i < 50; i++) {
            if (ptrs[i]) {
                free(ptrs[i]);
            }
        }
        
        iterations++;
        printf("\n");
    }
    
    // Try with mmap for larger allocations
    printf("Attempting with mmap for larger allocations...\n");
    for (i = 0; i < 10; i++) {
        void *mmap_ptr = mmap(NULL, alloc_size * 10, PROT_READ | PROT_WRITE, 
                             MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
        if (mmap_ptr != MAP_FAILED) {
            memset(mmap_ptr, 0x42, alloc_size * 10);
            printf("mmap allocated %zu bytes\n", alloc_size * 10);
            
            // Trigger the vulnerable code
            fp = fopen("/proc/meminfo", "r");
            if (fp) {
                while (fgets(buffer, sizeof(buffer), fp)) {
                    if (strncmp(buffer, "MemFree:", 8) == 0 || 
                        strncmp(buffer, "MemTotal:", 9) == 0) {
                        printf("%s", buffer);
                    }
                }
                fclose(fp);
            }
            munmap(mmap_ptr, alloc_size * 10);
        }
    }
    
    printf("PoC completed. If the kernel is vulnerable, it should have panicked.\n");
    printf("Note: This PoC attempts to trigger the overflow through memory allocation\n");
    printf("and reading /proc/meminfo, but success depends on the specific RuxOS\n");
    printf("configuration and memory management implementation.\n");
    
    return 0;
}

2.features.txt

alloc
paging
net
multitask
irq
fs

Environment

Logs

SeaBIOS (version 1.16.3-debian-1.16.3-2)


iPXE (https://ipxe.org) 00:03.0 CA00 PCI2.10 PnP PMM+7EFCAA40+7EF0AA40 CA00
                                                                               


Booting from ROM..
Initialize IDT & GDT...

8888888b.                     .d88888b.   .d8888b.
888   Y88b                   d88P" "Y88b d88P  Y88b
888    888                   888     888 Y88b.
888   d88P 888  888 888  888 888     888  "Y888b.
8888888P"  888  888 `Y8bd8P' 888     888     "Y88b.
888 T88b   888  888   X88K   888     888       "888
888  T88b  Y88b 888 .d8""8b. Y88b. .d88P Y88b  d88P
888   T88b  "Y88888 888  888  "Y88888P"   "Y8888P"

arch = x86_64
platform = x86_64-qemu-q35
target = x86_64-unknown-none
smp = 1
build_mode = debug
log_level = warn

[  0.189379 0 axfs_ramfs::dir:68] AlreadyExists sys
PoC: Attempting to trigger arithmetic overflow in get_meminfo()
Strategy: Allocate large memory chunks and read /proc/meminfo

Iteration 1: Allocating memory...
[  1.935369 0:1 ruxmm::paging:61] available page num is 1023
[  1.935801 0:1 ruxruntime::lang_items:14] panicked at api/ruxos_posix_api/src/imp/mmap/utils.rs:358:49:
called `Option::unwrap()` on a `None` value

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions