Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

selectively intercept syscall for getrandom #301

Closed
dkg opened this issue Feb 24, 2021 · 7 comments
Closed

selectively intercept syscall for getrandom #301

dkg opened this issue Feb 24, 2021 · 7 comments

Comments

@dkg
Copy link
Collaborator

dkg commented Feb 24, 2021

for historical reasons, many programs still invoke syscall() directly instead of using the glibc wrapper around getrandom (e.g. openssl does this).

If the build environment for faketime can guess at what such a syscall invocation would look like, maybe it could selectively intercept syscall itself. If FAKERANDOM_SEED is set, it could inspect the syscall number, and if it matches __NR_getrandom then invoke the internal getrandom instead. otherwise, it could forward the syscall through to real_syscall.

I don't know whether any of this makes sense for platforms other than GNU/Linux, so i might initially limit any attempts to that platform.

There is some weird variadic argument handling stuff that i'm not sure my C knowledge is deep enough handle.

@wolfcw
Copy link
Owner

wolfcw commented Feb 24, 2021

Please see the discussion in #176 about this. There seems to be no way to pass-through "varargs" parameters in C, so intercepting syscall() was a no-go. I gave up on this in 2019, but if there meanwhile are new ways to address this problem, it'd be certainly interesting.

@dkg
Copy link
Collaborator Author

dkg commented Feb 24, 2021

thanks for the pointer to the previous discussion! this was exactly what i was worried about with the "weird variadic argument handling" remark. I'll do a bit of research and see if i can turn up any other options.

@dkg
Copy link
Collaborator Author

dkg commented Feb 24, 2021

discussion about syscall in rust was the best i could find. Over there, @cuviper wrote:

The callee libc::syscall doesn't really have types at all! It doesn't try to interpret anything, as the whole point is that it allows syscalls that aren't known to your libc. It's really just a shim that translates arguments to the kernel syscall ABI registers, and then captures errno on return.

It's hard to even puzzle this out from glibc sources, because all arches are implemented directly in assembly, but in musl you can see that it simply reads 6 unconditional syscall_arg_t (long) arguments from the va_list. I think that's where the idea came to cast them in #56779, since that mentions musl.

https://git.musl-libc.org/cgit/musl/tree/src/misc/syscall.c

I have no idea whether such a hack is safe to do, but i'll try it out and you can reject it if it looks too scary. I'll make it require -DINTERCEPT_SYSCALL during build as well.

@dkg
Copy link
Collaborator Author

dkg commented Feb 24, 2021

@wolfcw
Copy link
Owner

wolfcw commented Feb 24, 2021

Sounds interesting indeed. I had not looked at it on the ABI level yet since I considered it more of a conceptual problem in C that sometimes (?) is solved by providing a separate function, such as vsyscall for syscall as an alternative to pass va-parameters through. This second v-function was not available for syscall at the time, and it still seems to be this way.

The linked musl solution apparently turns any (variadic) number of arguments into a call with six parameters (besides the syscall number). This hopefully works if we have no syscalls with more than six parameters (seems to be the case according to the ABI discussion), and if all syscalls can handle excess parameters (like a syscall requiring only one parameter, but getting six of them), which also seems to be the case (at least the musl solution seems to rely on it).

My man page on va_arg() tells me:

If there is no next argument, or if type is not compatible with the type of the actual next argument (as promoted according to the default argument promotions), random errors will occur.

I'm not too happy about the prospect of "random errors" when an intercepted syscall() gets less than the full six parameters (which will likely be the case most of the time), but I guess it's worth trying it out. :-)

dkg added a commit to dkg/libfaketime that referenced this issue Feb 24, 2021
This is an attempt at an implementation to address wolfcw#301.

Some things worth noting:

 - I am not particularly confident in my reverse of the variadic C
   ABI. While the code appears to work for me on x86_64, I could
   imagine some variations between platforms that I'm not
   understanding.

 - This works to intercept the invocation of syscall as seen in
   test/syscalltest.sh, as long as it was compiled with -DFAKE_RANDOM

 - defining -DINTERCEPT_SYSCALL on non-Linux platforms should result
   in a compile-time error.

 - This does *not* work to intercept the syscall sent by `openssl
   rand`, for some reason I don't yet understand.  Perhaps openssl has
   some platform-specific syscall mechanism that doesn't route them
   through libc's syscall() shim?
@dkg dkg mentioned this issue Feb 24, 2021
@dkg
Copy link
Collaborator Author

dkg commented Feb 24, 2021

I suspect that what "random errors" means is "we'll read from otherwise uninitialized places on the stack". but if the code on the backend (what we're passing through to) only reads from the expected places on the stack, then i don't see what the problem would be.

This is definitely operating in a gray zone, though.

anyway, #302 seems to work OK for me.

@dkg
Copy link
Collaborator Author

dkg commented Feb 25, 2021

Given that #302 is merged, and we're now doing more with that syscall interception, i'm closing this ticket.

@dkg dkg closed this as completed Feb 25, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants