Skip to content

free() panics due to integer overflow when handling corrupted memory control blocks #326

@nuczyc

Description

@nuczyc

Describe the bug

The free() function in ulib/axlibc panics when attempting to release memory associated with a corrupted memory control block.

Specifically, the allocator stores metadata (including the allocation size) immediately preceding the memory pointer returned to the user. When free(ptr) is called, the implementation reads this size and calculates the total memory layout to deallocate.

If the size field in the control block is corrupted (e.g., via a buffer overflow or intentional manipulation) to a large value (near SIZE_MAX), the calculation of the total size (requested size + control block overhead) results in an integer overflow. This triggers a panic at ulib/axlibc/src/malloc.rs:53, causing the entire system to shut down.

let layout = Layout::from_size_align(size + CTRL_BLK_SIZE, 8).unwrap();

To Reproduce

  1. Compile the program and run.
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>

/*
 * PoC for triggering panic in ArceOS free() function
 * 
 * The crash occurs at malloc.rs:53:22 where Layout::from_size_align() is called with unwrap()
 * This will panic if the size calculation overflows or is invalid
 * 
 * The vulnerability is reachable from user space through the standard free() function
 * We can trigger it by corrupting the memory control block stored before the allocated memory
 */

int main() {
    // Allocate some memory - this will include a control block before the actual data
    char *ptr = malloc(100);
    if (!ptr) {
        printf("malloc failed\n");
        return 1;
    }
    
    printf("Allocated memory at: %p\n", ptr);
    
    /*
     * In ArceOS, the MemoryControlBlock is stored before the allocated memory
     * We can corrupt it by writing before the allocated pointer
     * The control block contains a 'size' field that we can manipulate
     * 
     * By setting size to a value that causes overflow when adding CTRL_BLK_SIZE,
     * we can trigger the unwrap() panic in Layout::from_size_align()
     */
    
    // Write a large value to corrupt the size field in the control block
    // This will cause size + CTRL_BLK_SIZE to overflow
    // The exact offset depends on the MemoryControlBlock structure
    // We'll try writing at various offsets before the allocated memory
    
    // Try corrupting the size field (assuming it's at offset -8 from ptr)
    // Set size to SIZE_MAX - CTRL_BLK_SIZE + 1 to cause overflow
    uintptr_t *corrupt_ptr = (uintptr_t*)((char*)ptr - sizeof(uintptr_t));
    *corrupt_ptr = SIZE_MAX - 7; // Assuming CTRL_BLK_SIZE is 8
    
    printf("Corrupted control block size field\n");
    
    // Now call free() - this should trigger the panic
    // The Layout::from_size_align() will fail due to size overflow
    printf("Calling free() - this should trigger the panic...\n");
    free(ptr);
    
    // This line should not be reached due to the panic
    printf("This should not be printed\n");
    
    return 0;
}

Environment

Logs

SeaBIOS (version 1.16.3-debian-1.16.3-2)


