8

I'm working on a piece of software that monitors other processes' system calls using ptrace(2). Unfortunately most modern operating system implement some kind of fast user-mode syscalls that are called vsyscalls in Linux.

Is there any way to disable the use of vsyscalls/vDSO for a single process or, if that is not possible, for the whole operating system?

Michael
  • 8,920
  • 3
  • 38
  • 56

5 Answers5

7

Try echo 0 > /proc/sys/kernel/vsyscall64

If you're trying to ptrace on gettimeofday calls and they aren't showing up, what time source is the system using (pmtimer, acpi, tsc, hpet, etc). I wonder if you'd humor me by trying to force your timer to something older like pmtimer. It's possible one of the many gtod timer specific optimizations is causing your ptrace calls to be avoided, even with vsyscall set to zero.

Michael
  • 8,920
  • 3
  • 38
  • 56
Jeremiah Gowdy
  • 5,476
  • 3
  • 21
  • 33
  • 1
    Tried it, does not seem to work, I still don't see time/gettimeofday syscalls. I also tried the `vdso=0` boot option without success. – Michael Nov 26 '11 at 17:02
  • @Michael have you looked at the source of gettimeofday, seen if it honors the vsyscall flag? I'm going to look into it myself tonight. – Jeremiah Gowdy Nov 27 '11 at 01:23
  • OK, I *can* see the `gettimeofday` syscall with vsyscall64=0, but I still don't see the `time` syscall. – Michael Nov 27 '11 at 12:57
  • Must have been some other issue, you were right, I can see all syscalls with vsyscall64=0. – Michael Nov 27 '11 at 13:03
  • For a way to avoid vDSO calls for a specific process without system-wide changes, see my answer: https://stackoverflow.com/a/52402306/10126273 – Tenders McChiken Jan 07 '19 at 08:52
  • This doesn't work in newer kernels as this tunable was removed in 2011. – BeeOnRope Feb 12 '20 at 04:25
4

Is there any way to disable the use of vsyscalls/vDSO for a single process or, if that is not possible, for the whole operating system?

It turns out there IS a way to effectively disable linking vDSO for a single process without disabling it system-wide using ptrace!

All you have to do is to stop the traced process before it returns from execve and remove the AT_SYSINFO_EHDR entry from the auxiliary vector (which comes directly after environment variables along the memory region pointed to in rsp). PTRACE_EVENT_EXEC is a good place to do this.

AT_SYSINFO_EHDR is what the kernel uses to tell the system linker where vDSO is mapped in the process's address space. If this entry is not present, ld seems to act as if the system hasn't mapped a vDSO.

Note that this doesn't somehow unmap the vDSO from your processes memory, it merely ignores it when linking other shared libraries. A malicious program will still be able to interact with it if the author really wanted to.

I know this answer is a bit late, but I hope this information will spare some poor soul a headache

Tenders McChiken
  • 1,216
  • 13
  • 21
  • This requires that the process (for which you want to disable vdso) runs under some wrapper executable/script, right? Can you please post a source code for such executable or script (or post a link to it) in case you have one? – Andreas Kaufmann Feb 13 '20 at 11:08
3

For newer systems echo 0 > /proc/sys/kernel/vsyscall64 might not work. In Ubuntu 16.04 vDSO can be disabled system-wide by adding the kernel parameter vdso=0 in /etc/default/grub under the parameter: GRUB_CMDLINE_LINUX_DEFAULT.

IMPORTANT: Parameter GRUB_CMDLINE_LINUX_DEFAULT might be overwriten by other configuration files in /etc/default/grub.d/..., so double check when to add your custom configuration.

Anastasios Andronidis
  • 6,310
  • 4
  • 30
  • 53
  • 1
    For a way to avoid vDSO calls for a specific process without system-wide changes, see my answer: https://stackoverflow.com/a/52402306/10126273 – Tenders McChiken Jan 07 '19 at 08:50
2

Picking up on Tenders McChiken's approach, I did create a wrapper that disables vDSO for an arbitrary binary, without affecting the rest of the system: https://github.com/danteu/novdso

The general procedure is quite simple:

  1. use ptrace to wait for return from execve(2)
  2. find the address of the auxvector
  3. overwrite the AT_SYSINFO_EHDR entry with AT_IGNORE, telling the application to ignore the following value
David Buck
  • 3,752
  • 35
  • 31
  • 35
danteu
  • 21
  • 1
0

I know this is an older question, but nobody has mentioned a third useful way of disabling the vDSO on a per-process basis. You can overwrite the libc functions with your own that performs the actual system call using LD_PRELOAD.

A simple shared library for overriding the gettimeofday and time functions, for example, could look like this:

vdso_override.c:

#include <time.h>
#include <sys/time.h>
#include <unistd.h>
#include <sys/syscall.h>

int gettimeofday(struct timeval *restrict tv, struct timezone *restrict tz)
{
    return syscall(__NR_gettimeofday, (long)tv, (long)tz, 0, 0, 0, 0);
}

time_t time(time_t *tloc)
{
    return syscall(__NR_time, (long)tloc, 0, 0, 0, 0, 0);
}

This uses the libc wrapper to issue a raw system call (see syscall(2)), so the vDSO is circumvented. You would have to overwrite all system calls that the vDSO exports on your architecture in this way (listed at vdso(7)).

Compile with

gcc -fpic -shared -o vdso_override.so vdso_override.c

Then run any program in which you want to disable VDSO calls as follows:

LD_PRELOAD=./vdso_override.so <some program>

This of course only works if the program you are running is not actively trying to circumvent this. While you can override a symbol using LD_PRELOAD, if the target program really wants to, there is a way to find the original symbol and use that instead.

anroesti
  • 11,053
  • 3
  • 22
  • 33