1

I am working on a dynamic language written in go. I am using C to allow this language to access syscalls (for linux at least). C's any type is a void*, so I thought that I can pass them into a syscall. I tested using some code which looks like:

#include <stdio.h>
#include <unistd.h>

static inline long int vpsyscall(void* a0, void* a1, void* a2, void* a3) {
    return syscall(*((int*)a0), a1, a2, a3);
}

int main() {
    int a_ = 1;
    void* a = &a_;
    int b_ = 1;
    void* b = &b_;
    char* c_ = "hello\n";
    void* c = &c_;
    int d_ = 6;
    void* d = &d_;
    long int ret = tusksyscall(a, b, c, d);
    printf("%ld\n", ret);
}

I expect that this code should output

hello
0

to the console.

Instead, I get

-1

which (at least to my knowledge) means error.

Strangely, when I pass 1 argument (the ax), it works

int _ax = 39;
void* ax = &_ax;
long int ret = vpsyscall(ax, 0, 0, 0);
printf("%ld\n", ret);

It gives me the desired output (in this case, just the pid). So, whats going on here, and how can I pass void pointers to a syscall (if there is any way)?

Ank i zle
  • 2,089
  • 3
  • 14
  • 36
  • The syscall is presumably supposed to take integer and pointer arguments directly, but you are passing pointers to integers and pointers to pointers respectively. It's the same reason that `puts(c)` wouldn't print "Hello". If you insist on passing `void *` then you should be casting your pointers and integers to that type, not taking their addresses. – Nate Eldredge Sep 27 '20 at 01:53
  • It may help to think about why `syscall(1, 1, "hello\n", 6)` would work, and why that is not what your `vpsyscall` function is doing. It's really nothing to do with system calls per se; the issue would be the same if you replaced `syscall` with any other function. It's just about the distinction between an object and a pointer to that object, which you are mixing up. – Nate Eldredge Sep 27 '20 at 01:56
  • If the void pointer will not work, will any other solution (that allows for unkown types) work? – Ank i zle Sep 27 '20 at 02:08

1 Answers1

2

The Linux system call interface only takes values which are either pointers, or integers that are the same size as a pointer and can be safely cast to them. But you are not passing such objects, you are passing pointers to them. So for instance, instead of asking the OS to write to file descriptor 1, you're asking it to write to file descriptor 0x7fffdeadbeef or whatever the address of the local variable b_ happens to be. Clearly this will not succeed.

You need to cast instead of taking the address with the & operator.

You also might as well make the first argument of type int since you know that's what it will always be.

Try


static inline long int vpsyscall(int a0, void* a1, void* a2, void* a3) {
    return syscall(a0, a1, a2, a3);
}


int main() {
    vpsyscall(1, (void *)1, "hello", (void *)6);
}

I think you're misled by thinking of void * as an "any" type. It can be a pointer to any object, but it's not interchangeable with that object. To get the object back, you have to cast your pointer to the type of the object and dereference it. That's what you did with the first argument to your vpsyscall, which is why it worked, even though it was unnecessarily complicated.

Nate Eldredge
  • 48,811
  • 6
  • 54
  • 82