[HN Gopher] OpenBSD: Removing syscall(2) from libc and kernel
       ___________________________________________________________________
        
       OpenBSD: Removing syscall(2) from libc and kernel
        
       Author : ecliptik
       Score  : 106 points
       Date   : 2023-10-27 15:23 UTC (7 hours ago)
        
 (HTM) web link (marc.info)
 (TXT) w3m dump (marc.info)
        
       | brynet wrote:
       | Follow-up mail from Theo showing what the full removal looks
       | like.
       | 
       | https://marc.info/?l=openbsd-tech&m=169842095809570&w=2
        
       | __turbobrew__ wrote:
       | It looks like golang is going to have to deal with -- again --
       | OpenBSD treating libc as the interface with the kernel instead of
       | syscalls being the interface with the kernel.
       | 
       | I wonder if there could be some way to sign a dynamic library to
       | allow it to create direct system calls and then pass that as a
       | kernel command line argument at boot?
        
         | CHY872 wrote:
         | OpenBSD's position is far from unique. It's shared with MacOS
         | (and iOS).
        
           | steveklabnik wrote:
           | And Windows, in a sense: not that libc is the interface, but
           | that the assembly-level API is _not_ the interface.
        
             | pjmlp wrote:
             | And almost every other OS still in use today, what Linux
             | does is reminiscent from OSes like CP/M and MS-DOS.
        
               | monocasa wrote:
               | And seL4.
        
           | masklinn wrote:
           | And windows.
           | 
           | And that's just the platforms which technically enforce it.
           | Linux is essentially the only platform which actually
           | supports raw syscalls, in the sense that it's considered a
           | normal system API.
        
         | irdc wrote:
         | > I wonder if there could be some way to sign a dynamic library
         | to allow it to create direct system calls and then pass that as
         | a kernel command line argument at boot?
         | 
         | That sounds like an additional knob to tweak. The OpenBSD
         | project is famously opposed to such knobs.
        
         | matheusmoreira wrote:
         | OpenBSD is certainly taking it quite far. They want to disallow
         | system calls from all non-libc code segments so that only
         | whitelisted code can interface with it.
         | 
         | It is not the only operating system in the "unstable kernel
         | interface" group though. Linux is actually the only one with a
         | stable system call interface.
         | 
         | I've written somewhat at length about this:
         | 
         | https://www.matheusmoreira.com/articles/linux-system-calls
        
           | masklinn wrote:
           | FWIW your website is unusable on desktop: the font is
           | unreadably huge. And while Firefox's reader mode does seem to
           | mitigate the issue correctly, Safari's cuts off at the second
           | paragraph.
        
             | matheusmoreira wrote:
             | Thank you. I'll try to fix it. I gotta get better at this.
        
           | LegionMammal978 wrote:
           | > It is not the only operating system in the "unstable kernel
           | interface" group though. Linux is actually the only one with
           | a stable system call interface.
           | 
           | It's wrong to say that Linux stands alone in having a stable
           | syscall interface. FreeBSD [0] and NetBSD [1] both retain
           | syscall compatibility for old binaries (the former apparently
           | with some exceptions permitted); DragonFly BSD also appears
           | to keep old syscalls in place. In fact, I only know of
           | Windows, OpenBSD, and presumably macOS as mainstream desktop
           | OSes without mostly-stable syscalls.
           | 
           | [0]
           | https://wiki.freebsd.org/AddingSyscalls#Backward_compatibily
           | 
           | [1] https://www.netbsd.org/docs/internals/en/chap-
           | processes.html...
        
           | KRAKRISMOTT wrote:
           | We need more fat kernels and less libcs, libc ties us
           | forcibly to the C legacy. Time for more type safe and richer
           | interfaces with the kernel.
        
             | trealira wrote:
             | What do you mean when you say "fat kernel"?
             | 
             | Also, FYI, OpenBSD is never going to stop being written in
             | C, and is never going to introduce a language like Rust
             | into the kernel [1], so there's little point in wishing for
             | this.
             | 
             | If you wish to rid yourself of legacy of C (and therefore
             | that of Unix), then OpenBSD, which is Unix (or derived from
             | it) and will always be written in C, is not a good
             | operating system for you.
             | 
             | Edit: Changed to be less rude; it wasn't my intention to be
             | rude.
             | 
             | [1]: https://marc.info/?t=151233221700001&r=1&w=2
        
         | masklinn wrote:
         | > It looks like golang is going to have to deal with -- again
         | -- OpenBSD treating libc as the interface with the kernel
         | instead of syscalls being the interface with the kernel.
         | 
         | Something they wouldn't have to do if they'd heeded the
         | warnings they got since they first started going raw syscalls
         | on non linux systems.
         | 
         | But as usual, go is uniquely american, only doing the right
         | thing after it has tried everything else.
        
           | PrimeMcFly wrote:
           | > go is uniquely american, doing the right thing after it has
           | tried everything else.
           | 
           | That doesn't seem like anything common to an American way of
           | doing things. What an odd statement.
        
             | kstrauser wrote:
             | It's a play on a quote often attributed to Winston
             | Churchill:
             | https://quoteinvestigator.com/2012/11/11/exhaust-
             | alternative...
        
               | arp242 wrote:
               | Bizarre nationalistic "they're doing it because they're
               | Americans" rantings are still not okay even if they're a
               | "play on a quote". I can "play" with all sorts of quotes
               | to say all sorts of idiotic things.
        
               | kstrauser wrote:
               | I guess. I'm proudly American and I thought it was funny,
               | but everyone has different tolerances.
        
           | tadfisher wrote:
           | You could also describe it as doing the sane thing (avoiding
           | hardcoding against the libc ABI through FFI) until the only
           | sane option is removed. Did you know many libc APIs are
           | preprocessor macros?
        
             | masklinn wrote:
             | > You could also describe it as doing the sane thing
             | (avoiding hardcoding against the libc ABI through FFI)
             | 
             | No, you could not.
             | 
             | On pretty much every system but linux the libc (or
             | equivalent) is the officially supported API to the kernel.
             | Bypassing it is not supported and thus definitionally can
             | not be sane, that's like declaring that doing the sane
             | thing is avoiding hardcoding against the front door and
             | entering your home through the roof instead. The front door
             | is what you're supposed to use, do hardcode against it.
             | Same with libc.
             | 
             | And Go has been going "ouch" then putting its fingers
             | straight back in the socket from the start, as it keeps
             | trying to work around libcs on all the platform where it is
             | not supported.
        
               | dfox wrote:
               | On linux the stable API and ABI that you are supposed to
               | use is also just libc. But due to various reasons people
               | think that the libc ABI is less stable than how stable it
               | really is.
               | 
               | Edit: NT with its split OS API dll and separate libc is
               | its own can of worms. You can have multiple instances of
               | libc in the same address space, which among other things
               | implies that you cannot safely malloc() in one module and
               | free() in another, and don't even think about passing
               | FILE* around.
        
               | yakubin wrote:
               | Linux doesn't have an official libc. Raw syscalls are its
               | only official API.
        
           | FullyFunctional wrote:
           | I don't use Go, but I had the exact same reaction; C is the
           | source of most of the problems and this is just codifies the
           | use of C. The whole thing has a security by obscurity smell.
        
           | akira2501 wrote:
           | > go is uniquely american, only doing the right thing after
           | it has tried everything else.
           | 
           | Perhaps this is why most innovations are American. We don't
           | automatically fall for the bully pulpit of the gnostic class.
           | 
           | > Something they wouldn't have to do
           | 
           | Yea, but they'll get it done anyways, and the language will
           | continue to be excellent. I'm sure Google can absorb the
           | engineering challenge without subtracting anything from us or
           | other languages.
        
         | bregma wrote:
         | What about OSes that are not built around monolithic kernels
         | and the syscall() paradigm?
        
         | saagarjha wrote:
         | I am sure Cosmopolitan is going to love this change.
        
           | masklinn wrote:
           | I would assume they already have ways to bounce through a
           | dynamically linked system library since cosmopolitan works on
           | macos, and even more so windows.
        
             | Conscat wrote:
             | That's true, but it's a point of pride in Cosmo's marketing
             | that it produces statically linked binaries acroscreate,
             | least several operating systems.
             | 
             | It can dynamically link the system32 DLLs in Windows and
             | probably could OpenBSD's crt, but dynamic linking in
             | general doesn't work. `dlopen` is no-op, and this has made
             | Cosmo graphics extremely difficult so far.
        
               | actionfromafar wrote:
               | Is that a self-imposed limitation? It sounds eminently
               | possible to add some kind of work-around for dynamic
               | linking. Maybe not for a general case, but for creating a
               | GUI for instance.
        
         | blueflow wrote:
         | > OpenBSD treating libc as the interface with the kernel
         | instead of syscalls being the interface with the kernel
         | 
         | Which is a reasonable thing, given that the libc interface is
         | defined by a widely used IEEE standard while the kernel
         | interface is not.
        
           | Conscat wrote:
           | libC is an extremely leaky abstraction. Programming to it
           | assumes you have a runtime that supports constructor
           | functions, POSIX errno and locale, a global heap allocator
           | singleton, and more. The design space of _Hello World_ is
           | massively constrained by libC.
        
       | ahoka wrote:
       | Just remove the kernel from the base install already?
        
       | tiffanyh wrote:
       | Can someone explain the significance.
        
         | monocasa wrote:
         | OpenBSD has been putting in a lot of work lately to harden the
         | syscall ABI; a large component of that work has been
         | constricting how a syscall is invoked from user space as a
         | defense in depth technique to make shell code style exploits
         | more difficult. That's previously taken the form of techniques
         | like only allowing syscalls to be invoked from the libc .text
         | section.
         | 
         | This work is removing a very indirect morph of syscall where
         | the arguments/sysnum are in a struct in memory, making it
         | harder for exploits to invoke weird versions of syscalls on
         | their own terms.
        
           | eikenberry wrote:
           | Why aren't these changes made in kernel to keep the syscall
           | ABI standardized and safe instead of requiring the use of an
           | unsafe language wrapper? We should be discouraging more use
           | of unsafe languages, not forcing it.
        
             | ben_bai wrote:
             | Then don't run a OS with a kernel written in C. That would
             | rule out Windows, Mac, Linux and *BSD.
        
               | eikenberry wrote:
               | Using syscalls means you can replace the kernel. Using
               | libc means you are locked into and FFI/C calling
               | convention for anything other than C. Apples/Oranges.
        
           | krackers wrote:
           | But aren't shellcode style exploits already fairly rare with
           | W^X, so most end up using return-to-libc style attacks?
           | Wouldn't CFI be a much better solution?
        
       | kstrauser wrote:
       | I don't know a lot about this corner of the OS. Why are syscalls
       | in libc, instead of something like a libsyscall? I could see why
       | a language might want not to depend on what's at least notionally
       | the C runtime. Is the fact that the kernel interface is in libc
       | an accident of Unix being written in C, or is there something
       | more fundamental there?
        
         | monocasa wrote:
         | It's that libc is considered the stable interface for userland
         | in general on openbsd.
        
           | kstrauser wrote:
           | Is that different from Linux/Mac/etc?
        
             | masklinn wrote:
             | It is not different on mac (although there libc is just a
             | subset of the wider libSystem).
             | 
             | It _is_ different, uniquely so, on linux: on most unices
             | the kernel and libc are developed as two sides of an entire
             | system, both being updated in lockstep when the system is
             | updated. As such there is no real concern about keeping the
             | syscalls stable, if you need to change it on the kernel
             | side you update it to match on the libc side and you 're
             | done, everybody is supposed to use the libc.
             | 
             | Not so on linux, the kernel and the libc (most commonly
             | glibc) are developed by entirely different groups which
             | don't necessarily like or communicate with one another. As
             | a result, on linux syscalls are a stable API, and direct
             | syscalls are an officially supported method of interaction.
             | In fact its sometimes necessary as the libc might decide
             | not to expose a syscall.
        
               | kstrauser wrote:
               | Thanks for that. So although the name is "libc", it's
               | more like "lib-how-to-use-the-kernel", too?
        
               | masklinn wrote:
               | Yes, for historical reasons it bunches syscalls (section
               | 2) and C library functions (section 3) into a single
               | binary.
        
               | LegionMammal978 wrote:
               | > It _is_ different, uniquely so, on linux: on most
               | unices the kernel and libc are developed as two sides of
               | an entire system, both being updated in lockstep when the
               | system is updated.
               | 
               | Do FreeBSD [0] and NetBSD [1] not fall under "most
               | unices"? They similarly retain backward compatibility in
               | their syscall interface, so that old binaries with old
               | libcs can run on newer kernels. Linux does not stand
               | alone here.
               | 
               | [0] https://wiki.freebsd.org/AddingSyscalls#Backward_comp
               | atibily
               | 
               | [1] https://www.netbsd.org/docs/internals/en/chap-
               | processes.html...
        
           | masklinn wrote:
           | In pretty much every OS of the unix tradition. libc is the
           | API, with a stable ABI. Although Windows has a something
           | pretty much identical in -- I believe -- ntdll. The name and
           | API differ, but the intent is the same.
        
             | vlovich123 wrote:
             | Except notably Linux where the kernel ABI is the thing
             | that's stable.
        
               | mikepavone wrote:
               | glibc actually goes through quite a bit of effort to
               | remain backwards compatible. There have been times when
               | mistakes were made here, but the extent to which glibc
               | does not have a stable ABI is overstated. The only
               | annoying thing about glibc and ABI stability is that the
               | way it achieves that is via symbol versioning which makes
               | it annoying to target an old glibc on a system with a new
               | er glibc.
        
               | vlovich123 wrote:
               | I didn't mean to imply that glibc's API isn't stable.
               | Just that the stable API boundary for the kernel on Linux
               | is the syscall ABI, not libc.
        
               | mikepavone wrote:
               | Ah sorry, I misunderstood
        
               | vlovich123 wrote:
               | No worries. My wording was muddled.
        
               | dfox wrote:
               | Another stable ABI boundary is anything produced by glibc
               | that can be conceivably placed into memory shared by
               | different unrelated processes (ie. Posix IPC primitives).
               | The requirement for backwards compatibility and
               | architecture variant compatibility (32b/64b...) is the
               | largest reason why things like pthread_mutex have
               | somewhat large overhead and why it is worthwhile to
               | invent various iterations of "futex in userspace".
        
             | MarekKnapek wrote:
             | No, Windows has stable API and ABI named Win32 API. This
             | comes from times of 16bit Windows and works also in x64 /
             | ARM / ARM64, previously it worked in Alpha / MIPS / IA64.
             | This API is implemented in kernel.dll user.dll gdi.dll
             | advapi.dll and similar, it does some stuff, but mostly
             | forwards to the NT API. Beware, kernel.dll is user space
             | component despite its name (historical reasons). NT API is
             | undocumented and not meant to be used by user programs, it
             | is not stable, it lives in ntdll.dll, it does syscalls to
             | the kernel: ntoskrnl.exe. Windows doesn't have a libc (for
             | user programs, it has private one for its own programs),
             | Visual Studio has a libc. Each version of Visual Studio
             | (roughly) has its own libc named msvcrt.dll msvrt100.dll
             | msvcrt140.dll and similar, it hosts the C and C++ libc, it
             | could be linked statically for various benefits and
             | drawbacks.
        
               | aseipp wrote:
               | Windows does actually have a universal C runtime library
               | now, but it's pretty recent; only Windows 10+
               | https://learn.microsoft.com/en-us/cpp/porting/upgrade-
               | your-c...
               | 
               | A big motivation for it was security posture; it means
               | that Microsoft can now ship security updates to UCRT that
               | everyone can rely on rather than a ton of extra surface
               | area through various multiple versions of runtime
               | libraries.
        
               | actionfromafar wrote:
               | It had (has) an unsupported, crippled, unversioned
               | _msvcrt.dll_ which if you used it very carefully with a
               | subset of functions, you could write programs which
               | worked fine on Windows NT and up.
        
               | monocasa wrote:
               | FWIW, large swaths of ntdll are documented and supported
               | these days, for performing work that can't be expressed
               | via the win32 API like raw disk manipulation.
        
         | jcranmer wrote:
         | Libc is really a conflation of at least three different
         | notional libraries. The first library is what its name suggests
         | it is, a standard library for C. Another part of the library is
         | in providing the userspace portion of system services--things
         | like handling static initializers, dynamic loading, or the
         | userspace side of things like creating new threads (not to
         | mention, the actual raw functions you call to get the kernel to
         | do something). The final part of the library is a collection of
         | userspace services which are language agnostic, you might
         | choose different implementations, but you'll always assume are
         | somehow present--libm and malloc are the goto examples here.
         | 
         | As for why the userspace system service library is part of libc
         | instead of being a separate libsyscall or libkernel, that
         | probably is due to Unix being a C operating system--written at
         | a time when most operating systems also came with their own
         | system language. It's definitely not the case for all OS's that
         | the C runtime library is the same as the libsyscall/libkernel--
         | most notably, on Windows, the former is MSVCRT*.dll and the
         | latter is kernel32.dll (or ntdll.dll if you're looking very
         | specifically at syscalls themselves, but ntdll.dll is largely
         | an unstable interface).
        
           | actionfromafar wrote:
           | That's the best explanation I have ever read! I even knew
           | these things beforehand, nodding along, but now I can explain
           | it also to someone else in a coherent way instead of rambling
           | up implementation details like a madman.
           | 
           | Thanks!
        
         | marcosdumay wrote:
         | I'm pretty sure if you just repackage the syscalls into a basic
         | module of your language, you will get more stability than by
         | linking to the libc.
         | 
         | I guess people don't do it because the difference is minimal
         | and they use a lot of other features from the C runtime too.
         | 
         | EDIT: Ops. Not on BSD! The entire thread is about BSD and here
         | I am mindlessly talking about Linux.
        
           | dpassens wrote:
           | Only on systems where the syscall ABI is stable, like on
           | Linux. Others, like macOS and Windows[0], can and will change
           | theirs between releases. OpenBSD even goes one step further
           | and actively prevents code other than libc from performing
           | syscalls[1]
           | 
           | [0] I seem to remember that this changed in a recent Windows
           | version, but I couldn't immediately find a source.
           | 
           | [1] msyscall(2) or, if you don't have an OpenBSD system at
           | hand, https://man.openbsd.org/msyscall
        
       | PrimeMcFly wrote:
       | > Piece by piece, I've been trying to remove the easiest of the
       | terminal-actions that exploit code uses (ie. getting to execve,
       | or performing other system calls, etc).
       | 
       | > I recognize we can never completely remove all mechanisms they
       | use. However, I hope I am forcing attack coders into using
       | increasingly more complicated methods
       | 
       | It's honestly so ridiculous an OS that claims to have security a
       | a focus refuses to add even some sort of basic MAC/RBAC
       | implementation. Even both OSX and Windows have had something for
       | ages now.
        
         | actionfromafar wrote:
         | These things are kind of orthogonal. OpenBSD maybe gets there
         | eventually.
         | 
         | OpenBSD is like a very hardened safe, made of steel and huge
         | bolts and locks. Very polished, very smooth and hard surface.
         | 
         | MAC/RBAC is like having security officers, interviews, checking
         | of IDs, filling in forms and getting an OK from ones boss
         | before performing work someplace in the building and so on.
         | 
         | Both these things can be good. But OpenBSD was always about
         | making a small system as hardened as possible. Evidently, they
         | aren't completely done yet with making the core as hardened as
         | possible.
         | 
         | Windows has great architecture, but lacks instead severerly
         | this _hardness_ which OpenBSD possesses. What use is MAC /RBAC
         | if someone can gain kernel access with a 0 day exploit?
        
       | saagarjha wrote:
       | Why not remove syscall instructions altogether? When libc wants
       | to do something, it traps on an undefined instruction and then
       | the kernel looks at the program counter to see what it should do.
       | Seems like this would be the ultimate application of this line of
       | thought...
        
         | nine_k wrote:
         | Are there any performance implications on such traps on x64 or
         | ARM?
         | 
         | IIRC, PDP11 and VAX used traps as the way to call the
         | supervisor, and it was pretty cheap.
        
           | saagarjha wrote:
           | You're asking the wrong questions of OpenBSD :)
        
             | nine_k wrote:
             | Fair! :)
             | 
             | But interesting nevertheless.
        
           | MBCook wrote:
           | I think ARM (or at least ARM64) does the trap thing too.
        
         | monocasa wrote:
         | I don't think that helps much. OpenBSD already only allows
         | syscalls originating out of the libc .text section, so whether
         | the trap itself comes from a syscall instruction or some other
         | trap mechanism doesn't really improve security AFAICT.
        
           | saagarjha wrote:
           | Yeah but it sounds super cool doesn't it!
        
         | jcranmer wrote:
         | My understanding is this was done by one system, which then
         | made the architecture's life hard because now they lost half
         | their opcode encoding space due to it being used as pseudo-
         | syscalls by their largest customer.
        
         | Veserv wrote:
         | That is basically what we used to do, int 0x80 and such. I
         | mean, I guess you do not read the PC since that is much more
         | brittle than just having the caller say what they want to do,
         | but it is structurally the same.
         | 
         | Turns out, having a dedicated syscall instruction and trap
         | pathway is just better design. Unlike a regular exception, this
         | is a deliberate change of control to the kernel, so you can
         | enforce a much stronger ABI requirement. In particular, you can
         | define it to use a standard function call ABI with respect to
         | preserved and non-preserved registers making it literally look
         | like a standard function call.
         | 
         | For similar reasons, having a dedicated hardware pathway like
         | on x86-64 is also just better design. System calls are a
         | synchronous, voluntary transfer of control that is expected to
         | return in contrast to (1) interrupts which are a asynchronous
         | involuntary transfer of control and (2) instruction stream
         | exceptions which are a synchronous involuntary transfer of
         | control with no guarantee of return. This fundamental
         | distinction can be leveraged for more efficient and simpler
         | implementations.
        
       | throw2022110401 wrote:
       | For language runtimes that don't normally have to deal with C
       | baggage having to drag libc into your address space and going
       | through libc code for syscalls makes this a less secure platform.
        
         | eikenberry wrote:
         | +1.. Why not create a standard/stable syscall interface instead
         | of pushing an anachronistic libc interface.
        
       | notaplumber1 wrote:
       | OpenBSD developers are making a serious effort to kill off
       | indirect syscalls, the base system is completely clean, take a
       | look at the work Andrew Fresh did to adapt Perl. He wrote a
       | complete syscall "dispatcher" or emulator for the Perl syscall
       | function so that it calls the libc stubs.
       | 
       | https://github.com/openbsd/src/commit/312e26c80be876012ae979...
       | 
       | The ports tree is being cleansed of syscall(2) usage, until
       | they're all gone.
       | 
       | msyscall, pinsyscall, recent mandatory IBT/BTI, xonly. OpenBSD is
       | making some waves, but people aren't really seeing them yet.
        
       | pizlonator wrote:
       | A+
       | 
       | This is awesome.
       | 
       | Also, it's rarely used in my experience. I wonder what kind of
       | code would even notice?
        
         | actionfromafar wrote:
         | Exploits, I guess is the concern.
        
       ___________________________________________________________________
       (page generated 2023-10-27 23:01 UTC)