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