Skip to content
Snippets Groups Projects
Closed Disable RDTSC/RDTSCP to limit side-channel attacks
  • View options
  • Disable RDTSC/RDTSCP to limit side-channel attacks

  • View options
  • Closed Issue created by cypherpunks

    //TL;DR If you don't use vDSO, you can disable the TSC instructions without causing segfaults.//

    Side-channel attacks like FLUSH+FLUSH, FLUSH+RELOAD, PRIME+PROBE, and EVICT+TIME can be used for covert information exfiltration, cross-process and cross-VM keylogging, breaking poor crypto implementations (especially JS-based crypto), and massively enhancing rowhammer attacks by enumerating the physical row layout and physical memory layout, improving a generic attack of 2 flips in 10 minutes to 30 flips per second on some hardware. These attacks all require a high-precision timestamp counter instruction to function, and don't involve making syscalls, so seccomp does not defend against them. They have the same keylogging effects which grsecurity's /dev/ptmx anti-side channel technique was designed to protect against, so it bad news.

    This is even worse for Tor Browser users, as in addition to limited keylogging abilities, it allows for extensive cross-process (the user doesn't need to be using the keyboard or mouse in the compromise Firefox process) biometric fingerprinting via keystroke dynamics in a way that's not practical using JavaScript's performance.now() or whatever it does. That function has a resolution in the hundreds of nanoseconds even with JIT enabled (and I hope your sandboxed browser will disable JIT), compared to RDTSC which tends to be sub-nanosecond, making it fast enough to be cross-process, and cross-VM (everything but cross-NUMA node). On the bright side, x86 CPUs allow making this instruction available to ring 0 only via CR4.TSD (the timestamp disable bit in the 4th control register), and the Linux kernel exposes an interface to allow userland to toggle this on a per-process basis using prctl().

    Example usage, in C:

    #include <sys/prctl.h>
    #include <stdlib.h>
    #include <stdio.h>
    
    /* disable the TSC */
    if (prctl(PR_SET_TSC, PR_TSC_SIGSEGV, 0, 0, 0) == -1)
        exit(1);
    
    /* enable the TSC */
    if (prctl(PR_SET_TSC, PR_SET_ENABLE, 0, 0, 0) == -1)
        exit(1);
    
    /* check the TSC state */
    int tsc;
    if (prctl(PR_GET_TSC, &tsc, 0, 0, 0) == -1)
        exit(1);
    
    printf("The TSC is %s for this process\n", tsc ? "enabled" : "disabled");

    With this in mind, it should be important to consider disabling the TSC in Tor Browser using the sandbox, and using seccomp to prevent it from re-enabling it with prctl(). However, as a lot of people who have tried this have seen, many glibc processes instantly segfault when this is done. The reason this happens is that a few syscalls, namely getcpu(), gettimeofday(), time(), and clock_gettime() use the RDTSC instruction because, on x86, they are not a syscall, but a vDSO, meaning the syscalls take place entirely in userspace (though a small dynamically updating read-only bit of memory, called the VVAR, is created by the kernel for the vDSO to poll), without requiring a context switch at all, in order to improve performance. Unfortunately, some of those calls with some arguments end up using RDTSC in userspace, triggering a SIGSEGV. See vdso(7) for more information on how this works, and how glibc is involved. The solution is to wrap gettimeofday(), clock_gettime() (either entirely, or just some of its arguments), and time(), and force them to use their true syscall equivalents. If they go through their glibc functions, they'll use the vDSO mapping, and occur in userspace.

    This fix should be very simple, as long as Firefox does not manually use a TSC instruction in asm in its code by itself (in which case you'd have to patch around that). If it doesn't do that, then all you'd have to do is wrap a few functions like so:

    #include <unistd.h>
    #include <sys/syscall.h>
    #include <sys/time.h>
    #include <time.h>
    
    int gettimeofday(struct timeval *tv, struct timezone *tz)
    {
        return syscall(SYS_gettimeofday, tv, tz);
    }
    
    int clock_gettime(clockid_t clk_id, struct timespec *tp)
    {
        return syscall(SYS_clock_gettime, clk_id, tp);
    }
    
    time_t time(time_t *tloc)
    {
        return syscall(SYS_time, tloc);
    }

    If Firefox tries to call any of these syscalls from glibc, hooking it using LD_PRELOAD or so with these functions should prevent it from deferring to userspace execution as a vDSO, and instead use a true syscall.

    On a slight tangent, given that this would disable so many of the vDSOs that exist, in the long-term it might be a good idea to look into a way to completely disable all access to the vDSO and VVAR, if it's possible to do that on a per-process basis (it also seems that modern x86_64 kernels cannot have it disabled globally by adding vdso=0 to the kernel boot parameters either. Despite what the documentation says, the code says otherwise). vDSOs have been abused in combination with other exploit primitives for sandbox escapes and privesc. For example CVE-2016-5195 could escape sandboxes by overwriting the vDSO and waiting for a process outside the sandbox to call it. Other vulnerabilities that lead to turning read-only pages writable would lead to that issue as well, even if mapping sensitive files like /etc/passwd or /bin/su isn't possible due to a strict sandbox. I'm not entirely sure if simply preventing vDSO and VVAR from being loaded is sufficient, but if so, then disabling it entirely would be an even better solution (and glibc will gracefully fall back to using native syscalls if it detects this). But in the meantime, hooking the glibc functions and using syscall() to call the native syscalls works fine for the purpose of safely disabling RDTSC and RDTSCP.

    Oh, and another note. vDSO calls bypass seccomp (and strace) by nature, so if you disable vDSO, you'll suddenly see a couple syscalls that previously didn't seem to exist, so unless you have whitelisted things like time() and gettimeofday(), you may see new violations that you didn't see before.

    As for anyone still using emulated, or god forbid, native vsyscalls who haven't moved onto vDSO, screw them. A sandbox won't do much to protect them anyway until they upgrade to a modern kernel.

    Information about vDSO: http://blog.tinola.com/?e=5 https://www.linuxjournal.com/content/creating-vdso-colonels-other-chicken https://0xax.gitbooks.io/linux-insides/content/SysCall/syscall-3.html http://man7.org/linux/man-pages/man7/vdso.7.html http://lxr.free-electrons.com/source/arch/x86/entry/vdso/

    Information about side-channel attacks: https://www.tau.ac.il/~tromer/papers/cache-joc-20090619.pdf (EVICT+TIME and PRIME+PROBE) https://eprint.iacr.org/2013/448.pdf (FLUSH+RELOAD) https://gruss.cc/files/flushflush.pdf (FLUSH+FLUSH)

    Information about keystroke dynamics: https://www.cs.cmu.edu/~keystroke/KillourhyMaxion09.pdf https://www.ncbi.nlm.nih.gov/pmc/articles/PMC3835878/ https://people.eecs.berkeley.edu/~daw/papers/ssh-use01.pdf

    Information about the TSC and disabling it: https://en.wikipedia.org/wiki/Control_register#CR4 http://x86.renejeschke.de/html/file_module_x86_id_278.html http://man7.org/linux/man-pages/man2/prctl.2.html

    Linked items ... 0

  • Activity

    • All activity
    • Comments only
    • History only
    • Newest first
    • Oldest first
    Loading Loading Loading Loading Loading Loading Loading Loading Loading Loading