iPXE (https://ipxe.org) 00:03.0 CA00 PCI2.10 PnP PMM+06FCAA40+06F0AA40 CA00
                                                                               


Booting from ROM..TSC frequency: 4000 MHz

       d8888                            .d88888b.   .d8888b.
      d88888                           d88P" "Y88b d88P  Y88b
     d88P888                           888     888 Y88b.
    d88P 888 888d888  .d8888b  .d88b.  888     888  "Y888b.
   d88P  888 888P"   d88P"    d8P  Y8b 888     888     "Y88b.
  d88P   888 888     888      88888888 888     888       "888
 d8888888888 888     Y88b.    Y8b.     Y88b. .d88P Y88b  d88P
d88P     888 888      "Y8888P  "Y8888   "Y88888P"   "Y8888P"

arch = x86_64
platform = x86-pc
target = x86_64-unknown-none
build_mode = debug
log_level = info
smp = 1

[  0.002831 0 axruntime:136] Logging is enabled.
[  0.003144 0 axruntime:137] Primary CPU 0 started, arg = 0x9500.
[  0.003582 0 axruntime:140] Found physcial memory regions:
[  0.003946 0 axruntime:142]   [PA:0x0, PA:0x100000) reserved (READ | WRITE | RESERVED)
[  0.004433 0 axruntime:142]   [PA:0x100000, PA:0x200000) free memory (READ | WRITE | FREE)
[  0.004934 0 axruntime:142]   [PA:0x200000, PA:0x323000) .text (READ | EXECUTE | RESERVED)
[  0.005443 0 axruntime:142]   [PA:0x323000, PA:0x34b000) .rodata (READ | RESERVED)
[  0.005916 0 axruntime:142]   [PA:0x34b000, PA:0x36c000) .data .tdata .tbss .percpu (READ | WRITE | RESERVED)
[  0.006516 0 axruntime:142]   [PA:0x36c000, PA:0x3ac000) boot stack (READ | WRITE | RESERVED)
[  0.007040 0 axruntime:142]   [PA:0x3ac000, PA:0x3b5000) .bss (READ | WRITE | RESERVED)
[  0.007536 0 axruntime:142]   [PA:0x3b5000, PA:0x7fdc000) free memory (READ | WRITE | FREE)
[  0.008054 0 axruntime:142]   [PA:0xb0000000, PA:0xc0000000) mmio (READ | WRITE | DEVICE | RESERVED)
[  0.008612 0 axruntime:142]   [PA:0xfe000000, PA:0xfec00000) mmio (READ | WRITE | DEVICE | RESERVED)
[  0.009163 0 axruntime:142]   [PA:0xfec00000, PA:0xfec01000) mmio (READ | WRITE | DEVICE | RESERVED)
[  0.009713 0 axruntime:142]   [PA:0xfed00000, PA:0xfed01000) mmio (READ | WRITE | DEVICE | RESERVED)
[  0.010267 0 axruntime:142]   [PA:0xfee00000, PA:0xfee01000) mmio (READ | WRITE | DEVICE | RESERVED)
[  0.010808 0 axruntime:217] Initialize global memory allocator...
[  0.011181 0 axruntime:218]   use TLSF allocator.
[  0.011569 0 axmm:103] Initialize virtual memory management...
[  0.050230 0 axruntime:157] Initialize platform devices...
[  0.050573 0 axplat_x86_pc::apic:65] Initialize Local APIC...
[  0.050932 0 axplat_x86_pc::apic:80] Using x2APIC.
[  0.051268 0 axplat_x86_pc::apic:95] Initialize IO APIC...
[  0.051628 0 axtask::api:73] Initialize scheduling...
[  0.051991 0 axtask::api:79]   use FIFO scheduler.
[  0.052312 0 axdriver:152] Initialize device drivers...
[  0.052651 0 axdriver:153]   device model: static
[  0.060454 0 virtio_drivers::device::blk:59] config: 0xffff8000fe002000
[  0.060900 0 virtio_drivers::device::blk:64] found a block device of size 65536KB
[  0.061444 0 axdriver::bus::pci:104] registered a new Block device at 00:02.0: "virtio-blk"
[  0.066490 0 virtio_drivers::device::net::dev_raw:30] negotiated_features Features(MAC | STATUS | RING_INDIRECT_DESC | RING_EVENT_IDX)
[  0.068420 0 axdriver::bus::pci:104] registered a new Net device at 00:03.0: "virtio-net"
[  0.173742 0 axfs:41] Initialize filesystems...
[  0.174040 0 axfs:44]   use block device 0: "virtio-blk"
[  0.179394 0 fatfs::dir:145] Is a directory
[  0.184256 0 fatfs::dir:145] Is a directory
[  0.191516 0 fatfs::dir:145] Is a directory
[  0.201012 0 fatfs::dir:145] Is a directory
[  0.205113 0 axnet:42] Initialize network subsystem...
[  0.205465 0 axnet:45]   use NIC 0: "virtio-net"
[  0.207178 0 axnet::smoltcp_impl:333] created net interface "eth0":
[  0.207579 0 axnet::smoltcp_impl:334]   ether:    52-54-00-12-34-56
[  0.207975 0 axnet::smoltcp_impl:335]   ip:       10.0.2.15/24
[  0.208345 0 axnet::smoltcp_impl:336]   gateway:  10.0.2.2
[  0.208703 0 axruntime:183] Initialize interrupt handlers...
[  0.209059 0 axcpu::x86_64::trap:46] No registered handler for trap IRQ
[  0.209588 0 axruntime:195] Primary CPU 0 init OK.
Allocated memory at: 0xffff8000003bc378
Corrupted control block size field
Calling free() - this should trigger the panic...
[  0.210515 0:2 axruntime::lang_items:5] panicked at ulib/axlibc/src/malloc.rs:53:46:
attempt to add with overflow
[  0.211128 0:2 axplat_x86_pc::power:25] Shutting down...

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