9 ms·
Related (AFAIU): https://news.ycombinator.com/item?id=21653119 https://news.ycombinator.com/item?id=21653119 > We've been concerned about adding even one addit
by Reelin 7y ago
Related (AFAIU): https://news.ycombinator.com/item?id=21653119 https://news.ycombinator.com/item?id=21653119
> We've been concerned about adding even one additional syscall entry point
I don't understand the need for such a severe "only libc syscalls ever" approach.
What would be the security concern with allowing syscalls only from preauthorized (ie msyscall(2)) regions, making initial region authorization opt-in (instead of opt-out), allowing the program to call msyscall(2) itself, and rejecting any statically linked (ie non-ASLR'd) regions for authorization?
- masklinn 7y ago> I don't understand the need for such a severe "only libc syscalls ever" approach. There's nothing severe about it. Most systems are exactly that: systems of which the kernel is only one part, syscalls are rarely if ever intended to be called directly nilly-willy. The issue is that unlike windows unices have never enforced this.
- amscanne 7y agoSorry, that generalization doesn’t hold water. It makes sense for systems where libc is tightly coupled and coversioned with the kernel, e.g. BSDs, but Linux always relied on third-party C libraries and supported static binaries, etc. You could argue that BSD made the mistake of intending to have a Windows-style C library compat guarantee but not enforcing it, but that was not in scope for Linux. The philosophy has always been syscall-level compat (and there are lots of famous threads with Linus re-enforcing this to others who would presume that things should be “fixed in user space”). So it’s hardly reasonable to generalize based on some BSD concerns; Linux is WAI and represents the most common Unix-like system people use today by far. There’s a pretty good argument that this level of compat, while the source of some problems, has also made other things much easier: consider container images that are bundled with their own system libraries. (You could certainly invent schemes to inject these libraries, but dealing with link and library level compatibility seems even more complex to deal with than system call-level compatibility.)
- pcwalton 7y agoDarwin/macOS has the same rules as Windows and the BSDs--syscalls are private API--and it's extremely popular due to iOS. Linux is in fact the odd one out here.
- giovannibajo1 7y agoThere is a difference though: libSystem on Darwin is a very thin wrapper over the kernel syscalls; on the contrary, libc is a library that was designed for C, then standardized in POSIX, and has several layer of abstraction over kernel syscalls including many bad defaults that are universally recognized as wrong today (eg: libc’s created file descriptors will all inherit by default).
- cesarb 7y ago> (eg: libc’s created file descriptors will all inherit by default) Isn't that the kernel default? Even if you use system calls directly, file descriptors still inherit by default.
- Gaelan 7y agoI mean, theoretically (i.e. I have no idea if anything does this), you could have underlying system file descriptors, which did not inherit, then a mapping from "libc" file descriptors onto OS ones and some code in fork() to copy over any OS file descriptors that are exposed to libc-using code.
- loeg 7y agoThe mapping is the identity function and whether a descriptor is inherit or not is just a function of O_CLOEXEC / fcntl(FD_CLOEXEC) / some special fd types are always cloexec, such as kqueues on FreeBSD. Libc fds aren't special to the operating system in any way.
- loeg 7y agoYeah, libc's syscall wrappers just do what you tell them. If you don't pass O_CLOEXEC to the kernel syscalls, you get the inherit behavior. Libc's syscall wrappers don't change this in any way. To the extent that Go's default for file descriptors today is !inherit (I'm unfamiliar, but if so, it's a good choice), the Go runtime must already add O_CLOEXEC to bare syscalls. There's no reason to believe it incapable of adding the flag to libc syscalls instead.
- jchw 7y agoWindows did not enforce this either, and despite how bad of an idea it is, there have been software that do their own syscalls. Mostly tricky things like DRM or anticheat. Still, I don’t think there’s anything wrong with letting an application mark part of its code safe for syscall execution, versus enforcing libc only. Seems like the exact same thing as the execute bit. Moreover, some systems genuinely have a stable syscall ABI - I think Linux would be considered one.
- loeg 7y agoLinux is basically the only system that targets a stable syscall ABI, and that's basically due to being loosely coupled with all other parts of a Linux-based operating system.
- masklinn 7y ago> Windows did not enforce this either Windows pretty much enforces it in the sense that syscall numbers can change between minor updates, so raw syscalls breaks extremely often. > Moreover, some systems genuinely have a stable syscall ABI - I think Linux would be considered one. Linux is not a system. It's a kernel, with userlands you can bolt on. That's why it has a stable syscall ABI: that's the only interface Linux can have if it intends to provide an interface.
- Reelin 7y agoPerhaps "strict" would have been a better word choice than "severe". It does seem that there's a very reasonable security concern about doing so from +w+x memory, or from non-PIE regions, or without first explicitly authorizing the calling range. My question still stands regarding what the security concern of opt-in PIE -w+x code making direct syscalls is. Edit: (Of course I do understand that the BSDs (unlike Linux) do not guarantee a stable syscall ABI and as such performing them directly is strictly a bad practice.)
- binarycrusader 7y agoThe issue is that unlike windows unices have never enforced this. That's not true; Solaris and I suspect other *nixes of the past made it explicitly clear that libc was the interface for userland, not the kernel.
- matheusmoreira 7y ago> syscalls are rarely if ever intended to be called directly nilly-willy. I don't think this is true, at least on Linux. On Linux, the system call interface¹ is the documented interface to user space. Even the commonly used vDSO² is a stable interface. This is important because it means the popular C libraries are not part of the Linux kernel interface. Although glibc is often portrayed³ as some kind of Linux kernel wrapper, they are entirely separate projects. Linux manuals⁴ also make it seem like they are one and the same: > The Linux man-pages project documents the Linux kernel and C library interfaces that are employed by user-space programs. These same manuals also document systemd as if it was part of Linux. I went there expecting low level documentation useful for writing one's own init system and got systemd documentation instead. It's very confusing in my opinion. Why are external projects documented in the Linux manuals? Anyway, these kernel features are used by C libraries to implement all their functions. Using C libraries is the traditional way to build a Linux user space but it is certainly not the only way. Compilers could emit these system calls directly, avoiding the need for a runtime library. A programming language virtual machine could be built directly on top of Linux system calls. It is possible to create freestanding programs that run on Linux with zero dependencies. Incompatibilities are caused by these user space libraries, not by the system calls themselves. For example, glibc maintains a lot of thread local state and will not work correctly if the program calls clone(). A program that does not link to glibc does not have this limitation. Although low level, Linux system calls are in many ways a simpler interface: their behavior is more precisely documented compared to POSIX; there is no need to deal with errno; there is no hidden C library functionality that's hard to understand; freestanding programs do not contain references to hundreds of hidden standard library symbols that implement obscure functionality. The kernel itself containd a nolibc.h header[5] that it apparently uses for its own tools. [1]: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/Documentation/ABI/stable/syscalls https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/lin... [2]: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/Documentation/ABI/stable/vdso https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/lin... [3]: https://en.wikipedia.org/wiki/File:Linux_kernel_System_Call_Interface_and_glibc.svg https://en.wikipedia.org/wiki/File:Linux_kernel_System_Call_... [4]: https://www.kernel.org/doc/man-pages/ https://www.kernel.org/doc/man-pages/ [5]: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/tools/include/nolibc/nolibc.h https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/lin...
- ori_b 7y ago> What would be the security concern with allowing syscalls only from preauthorized (ie msyscall(2)) regions Who preauthorizes them, and what prevents more from getting added?
- Reelin 7y agoCurrently ls.so upon linking in libc; invoking msyscall() is only permitted once per process. Seeing as msyscall() is itself a system call, any calls to it must themselves originate from an authorized region.
- ori_b 7y agoCorrect. So, now, if you want to allow any program, and presumably its libraries, to register their regions of memory as being syscall-worthy you need to lift the called-once restrictions, which opens a hole for an attacker.
- ianlevesque 7y agoI think the idea here is to just let the linker do it.
- ori_b 7y agoWhich is why go is such a problem -- because the linker can't do it without hacks and guesswork, in the case of a static binary that dlopens libc. I'm aware of the issues. I was a part of the discussions in the hut when they were being implemented.
- Reelin 7y ago> you need to lift the called-once restrictions, which opens a hole for an attacker I don't see how that's the case; my original question was essentially asking what that security hole is. Provided that syscall ability is opt-out by default and that only code subjected to ASLR is permitted to be authorized, it doesn't seem terribly risky to allow additional such regions to be registered. An exploit has to contend with ASLR either way; either by locating libc, or by locating some other authorized region within the current process.