Question
Intercept open syscall in C, when called via fopen
I'm trying to code up a (limited) in-memory file system redirects via libc/syscall interception and returning memfd_create
-produced file descriptors for the files that are virtualized. As a preparation step, I have a following test.c
file:
#define _GNU_SOURCE
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <dlfcn.h>
#include <sys/stat.h>
/*
FILE* fopen(const char *path, const char *mode)
{
typedef FILE* (*orig_fopen_func_type)(const char *path, const char *mode);
fprintf(stderr, "log_file_access_preload: fopen(\"%s\", \"%s\")\n", path, mode);
orig_fopen_func_type orig_func = (orig_fopen_func_type)dlsym(RTLD_NEXT, "fopen");
return orig_func(path, mode);
}
*/
int open(const char *path, int flags)
{
typedef int (*orig_func_type)(const char *pathname, int flags);
fprintf(stderr, "log_file_access_preload: open(\"%s\", %d)\n", path, flags);
orig_func_type orig_func = (orig_func_type)dlsym(RTLD_NEXT, "open");
return orig_func(path, flags);
}
int open64(const char *path, int flags)
{
typedef int (*orig_func_type)(const char *pathname, int flags);
fprintf(stderr, "log_file_access_preload: open64(\"%s\", %d)\n", path, flags);
orig_func_type orig_func = (orig_func_type)dlsym(RTLD_NEXT, "open64");
return orig_func(path, flags);
}
//TODO: int openat(int dirfd, const char *path, int flags, mode_t mode)
int openat(int dirfd, const char *path, int flags)
{
typedef int (*orig_func_type)(int dirfd, const char *pathname, int flags);
fprintf(stderr, "log_file_access_preload: openat(%d, \"%s\", %d)\n", dirfd, path, flags);
orig_func_type orig_func = (orig_func_type)dlsym(RTLD_NEXT, "openat");
return orig_func(dirfd, path, flags);
}
int main()
{
(void)fopen("test.txt", "r");
}
Compiling as gcc test.c
(on Ubuntu 22.04) and calling it as ./a.out
does not print anything (if I uncomment fopen
interception, then it works but unfortunately fmemopen
does not allow creating a corresponding fd
/fileno
with it, as opposed to shm_open
and memfd_create
so I prefer to only override open
calls)
If I put similar interception in LD_PRELOAD
ed shared library, it still does not get called for an open
call (again, it works if I intercept fopen
).
If I strace -f ./a.out
, then I do get openat(AT_FDCWD, "test.txt", O_RDONLY) = -1 ENOENT (No such file or directory)
on the output, so open
does get called?
execve("./a.out", ["./a.out"], 0x7ffff9b89518 /* 22 vars */) = 0
brk(NULL) = 0x7fffde75e000
arch_prctl(0x3001 /* ARCH_??? */, 0x7fffe5547b50) = -1 EINVAL (Invalid argument)
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fcee5410000
access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
newfstatat(3, "", {st_mode=S_IFREG|0644, st_size=103195, ...}, AT_EMPTY_PATH) = 0
mmap(NULL, 103195, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7fcee53b6000
close(3) = 0
openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0P\237\2\0\0\0\0\0"..., 832) = 832
pread64(3, "\6\0\0\0\4\0\0\0@\0\0\0\0\0\0\0@\0\0\0\0\0\0\0@\0\0\0\0\0\0\0"..., 784, 64) = 784
pread64(3, "\4\0\0\0 \0\0\0\5\0\0\0GNU\0\2\0\0\300\4\0\0\0\3\0\0\0\0\0\0\0"..., 48, 848) = 48
pread64(3, "\4\0\0\0\24\0\0\0\3\0\0\0GNU\0\244;\374\204(\337f#\315I\214\234\f\256\271\32"..., 68, 896) = 68
newfstatat(3, "", {st_mode=S_IFREG|0755, st_size=2216304, ...}, AT_EMPTY_PATH) = 0
pread64(3, "\6\0\0\0\4\0\0\0@\0\0\0\0\0\0\0@\0\0\0\0\0\0\0@\0\0\0\0\0\0\0"..., 784, 64) = 784
mmap(NULL, 2260560, PROT_READ, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7fcee5180000
mmap(0x7fcee51a8000, 1658880, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x28000) = 0x7fcee51a8000
mmap(0x7fcee533d000, 360448, PROT_READ, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1bd000) = 0x7fcee533d000
mmap(0x7fcee5395000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x214000) = 0x7fcee5395000
mmap(0x7fcee539b000, 52816, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7fcee539b000
close(3) = 0
mmap(NULL, 12288, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fcee53b0000
arch_prctl(ARCH_SET_FS, 0x7fcee53b0740) = 0
set_tid_address(0x7fcee53b0a10) = 5270
set_robust_list(0x7fcee53b0a20, 24) = 0
rseq(0x7fcee53b10e0, 0x20, 0, 0x53053053) = -1 ENOSYS (Function not implemented)
mprotect(0x7fcee5395000, 16384, PROT_READ) = 0
mprotect(0x7fcee5419000, 4096, PROT_READ) = 0
mprotect(0x7fcee5408000, 8192, PROT_READ) = 0
prlimit64(0, RLIMIT_STACK, NULL, {rlim_cur=8192*1024, rlim_max=8192*1024}) = 0
munmap(0x7fcee53b6000, 103195) = 0
getrandom("\x05\x04\x5c\x57\xcc\x25\x9c\x8e", 8, GRND_NONBLOCK) = 8
brk(NULL) = 0x7fffde75e000
brk(0x7fffde77f000) = 0x7fffde77f000
openat(AT_FDCWD, "test.txt", O_RDONLY) = -1 ENOENT (No such file or directory)
exit_group(0) = ?
+++ exited with 0 +++
Why can strace
see this call and my interception cannot? Is there a way to make this interception work?
Thanks!
P.S. At the end of the day, I'm looking to create some fd
for an in-memory buffer (maybe by memfd_create
and then writing to it my buffer and then seek
ing to 0).