RFR 8224676: Java crash if VSYSCALL is missing

Vladimir Kempik vkempik at azul.com
Fri May 24 08:41:05 UTC 2019


Hello

I have made a simple reproducer and tested some linux distros and here are results:

distro kernel cpu gen result
ubuntu 12.04.  3.8.0-44-generic nehalem ok
ubuntu 14.04 3.13.0-24-generic core 2 duo ok
ubuntu 16.04 4.15.0-47-generic qemu ok
ubuntu 18.04 4.15.0-47-generic Opteron 63xx ok
Alpine 4.4.30-0-grsec qemu ok
Centos 5.8 2.6.18-308.el5 nehalem ok
Centos 6.4 2.6.32-358.el6 core 2 duo ok
RHEL 5.9 2.6.18-348.el5 nehalem ok
RHEL 6.4 2.6.32-358.el6 Opteron 6386 ok
Wind River 5.2 4.1.21-WR8.0.0.9 Opteron 63xx ok
SLES 10.3 2.6.16.60 qemu not ok

>I think the CPU index needs to be extracted from the number, because not all bits are CPU index..?

we cna apply this mask:
#define VGETCPU_CPU_MASK 0xfff


the code for __getcpu  comes directly from linux kernel
test.c:

#include <stdio.h>

#define GDT_ENTRY_PER_CPU 15 /* Abused to load per CPU data from
limit */
#define __PER_CPU_SEG (GDT_ENTRY_PER_CPU * 8 + 3)

static inline unsigned int __getcpu(void)
{
        unsigned int p;

        asm volatile ("lsl %1,%0" : "=r" (p) : "r" (__PER_CPU_SEG));

        return p;
}

int main(){
printf("cur cpu is %d\n", __getcpu());
}

Regards, Vladimir
23 мая 2019 г., в 20:16, Mikael Vidstedt <mikael.vidstedt at oracle.com<mailto:mikael.vidstedt at oracle.com>> написал(а):


Is this really correct? I think the CPU index needs to be extracted from the number, because not all bits are CPU index..?

Do we know that all relevant versions of linux support this? Is it always available on those versions?

Cheers,
Mikael

On May 23, 2019, at 8:23 AM, Vladimir Kempik <vkempik at azul.com<mailto:vkempik at azul.com>> wrote:

Hello,

Please review the following patch to address the issue:

Bug: https://bugs.openjdk.java.net/browse/JDK-8224676

Webrev: http://cr.openjdk.java.net/~vkempik/8224676/webrev.00/

Summary:
Java (and epecially ZGC) is using VSYSCALL linux kernel functionality to get cpu-id for a thread, basically to know on which cpu current thread is running.
A relevant peace of code:


int os::Linux::sched_getcpu_syscall(void) {
unsigned int cpu = 0;
int retval = -1;

#if defined(IA32)
#ifndef SYS_getcpu
  #define SYS_getcpu 318
#endif
retval = syscall(SYS_getcpu, &cpu, NULL, NULL);
#elif defined(AMD64)
// Unfortunately we have to bring all these macros here from vsyscall.h
// to be able to compile on old linuxes.
#define __NR_vgetcpu 2
#define VSYSCALL_START (-10UL << 20)
#define VSYSCALL_SIZE 1024
#define VSYSCALL_ADDR(vsyscall_nr) (VSYSCALL_START+VSYSCALL_SIZE*(vsyscall_nr))
typedef long (*vgetcpu_t)(unsigned int *cpu, unsigned int *node, unsigned long *tcache);
vgetcpu_t vgetcpu = (vgetcpu_t)VSYSCALL_ADDR(__NR_vgetcpu);
retval = vgetcpu(&cpu, NULL, NULL);
#endif


In x86 linux it’s going syscall way, which is a bit slow due to context switching.
on linux x86_64 it’s using VSYSCALL functionality, which is basically a page at fixed address which can execute 3 different syscalls (their analogues in user space) without context switch, in a very fast way.

VSYSCALL functionality is known to be insecure when ASLR is used, hence it’s being disabled sometimes.
For example in alpine linux it’s disabled by default and even its emulation (via a trap) by kernel is disabled. So one can’t use VSYSCALL at all and java crashes.

There is two ways to work it around: unify x86 and x86_64 routines and go syscall always (could be slow) or use functionality similar to vdso (it’s similar to VSYSCALL but the page is at different address on every app launch).

There is no low cost way to find out if VSYSCALL functionality is present  or not.
This patch remove VSYSCALL functionality and does perform simple asm instruction to get cpu id.
it’s very imilar to what happens in vgetcpu function in vdso: https://elixir.bootlin.com/linux/v4.4/source/arch/x86/include/asm/vgtod.h#L77

Regards, Vladimir



More information about the hotspot-runtime-dev mailing list