为何openat()在3.10.0-693内核中将/dev/stdout识别为目录?
为何Linux 3.10内核中openat会将/dev/stdout识别为目录?
演示程序
#define _GNU_SOURCE #include <stdio.h> #include <fcntl.h> #include <unistd.h> int main(void) { int fd = openat(AT_FDCWD, "/dev/stdout", O_RDONLY|O_PATH|O_DIRECTORY); if (fd == -1) { perror("openat"); return 1; } return 0; }
不同内核下的ltrace结果
Linux 3.10.0-693内核
__libc_start_main([ "./a.out" ] <unfinished ...> openat(0xffffff9c, 0x400670, 0x210000, 0x4005e0) = 3 +++ exited (status 0) +++
Linux 4.14.15-1内核
__libc_start_main(0x40059d, 1, 0x7ffcaccd9e98, 0x4005e0 <unfinished ...> openat(0xffffff9c, 0x400670, 0x210000, 0x4005e0) = -1 perror("openat"openat: Not a directory ) = <void> +++ exited (status 1) +++
补充:3.10.0-693内核下的strace结果
mprotect(0x7f568f480000, 4096, PROT_READ) = 0 munmap(0x7f568f473000, 47716) = 0 openat(AT_FDCWD, "/dev/stdout", O_RDONLY|O_PATH|O_DIRECTORY) = 3 exit_group(0) = ? +++ exited with 0 +++
问题核心
/dev/stdout是指向字符设备的符号链接,绝非目录,但3.10.0-693内核中,带O_PATH|O_DIRECTORY标志的openat()调用居然能成功打开它;而4.14内核中该调用会正确返回Not a directory错误,想了解该边缘情况的具体原因。
原因解析
这是Linux内核处理O_PATH标志时的旧bug,存在于3.10到4.13左右的版本中:
- 3.10内核中,当使用
O_PATH打开文件时,内核跳过了部分常规文件类型检查逻辑。针对/dev/stdout这类符号链接,O_DIRECTORY的类型验证被错误地应用在符号链接本身而非其指向的目标文件上——内核没有跟进符号链接去检查最终目标的类型,只要路径解析完成就返回成功,完全忽略了目标是字符设备的事实。 - 后续4.14及以上内核修复了该问题,在
O_PATH模式下也会强制验证最终目标文件的类型是否符合O_DIRECTORY要求,因此打开/dev/stdout会正确返回ENOTDIR错误。
内容的提问来源于stack exchange,提问作者ms2008
相关产品推荐
相关产品推荐

