Hacktoberfest 2026:维护者为十月标记出来的 issue,仍然开放、适合新手。 浏览 Hacktoberfest issue

fork() succeeds with a descriptor missing when its pin cannot be allocated

未关闭 适合新手
#414 0 条评论 0 个 reaction 已指派 0 人 在 GitHub 查看

维护者通常 3 天内回复

还没有人认领这个 Issue。

评估

难度
2/5
预计耗时
1-3 小时
新手友好度
75/100
Issue 类型
缺陷
描述清晰度
描述清楚
活跃度
活跃
技术栈
c

调研方向

从 src/runtime/fork-state.c 的 fork_ipc_send_fd_table() 开始,查看它如何处理 fd_lifetime_pin_locked() 返回的 NULL 值。然后查看 src/syscall/fdtable.c 中的 fd_lifetime_pin_spare(),了解分配失败的情况。修复应使 fork() 在无法分配 pin 时返回 -1 并携带 ENOMEM,你可以使用所描述的故障注入复现步骤进行验证。

由索引模型根据 Issue 内容生成。

描述

bug

fork_ipc_send_fd_table() (src/runtime/fork-state.c) skips a slot when fd_lifetime_pin_locked() returns NULL:

pin = fd_lifetime_pin_locked(i);
if (!pin)
    continue;

fd_lifetime_pin_spare() returns NULL for a closed slot, for a slot with no host descriptor, and when malloc fails. The loop has already skipped FD_CLOSED under the same fd_lock hold, so on an open slot with a host descriptor NULL means the allocation failed. The slot is then left out of the table sent to the child, the send completes, and the parent's fork() returns the child pid. Nothing is logged.

Linux fork() either gives the child every descriptor or fails with ENOMEM.

Reproduction

The allocation cannot be made to fail from the guest, so this uses fault injection on b57f367. In fd_lifetime_pin_spare() (src/syscall/fdtable.c):

-            lifetime = malloc(sizeof(*lifetime));
+            lifetime = (fd == 5 && getenv("PINFAIL"))
+                           ? NULL
+                           : malloc(sizeof(*lifetime));

Guest:

for (int i = 0; i < 5; i++)
    open("/dev/null", O_RDONLY); /* fds 3..7 */
pid_t pid = fork();
printf("[%s] fork returned %d\n", pid ? "parent" : "child", (int) pid);
if (pid == 0) {
    for (int fd = 3; fd <= 7; fd++)
        printf("child fd %d: %s\n", fd,
               fcntl(fd, F_GETFD) >= 0 ? "open" : strerror(errno));
    _exit(0);
}
waitpid(pid, NULL, 0);

With PINFAIL=1:

[parent] fork returned 2
[child] fork returned 0
child fd 3: open
child fd 4: open
child fd 5: Bad file descriptor
child fd 6: open
child fd 7: open

Without the variable, fd 5 is open in the child.

Expected

fork() returns -1 with ENOMEM and no child is left running.

The skip is older than the pin. At 628181b, where this loop still calls dup(), a guest holding 1021 descriptors under ulimit -n 1280 gets a successful fork() and a child with descriptors missing.

主要语言
C
星标
275
派生
29
平均合并
2 天 22 小时
30 天内合并 PR
18

环境准备

  • 没有 Dockerfile 或 Docker Compose 文件
  • 没有 Pull Request 模板
  • 阅读贡献指南

从这里开始

  1. 先读完整个 Issue,再读项目的贡献指南。
  2. 在 Issue 下留言说明你要接手 —— 这能避免两个人做同样的事。
  3. Fork 仓库,在一个分支上完成修改。
  4. 提交 Pull Request,并在描述里引用这个 Issue 编号。

sysprog21/elfuse 的其他 Issue

查看 sysprog21/elfuse 的全部 Issue

相似的 Issue

更多 C Issue

把新 issue 发到你的邮箱

精选适合新手参与的 GitHub issue 摘要。