fork() succeeds with a descriptor missing when its pin cannot be allocated
维护者通常 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 内容生成。
描述
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 模板
- 阅读贡献指南
从这里开始
- 先读完整个 Issue,再读项目的贡献指南。
- 在 Issue 下留言说明你要接手 —— 这能避免两个人做同样的事。
- Fork 仓库,在一个分支上完成修改。
- 提交 Pull Request,并在描述里引用这个 Issue 编号。
sysprog21/elfuse 的其他 Issue
-
bug
难度 2/5 1-3 小时 新手友好度 86/100
维护者通常 3 天内回复
-
bug
难度 2/5 1-3 小时 新手友好度 72/100
维护者通常 3 天内回复
-
bug
难度 5/5 一周以上 新手友好度 35/100
维护者通常 3 天内回复
-
难度 5/5 一周以上 新手友好度 25/100
维护者通常 3 天内回复
-
bug
难度 5/5 一周以上 新手友好度 30/100
维护者通常 3 天内回复
相似的 Issue
-
difficulty: easy
难度 2/5 1-3 小时 新手友好度 64/100
-
难度 2/5 1-3 小时 新手友好度 65/100
-
难度 2/5 1-3 小时 新手友好度 76/100
ChefKissInc/Inferno#319 ·
-
llvm-tools
难度 1/5 1 小时以内 新手友好度 72/100
llvm/llvm-project#235522 ·
维护者通常 1 天内回复
-
难度 2/5 1-3 小时 新手友好度 76/100