1

I am trying to intercept openat() calls with the following library comm.c. This is very standard minimal example, nothing special about it. I compile it with

>gcc -shared -Wall -fPIC -Wl,-init,init  comm.c -o comm.so

I am pasting this standard minimal example to show that, I thought, I knew what I was doing.

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

typedef int (*openat_type)(int, const char *, int, ...);

static openat_type g_orig_openat;

void init() {
    g_orig_openat = (openat_type)dlsym(RTLD_NEXT,"openat");
}

int openat(int dirfd, const char* pathname, int flags, ...) {
    int fd;
    va_list ap;

    if (flags & (O_CREAT)) {
            va_start(ap, flags);
            fd = g_orig_openat(dirfd, pathname, flags, va_arg(ap, mode_t));
        }
    else
        fd = g_orig_openat(dirfd, pathname, flags);

    printf("openat dirfd %d pathname %s\n", dirfd, pathname);

    return fd;
}

I am running a tar command, again a minimal example, untarring an archive containing a single file foobar, to a pre-existing subdirectory dir:

>strace -f tar xf foobar.tar -C dir 2>&1 | grep openat
openat(AT_FDCWD, "dir", O_RDONLY|O_NOCTTY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC) = 4
openat(4, "foobar", O_WRONLY|O_CREAT|O_EXCL|O_NOCTTY|O_NONBLOCK|O_CLOEXEC, 0600) = -1 EEXIST (File exists)
openat(4, "foobar", O_WRONLY|O_CREAT|O_EXCL|O_NOCTTY|O_NONBLOCK|O_CLOEXEC, 0600) = 5

However,

>LD_PRELOAD=./comm.so tar xf foobar.tar -C dir
openat dirfd 4 pathname foobar
openat dirfd 4 pathname foobar

OK, I know how to handle this - I have done this before - the reason for this kind of discrepancy, is that the system call openat() that is shown by strace is not done by the same-named user function openat(). To find out what that other user function is, one gets the sources, rebuilds them, and finds out.

So, I got the sources for my tar:

>$(which tar) --version
tar (GNU tar) 1.26

I got the tar 1.26 sources and rebuilt them myself, and, lo and behold, if I use the binary tar that I built, rather than the above installed one, then comm.so does catch all 3 openat calls!

So that means there is no "other user function".

Please help, what is possibly going on here??

NO, the question is not answered by that previous question. That previous answer simply said, the library call may be differently named, than the underlying system call. Here, that is NOT the case because I recompiled the same code myself, and there are no other library calls in there.

Mark Galeck
  • 6,155
  • 1
  • 28
  • 55
  • 2
    Possible duplicate of [intercepting the openat() system call for GNU tar](https://stackoverflow.com/questions/9161116/intercepting-the-openat-system-call-for-gnu-tar) – Michael Foukarakis Feb 07 '18 at 08:13
  • @MichaelFoukarakis no, thank you for the pointer, but the other question does not understand the difference between system calls and user calls, and how to diagnose them. I think I do - please see the edit to clarify my point. – Mark Galeck Feb 07 '18 at 09:22
  • The difference between system calls and library calls has nothing to do with this, as the answer in the linked question indicates. The fact you're attempting to intercept an [alias](https://sourceware.org/git/?p=glibc.git;a=blob;f=sysdeps/unix/sysv/linux/openat.c;h=9601e617dbd71d6474c1b28b255b706c20647b3e;hb=HEAD) does. I can put a complete answer together if you want. – Michael Foukarakis Feb 07 '18 at 10:02
  • Glibc may perform `openat` internally and it won't allow you to intercept those with `LD_PRELOAD` (calls in Glibc are not done through PLT). – yugr Feb 07 '18 at 10:06
  • @MichaelFoukarakis then why does it work when I recompile the same code by myself?? – Mark Galeck Feb 07 '18 at 16:26
  • @yugr can you elaborate, I have no idea what you are talking about. I tried to google-fu what you are saying and I came up with nothing of the sort. – Mark Galeck Feb 07 '18 at 16:55
  • Take a look e.g. at [this answer](https://stackoverflow.com/a/7789619/2170527), it mentions that calls to standard functions inside Glibc (e.g. calls to `open` in `dlopen`) are non-interposable. That's a well-known Glibc trick to achieve higher performance. – yugr Feb 07 '18 at 17:34
  • @yugr OK thank you yes I got it, but, can you just tell me, how come, when I recompile the same `tar` code myself, then it works... It's the same code, there is no other 1.26 version. – Mark Galeck Feb 07 '18 at 17:37
  • @MichaelFoukarakis thank you, yes if you can explain the `alias` thing a little, I would appreciate - but I really want to know, is why it works when I recompile the same `tar` code myself. – Mark Galeck Feb 07 '18 at 17:53
  • It might be that in case of `tar` all `openat`s happen to be in user code whereas for the program some are done by Glibc. – yugr Feb 08 '18 at 09:40
  • @yugr no, no you don't understand - if I use the `tar` binary that is installed on my machine, the interception does not work, but if I recompile THE SAME code myself, then it does work. It's the same code, same version of `tar`, uses precisely the same `openat` calls, aliases, or whatever. – Mark Galeck Feb 08 '18 at 11:25
  • That's a good question but harder to answer without details (e.g. symbolized gdb stacktraces, for old and new `tar`s, for openat call which is not instrumented in original version of `tar`). I wonder if this may be related to [symbol versioning](https://www.akkadia.org/drepper/symbol-versioning) i.e. `tar` uses older version of `openat`. – yugr Feb 08 '18 at 11:41

1 Answers1

1

According to the discussion mentioned, openat will probably be called by different symbol or function. The system call dumped by tool such as strace is raw system call. It might be wrapped by user function or glibc. If you want intercept it by LD_PRELOAD, you need to find out those wrapper instead of openat. To my experience, you can try intercept open64 or open, it can redirect to openat which you observe on strace.

The link is one example to wrap openat from open64.

Steven
  • 811
  • 4
  • 23