
Syscall Call-From Verification - cnst
https://marc.info/?l=openbsd-tech&m=157488907117170&w=2
======
jchw
My only question: does making Go use libc stubs require Cgo? One property of
Go that is really nice is that you can cross compile effortlessly without
needing to build a cross compilation toolchain. Cgo negates this because it
requires a C toolchain for the target...

~~~
Gaelan
IIRC macOS only supports syscalls via dynamically linked libc
(libsystem)—there's no guarantee of a stable ABI for direct syscalls. Assuming
go uses libsystem on macOS, is should be able to do something similar for BSD.

~~~
jchw
I guess it actually should be doable. In theory, all it has to do is link to
the correct symbols in libc.so (or the respective library.) That doesn't seem
like it would require a C compiler, so as long as the ABI is guaranteed to be
stable, and the Go linker is capable of handling it.

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.)

~~~
clarry
OpenBSD binaries in general don't have a long shelf life. They will break ABI
when they want to move forward.

------
Reelin
The description says to program to the API instead of the ABI. I'm not very
familiar with low level work and exploits, but aren't syscalls the API for the
kernel itself?

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](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?

~~~
mike_hock
syscall numbers are constant (part of the ABI!) regardless of the kernel's
address space layout.

Conversely, addresses of libc functions or any other code in the program are
randomized at load time, so are unpredictable to an attacker.

~~~
Reelin
I don't usually think of integer constants passed as an argument to a function
as an ABI, but given that they're hard coded at kernel compile time, fair
enough.

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](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.

~~~
clarry
> What I'm struggling to understand is why an additional layer of indirection
> is required to facilitate randomization in this case.

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](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.

~~~
Reelin
After a bit more reading, it seems I hadn't realized just how low level direct
system calls actually are
([https://en.wikibooks.org/wiki/X86_Assembly/Interfacing_with_...](https://en.wikibooks.org/wiki/X86_Assembly/Interfacing_with_Linux)).
Apparently it's either a software interrupt or dedicated instruction, with the
constant in a register. ASLR requires more abstraction than that (obviously),
and there's no point to it if an exploit can just use the front door instead.

------
jeffrallen
Damn, OpenBSD is good.

------
notaplumber
From my dupe submission:
[https://news.ycombinator.com/item?id=21653796](https://news.ycombinator.com/item?id=21653796)

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.

[https://www.openbsd.org/innovations.html](https://www.openbsd.org/innovations.html)

------
basementcat
Looks like this may break some JIT's? Or is the workaround to have the JIT'ed
code call libc instead of making direct syscalls?

~~~
jchw
Hmm. Do any JITs actually emit syscalls directly? I would’ve guessed most JITs
won’t even emit direct libc calls directly, since there tends to be layers of
abstraction between it and the software.

~~~
pm215
QEMU does for its user-mode-only code, but not in JITted code. We have a
little shim that directly executes a syscall instruction because we want to be
able to wind the PC forward/backward across it to avoid a race condition with
a signal arriving after the guest thinks it's made the syscall but before
we've made the syscall in the host. But that's currently only done by linux-
user; bsd-user should ideally have a similar mechanism but is basically
dead/dying for lack of maintainers who care about the BSDs. In any case, that
falls under "in the binary's text segment" rather than JIT output.

(code at [https://git.qemu.org/?p=qemu.git;a=blob;f=linux-
user/host/x8...](https://git.qemu.org/?p=qemu.git;a=blob;f=linux-
user/host/x86_64/safe-
syscall.inc.S;h=f36992daa34e0f59c621d506bdda00e281e437be;hb=HEAD) )

------
jacobush
Defense in depth, this is not unbreakable, but it adds another layer of
difficulty an attacker must bridge.

