How to use syscall to change vDSO content

249 Views Asked by At

I am trying to implement a syscall on x86 that is able to modify the content of vDSO. Given vDSO content is read-only for userspace, I believe a syscall is needed. Otherwise it will cause segmentation fault. However, I can't do it successfully at this moment.

My plan:

syscall in kernel/sys.c

extern struct vdso_data _vdso_data[CS_BASES] __attribute__((visibility("hidden")));
SYSCALL_DEFINE1(foo, int, num) {
    struct vdso_data *vdata = _vdso_data;
    vdata[0].__unused = num;
    return 0;
}

The syscall will read the _vdso_data which is declared in arch/x86/include/asm/vvar.h and defined in arch/x86/include/asm/vdso/vsyscall.h. Then it will modify the variable __unused into some number input by the user.

vDSO in arch/x86/entry/vdso/vclock_gettime.c

#include <asm/vdso/vsyscall.h>
notrace int __vdso_query()
{
    struct vdso_data *vdata = __arch_get_k_vdso_data();
    return vdata[0].__unused;
}

I know it's a bad idea to define something unrelated to time in a timing fucntion, but this is what I'm testing right now. The __vdso_query() function will read the data and return the value of the __unused variable.

Testing plan

#define _GNU_SOURCE
#include <unistd.h>
#include <sys/syscall.h>
#include <stdio.h>
#define SYS_foo 548
extern int __vdso_query();
int main(int argc, char **argv)
{
  int before = __vdso_query();
  printf("Before syscall: %d\n", before);

  int num = 10;

  long res = syscall(SYS_foo, num);

  int after = __vdso_query();
  printf("After syscall: %d\n", after);
  return res;
}

Expected results

I want to see 0 before the syscall and 10 after the syscall returned by __vdso_query() since the syscall will change the _vdso_data[0].__unused if everything follows the plan.

Current results

For now, the content is always 0 regardless of the syscall.

Questions

How to access _vdso_data properly

There are 2 ways I tried to access the _vdso_data: __arch_sync_vdso_data() and extern.

  1. When I tried to use __arch_get_k_vdso_data function where defined in asm/vdso/vsyscall.h to access the _vdso_data, I encounter some variable redefinition errors. If I include vsyscall.h in both kernel/sys.c and arch/x86/entry/vdso/vclock_gettime.c, it seems DEFINE_VVAR(struct vdso_data, _vdso_data); will be called twice and leads to redefinition error of _vdso_data. I tried to put #ifndef, #def, and #endif macros around it but the error still exists.
  2. Another thing I tried is only declaring the _vdso_data as extern variable shown in the syscall implementation. The redefinition error is gone if I do this. The code compiles and runs properly without error or segmentation fault, but the result doesn't change.

__arch_sync_vdso_data() function

I read some example usages in the kernel source code a little bit and I find people use __arch_sync_vdso_data function to write the _vdso_data in the end. However, I think this function is actually empty in the generic-asm and the only definition I can find is on Arm. Not really sure how this will work on x86 and is it needed.

It's really hard to find vdso resources online and I've read all the recent posts. I would really appreciate the help and thanks in advance.

1

There are 1 best solutions below

0
On

It turns out syscall is doing the right work and the problem is how to access the vDSO data.

In fact, __arch_get_vdso_data is the right function to access vDSO data. It returns a pointer which can be further dereferenced to access the data like __arch_get_vdso_data()->__unused.

On the other hand, __arch_get_k_vdso_data never works and I suspect the _k_ in the function name indicates that this function can be only used in kernel space.