Linux compatibility in IncludeOS
The last version of IncludeOS, 0.13, is the most significant release we’ve made to date. The important change is the depreciation of Newlib, which used to be our C library. The C library is one of the fundamental APIs an operating system can provide.
In addition to having a C library modern operating systems typically implement POSIX, a somewhat arbitrary collection of APIs - standardized by IEEE in 1988. We’ve tried implementing parts of POSIX on our own, but implementing something you don’t really need in old fashioned C is rough. It was soul-sucking work, and we gave up on building POSIX support ourselves.
In addition to our lack of POSIX support, we’ve wantet to address to our missing paging support. Musl requires mmap and mprotect so we had to implement paging whilst adding Musl. Without paging, you cannot write-protect the executable code in memory which means a buffer overflow can very easily translate into remote code execution.
With this in mind, we found Musl. Musl is written explicitly for the Linux kernel. In addition to providing a C library, it also provides a complete POSIX implementation on top of it.
As Musl is written for Linux we have to implement the underlying system calls that Musl relies on. However, as many of you know, Unikernels typically don’t use system calls. So we’ve used macros in order to map the internal system calls in Musl to function calls to IncludeOS.
Before our 1.0 relase we’ll lock down more of our address space and add ASLR. At that point we should have a pretty solid security framework in place. Another advantage is that we are now, to a much greater extent than before, compatible with Linux. If your application can compile on Alpine Linux, or another Linux distribution that uses Musl, we can make it compile on IncludeOS as well. After all, we’re providing the same API. The program still can’t fork() and there are still portions of Musl we haven’t provided the underlying system calls for. POSIX has many dusty corners and we don’t really see the need for supporting all of it. To date we’ve only added what we ourselves need. However, if you need more than what we currently offer we’re willing to expand our support if the use-case is valid. So please get in touch if you need more POSIX.
Out of the close to 400 system calls Linux provides we have implemented 42 so far. For each system call we implement Musl unlocks the relevant parts of POSIX to us. Moreover, since implementing a system call is typically pretty simple we have an easy way of expanding our Linux support should we need to.
A quick and dirty example follows:
#include <sys/utsname.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
int main(void) {
struct utsname buffer;
errno = 0;
if (uname(&buffer) != 0) {
perror("uname");
exit(EXIT_FAILURE);
}
printf("system name = %s\n", buffer.sysname);
printf("node name = %s\n", buffer.nodename);
printf("release = %s\n", buffer.release);
printf("version = %s\n", buffer.version);
printf("machine = %s\n", buffer.machine);
return EXIT_SUCCESS;
}
Running this yields the following output:
* Brk initialized. Begin: 0x46f000, end 0x46f000, MAX 0x100000
* mmap initialized. Begin: 0x56f000, end: 0x7fde000
================================================================================
IncludeOS v0.12.0-999-g1850d7404-dirty (x86_64 / 64-bit)
+--> Running [ IncludeOS seed ]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
system name = IncludeOS
node name = IncludeOS-node
release = v0.12.0-999-g1850d7404-dirty
version = v0.12.0-999-g1850d7404-dirty
machine = x86_64
domain name =
[ main ] returned with status 0
The addition of Musl should make it a lot simpler to develop applications for IncludeOS. Specifically we now have a way of easily adding support for other langauge runtimes.