...and with no way to run ancillary scripts and cronjobs e.g. log management, backups, security tools... Nor ssh into the VM to inspect it and run debuggers or monitoring and metric agents.
Essentially you are giving away all the practical advantages of modern OSes. There's a reason why the usual famous tech companies don't use unikernels, generally speaking.
As for memory management, in the Solo5/ukvm case the amount of memory you grant to the guest (monitor process) is essentially the maximum you expect the guest to need, ever. So it's similar in terms of overhead to many usermode allocators which just mmap() a huge block of memory at start and then hand out to the application from that.
FV guests can similarly have a balloon driver, that doesn't mean the interface isn't awkward.
Also, there's a small set of programs that don't get along with the abstractions provided by OSes and run better on a virtual hardware abstraction instead.
The intrinsic overhead of the OS (i.e. costs of mode switching) is much smaller than you might think.
It's true that some programs don't really want an OS getting in their way, but a modern operating system provide decent facilities for getting _out_ of the way (e.g. O_DIRECT for block I/O, pinning tasks to cores) and that's usually what those things use.
A userspace process on x86 has a certain amount of overhead, namely the overhead needed to switch between it and the host kernel. This takes the form of SYSCALL/SYSRET (mostly) for system calls and interrupts/IRET (mostly) for interrupt returns. SYSCALL and SYSRET are quite fast. interrupts and IRET are slow.
A VM guest needs to do a VM exit (which is quite a bit slower than even an interrupt, I believe) and then fix up a bunch of host state that doesn't get restored by the CPU. Then, to get back to guest mode, it needs to manually restore some guest state and do VMRESUME.
Regular user mode wins hands down.
To be fair, unikernels get to directly create page tables, which is potentially very useful. But, for the simpler applications, it's probably not.
(I more or less maintain the glue between x86 Linux user code and the kernel. I do, in fact, know exactly how this works.)
Anyway, I don't personally think reducing system call overhead is interesting but it seems to be one of the arguments unikernel people make.
Some more closely meet the definition of a Unikernel, others are more like stripped down Linux, others are more exotic OS's that are nothing more than a network driver and disk driver in a virtual machine, such as Solo5.
I got Bootrino (see elsewhere) to boot all of these types of operating systems including embedded Linux (Yocto), Solo5, TinyCore, Alpine, and some other weird run-from-RAM OS configurations.
I got MirageOS running on all of the above, so unikernels can fit within other OSs or live on their own.
Why is this better than running as a normal user process? Look at the complexity inherent in securely sandboxing applications using existing interfaces -- the Linux system call interface amounts to hundreds of system calls, with different mutations for different architectures and many calls multiplexing even more operations (eg. ioctl()) the numbers grow even higher.
Unikernels give us a shot at redefining the process interface, while still being able to run on existing infrastructure (host OS or hypervisor). This means we can work toward a system where the application (unikernel) has least privilege by default.
With Solo5 [1] we're working towards defining such an interface. We have a way to go yet [2], and are still very much in the "early adopter who knows what a unikernel is" stage, but the possibilities are exciting, not only from the application security point of view.
[1] https://github.com/solo5/solo5 [2] I'd be the first to say that the current Solo5 interfaces are suboptimal, but we're working on it: https://github.com/Solo5/solo5/pull/201
One of the justifications: it removes the need to copy data between kernel/user-space.
KTLS strikes me as inelegant. Socket programming requires you to reason about your frames on the wire. Proto-socket features complicate this.
A unikernel approach would allow us to eliminate unnecessary copies, and could be done with more elegance than KTLS.
The kTLS paper showed pretty small speedups, so maybe the real motivation is TLS hardware offload.
What would happen if you used special instructions to try to implement a non-cache-polluting memcpy? Or is that even possible? It's been a while but I seem to think that modern CPUs have a variety of cache control instructions. Zero copy is great but it can be a PITA.
I'm not buying into this myself as of yet but at least in theory this could lead to.an "application provably correct all the way down the stack".
If you compare the attack surface of a Linux user process that is very tightly seccomped to the attack surface of a whole guest VM (with all the awkward emulation needed), it's not at all clear to me that guests are better.
I'm not sure I follow the logic in your harder-to-retrofit opinion. In unikernels the central idea is retrofitting the unikernel around the host interface and the application. It seems obvious to me that this would be more easier to develop and debug inside a standard process, than an emulation of bare iron, even if you would have to call it a different platform due to a radically smaller syscall interface.
I don't see that they have to be? There's no reason for the host to offer e.g. a virtualized graphics card except to unikernels that specifically want to output graphics.
> In unikernels the central idea is retrofitting the unikernel around the host interface and the application.
I don't think I follow? The only part that's retrofitted is using an interface that was designed to host OSes to instead host an application, but it's a good fit because virtualized guests haven't been treated as trusted (particularly not in contexts like AWS), whereas for decades of unix history any local process was treated as more-or-less trusted.
> It seems obvious to me that this would be more easier to develop and debug inside a standard process, than an emulation of bare iron, even if you would have to call it a different platform due to a radically smaller syscall interface.
If we're talking about creating a new platform with a restrictive, strictly secured syscall interface, whether we talk about a hypervisor running unikernels or a kernel running processes is pretty much just terminology. I would think the actual interface specification would end up very similar to the current hypervisor/unikernel one.
The big advantage of keeping compatible with existing VMs is to provide a way to run legacy processes without compromising the security of new-style ones (and while making sure the incentives in terms of e.g. performance run in the right direction). If one component of your system needs to be run as a traditional unix process, that's fine - it gets its own VM in which it's running a traditional kernel and the actual process - but it can't interfere directly with your other components, and it pays for its own overheads.
Historically Unix processes and VM guests have both been treated as untrusted on paper, but with not much scrutiny. There are frequent VM breakout vulnerabilities announced and there's wide agreement that this is a porous security boundary. The numbers are probably better for the VM interface, but it's in the same category.
Apparently we agree that in terms of exposed functionality, the end result is not going to be very different, if we restrict the VM interface vs use seccomp or similar.
A difference here is that there is already an easy and widely supported and understood system for this restriction for the process case. And processes are easier to debug, instrument, monitor and manage. And they don't require platform support for virtualization, which can be useful on eg embedded platforms, phone apps etc.
For the last point about running legacy processes, maybe we have a different understanding of what Unikernels are. If you have an unikernel application that uses a Rump kernel or MirageOS, you are not going to be able to run legacy processes in the Unikernel container in any case, there is not even going to be a kernel/userspace boundary. There is just one code image with everyting linked into it. So the traditional VM interface is not going to help with the legacy processes.
If you were talking about running regular VMs for legacy apps alongside unikernel images, then that's of course valid, but I don't agree that this is an argument for using the VM interface for unikernel images.
There is? I see a bunch of ad-hoc, retrofitted efforts, which seem to be constantly playing whack-a-mole in terms of what they forgot to restrict.
> And processes are easier to debug, instrument, monitor and manage.
Yes and no. The kernel/process boundary can make debugging and profiling much harder when you need to do so across it. With a lot of unikernels you can run the whole thing as a userspace process for development purposes and debug/profile without worrying about that. And these days we have a lot of good tooling for monitoring and managing VMs.
> If you were talking about running regular VMs for legacy apps alongside unikernel images, then that's of course valid, but I don't agree that this is an argument for using the VM interface for unikernel images.
That's what I meant. If you have a mixed deployment of legacy components and new-style components, that approach lets you realise the benefits quicker for the new-style pieces and ensure there's an incentive to move components into the new style. Whereas if you just run a bunch of processes, then no single component can benefit from isolation until you've ported everything.
The debugging would be similar with a unikernel run as a seccomp process: you could debug it as a single process. I believe the BSD rump kernel is already used like this for debugging purpouses.
If you want to mix legacy processes and unikernels in parallel, you would still run the legacy processes on a different VM in the seccomp case.