Syscall Call-From Verification
marc.info
marc.info
Though one unfortunate aspect of this is even despite that, once they DO flip the switch, all old Go binaries will stop working.
(Though really, Go binaries don't have a long shelf life, because you have to recompile them when new security patches are released anyway. So maybe that is a non-issue.)
As far as I'm able to understand, it seems like the proposed mitigation blocks access to the kernel unless your code is either preauthorized (msyscall) or goes through a layer of indirection (libc) that undergoes randomized re-linking at boot. That seems to make sense to me, since it significantly reduces an adversary's knowledge about internals they are presumably targeting.
A few questions:
* Is it possible to authorize arbitrary code, or is access to msyscall (via libc or otherwise) restricted outside of boot?
* It seems that the kernel itself also undergoes randomized re-linking at boot (https://www.openbsd.org/innovations.html). So what does forcing everything through libc gain us?
* Is equivalent hardening likely to make it to the Linux world in the foreseeable future?
* What sort of attacks is this likely to prevent in practice?
* What have I misunderstood, and would someone mind explaining?
Conversely, addresses of libc functions or any other code in the program are randomized at load time, so are unpredictable to an attacker.
What I'm struggling to understand is why an additional layer of indirection is required to facilitate randomization in this case. It seems I must have a fundamental misunderstanding of how some part of the system works at this low level.
I'm also wondering what (if anything) is being lost to this mitigation - the syscall(2) manpage (http://man7.org/linux/man-pages/man2/syscall.2.html) seems to imply that not all system calls necessarily have matching wrapper functions in the platform's C library.
You can't randomize the integer constants without rebuilding the kernel and everything that depends on those constants. It would be very inconvenient, and the amount of randomization would likely be limited by the number of syscalls (because you want to pack them tight in a lookup table instead of having sparse numbers that are expensive to look up). If the number of syscalls is known, randomization can at best make you call the wrong one.
By contrast, address randomization means the program will probably crash (instead of executing some random syscall) unless it can figure out the address of the function it wants. Randomizing addresses can be done on the fly in the runtime linker, without rebuilding binaries. Additional randomness can be introduced by relinking binaries with a randomizing linker, which is much simpler than running a full build.
> I'm also wondering what (if anything) is being lost to this mitigation - the syscall(2) manpage (http://man7.org/linux/man-pages/man2/syscall.2.html) seems to imply that not all system calls necessarily have matching wrapper functions in the platform's C library.
syscall(2) lives in libc.
mov rax, <syscall number>
; Depending on the syscall, it expects its parameters
; in various other registers
syscall ; this is an actual instruction
So yes, it is very much part of the ABI. It selects which function to call.The extra indirection, AFAIK, is needed because you couldn't just call a kernel function from userspace and have it run with kernel privileges. That's what the syscall instruction is for, to put the processor back in ring 0 so the syscall handler runs in kernel space.
As for what's lost, well, for example, a sane language-agnostic kernel ABI is lost to a C-centric libc API. I don't know if the syscall wrapper support in libc is as incomplete on BSD as it is on Linux, but they could add the missing wrappers, of course.
A cleaner approach might have been to have a separate libsyscall that only wraps syscalls and is mapped into the "blessed" address space that is allowed to issue syscalls. But then libc would have to wrap the wrappers, and they probably didn't want that extra indirection.
It looks like there are restrictions if the binary is statically-linked, but I didn’t see anything else. You could probably pledge out of it.
> It seems that the kernel itself also undergoes randomized re-linking at boot (https://www.openbsd.org/innovations.html). So what does forcing everything through libc gain us?
This doesn’t protect against attacks on the kernel (which randomizing the kernel object files protects against); exploits at this stage are trying to spawn a shell (or similar) in userspace. Going through libc means you need to 1. find it and 2. pass whatever checks it has in place to get it to make the syscall.
> Is equivalent hardening likely to make it to the Linux world in the foreseeable future?
Parts of it, maybe. But turning this on by default would break applications.
> What sort of attacks is this likely to prevent in practice?
JIT code that has maliciously crafted to call execve(“/bin/sh”, …)
No. The kernel doesn't call syscalls itself. It provides them for applications.
Just to clarify a potential misunderstanding. The kernel and userspace run at different "hardware" privilege levels (ring0<=>ring3). Userspace applications cannot simply call the kernel API directly, they must "trap" into the kernel to perform some pre-defined service request, aka. System calls.
There are usually a few hundred or more of these, like e.g. read(2)/write(2). They are also generally exposed via libc wrappers (but not always) or via the indirect syscall(3) function. But it is also technically possible to directly encode the trap instruction, for example on x86 "int $0x80" or amd64 "syscall" with the desired syscall index. This is the part OpenBSD is proposing to lock down. The low-level syscall ABI has always been unstable, meaning it can be changed incompatibility, and libc is the more-stable interface applications should be using.
With that out of the way..
> Is it possible to authorize arbitrary code, or is access to msyscall (via libc or otherwise) restricted outside of boot?
msyscall(2) can only be called /once/ per-process, e.g. by the ld.so dynamic linker. This is used to indicate where libc is mapped (libraries are mapped randomly) to the kernel so it can permit that area to make syscalls.
This new proposed mitigation builds upon other work, such as libc/ld.so random re-linking at boot, and opportunistic enforcement of syscalls from only un-writable pages by default.
(code at https://git.qemu.org/?p=qemu.git;a=blob;f=linux-user/host/x8... )
https://marc.info/?l=openbsd-cvs&m=155942895309114&w=2
This means it's forced to use the libc stubs for syscalls. Any direct syscall from a JIT is suspect.