为什么......在syscall中使用与之前打开的dirfd不同的dirfd?

Mar*_*eck 4 c linux linux-kernel

使用LD_PRELOAD我正在打印所有打开的文件描述符,但...at系统调用使用不同的文件描述符.为什么?

最简单的例子:

这是我的库foobar.c- 它拦截所有open...调用并打印文件描述符,并打印调用参数unlinkat

#define _GNU_SOURCE
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <dlfcn.h>
#include <stdio.h>
#include <unistd.h>

int open(const char *pathname, int flags, ...)
{
    int fd = ((int (*) (const char*, int, ...))dlsym(RTLD_NEXT, "open"))(pathname, flags);
    printf("open pathname %s fd %d\n", pathname, fd);
    return fd;  
}

int open64(const char *pathname, int flags, ...)
{
    int fd = ((int (*) (const char*, int, ...))dlsym(RTLD_NEXT, "open64"))(pathname, flags);
    printf("open64 pathname %s fd %d\n", pathname, fd);
    return fd;  
}

int openat(int dirfd, const char* pathname, int flags, ...) {
    int fd = ((int (*) (int, const char*, int, ...))dlsym(RTLD_NEXT, "openat"))(dirfd, pathname, flags);
    printf("openat pathname %s fd %d\n", pathname, fd);
    return fd;
}    

int unlinkat(int dirfd, const char *pathname, int flags) {
    printf("unlinkat dirfd %d pathname %s\n", dirfd, pathname); 
    return ((int (*) (int, const char*, int))dlsym(RTLD_NEXT, "unlinkat"))(dirfd, pathname, flags);
}
Run Code Online (Sandbox Code Playgroud)

我没有包括dup拦截以保持简单,他们没有被调用.

现在注意到库工作正常,我捕获所有打开的文件描述符,它们都等于3,但是,打开的文件描述符相对于它unlinkat被调用,是4.为什么?

/tmp>mkdir -p dir/dir1
/tmp>gcc -shared  -fPIC -ldl foobar.c -o foobar.so
/tmp>LD_PRELOAD=$PWD/foobar.so rm -rf dir
openat pathname dir fd 3
openat pathname dir1 fd 3
unlinkat dirfd 4 pathname dir1
unlinkat dirfd -100 pathname dir
Run Code Online (Sandbox Code Playgroud)

Mat*_*lia 5

[mitalia@mitalia /tmp]$ mkdir -p dir/dir1
[mitalia@mitalia /tmp]$ strace rm -rf dir
execve("/bin/rm", ["rm", "-rf", "dir"], [/* 76 vars */]) = 0
brk(0)                                  = 0x1538000
access("/etc/ld.so.nohwcap", F_OK)      = -1 ENOENT (No such file or directory)
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f4c779a2000
access("/etc/ld.so.preload", R_OK)      = -1 ENOENT (No such file or directory)
open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=211009, ...}) = 0
mmap(NULL, 211009, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7f4c7796e000
close(3)                                = 0
access("/etc/ld.so.nohwcap", F_OK)      = -1 ENOENT (No such file or directory)
open("/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\320\37\2\0\0\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0755, st_size=1840928, ...}) = 0
mmap(NULL, 3949248, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f4c773bd000
mprotect(0x7f4c77578000, 2093056, PROT_NONE) = 0
mmap(0x7f4c77777000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1ba000) = 0x7f4c77777000
mmap(0x7f4c7777d000, 17088, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7f4c7777d000
close(3)                                = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f4c7796d000
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f4c7796b000
arch_prctl(ARCH_SET_FS, 0x7f4c7796b740) = 0
mprotect(0x7f4c77777000, 16384, PROT_READ) = 0
mprotect(0x60d000, 4096, PROT_READ)     = 0
mprotect(0x7f4c779a4000, 4096, PROT_READ) = 0
munmap(0x7f4c7796e000, 211009)          = 0
brk(0)                                  = 0x1538000
brk(0x1559000)                          = 0x1559000
open("/usr/lib/locale/locale-archive", O_RDONLY|O_CLOEXEC) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=5766672, ...}) = 0
mmap(NULL, 5766672, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7f4c76e3d000
close(3)                                = 0
ioctl(0, SNDCTL_TMR_TIMEBASE or SNDRV_TIMER_IOCTL_NEXT_DEVICE or TCGETS, {B38400 opost isig icanon echo ...}) = 0
lstat("/", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
newfstatat(AT_FDCWD, "dir", {st_mode=S_IFDIR|0775, st_size=4096, ...}, AT_SYMLINK_NOFOLLOW) = 0
openat(AT_FDCWD, "dir", O_RDONLY|O_NOCTTY|O_NONBLOCK|O_DIRECTORY|O_NOFOLLOW) = 3
fstat(3, {st_mode=S_IFDIR|0775, st_size=4096, ...}) = 0
fcntl(3, F_GETFL)                       = 0x38800 (flags O_RDONLY|O_NONBLOCK|O_LARGEFILE|O_DIRECTORY|O_NOFOLLOW)
fcntl(3, F_SETFD, FD_CLOEXEC)           = 0
getdents(3, /* 3 entries */, 32768)     = 72
close(3)                                = 0
openat(AT_FDCWD, "dir", O_RDONLY|O_NOCTTY|O_NONBLOCK|O_DIRECTORY|O_NOFOLLOW) = 3
fcntl(3, F_GETFD)                       = 0
fcntl(3, F_SETFD, FD_CLOEXEC)           = 0
fstat(3, {st_mode=S_IFDIR|0775, st_size=4096, ...}) = 0
fcntl(3, F_GETFL)                       = 0x38800 (flags O_RDONLY|O_NONBLOCK|O_LARGEFILE|O_DIRECTORY|O_NOFOLLOW)
fcntl(3, F_SETFD, FD_CLOEXEC)           = 0
fcntl(3, F_DUPFD, 3)                    = 4
fcntl(4, F_GETFD)                       = 0
fcntl(4, F_SETFD, FD_CLOEXEC)           = 0
getdents(3, /* 3 entries */, 32768)     = 72
getdents(3, /* 0 entries */, 32768)     = 0
close(3)                                = 0
newfstatat(4, "dir1", {st_mode=S_IFDIR|0775, st_size=4096, ...}, AT_SYMLINK_NOFOLLOW) = 0
openat(4, "dir1", O_RDONLY|O_NOCTTY|O_NONBLOCK|O_DIRECTORY|O_NOFOLLOW) = 3
fstat(3, {st_mode=S_IFDIR|0775, st_size=4096, ...}) = 0
fcntl(3, F_GETFL)                       = 0x38800 (flags O_RDONLY|O_NONBLOCK|O_LARGEFILE|O_DIRECTORY|O_NOFOLLOW)
fcntl(3, F_SETFD, FD_CLOEXEC)           = 0
getdents(3, /* 2 entries */, 32768)     = 48
getdents(3, /* 0 entries */, 32768)     = 0
close(3)                                = 0
unlinkat(4, "dir1", AT_REMOVEDIR)       = 0
close(4)                                = 0
unlinkat(AT_FDCWD, "dir", AT_REMOVEDIR) = 0
lseek(0, 0, SEEK_CUR)                   = -1 ESPIPE (Illegal seek)
close(0)                                = 0
close(1)                                = 0
close(2)                                = 0
exit_group(0)                           = ?
+++ exited with 0 +++
Run Code Online (Sandbox Code Playgroud)

在我的系统上,文件描述符4是通过fcntl调用返回的文件描述符F_DUPFD,它是近亲dup2; 我也期待你的类似事情发生.

一般来说,如果你想监控的文件描述符的创作就必须监视也文件描述符的复制功能(dup,dup2,dup3,fcntlF_DUPFD,也许别人认为我不知道).

另外,请记住,该LD_PRELOAD技巧仅挂钩C库函数,应用程序仍然可以自由执行"原始"系统调用(int 0x80在x86上,sysenter在x86_64上以及其他平台上的任何内容),而无需您注意.可靠地监视所有这些东西的最可靠的方法是使用(可怕的)ptrace接口(PTRACE_SYSCALL请求应该是你的朋友),顺便说一句,这是如何strace实现的.