The MirageOS Blogon building functional operating systems


Announcing MirageOS 3.10

By Hannes Mehnert

MirageOS 3.10 release - IPv6

IPv6 and dual (IPv4 and IPv6) stack support

Since a long time, IPv6 code was around in our TCP/IP stack (thanks to @nojb who developed it in 2014). Some months ago, @hannesm and @MagnusS got excited to use it. After we managed to fix some bugs and add some test cases, and writing more code to setup IPv6-only and dual stacks, we are eager to share this support for MirageOS in a released version. We expect there to be bugs lingering around, but duplicate address detection (neighbour solicitation and advertisements) has been implemented, and (unless "--accept-router-advertisement=false") router advertisements are decoded and used to configure the IPv6 part of the stack. Configuring a static IPv6 address is also possible (with "--ipv6=2001::42/64").

While at it, we unified the boot arguments between the different targets: namely, on Unix (when using the socket stack), you can now pass "--ipv4=" to the same effect as the direct stack: only listen on (the subnet mask is ignored for the Unix socket stack).

A dual stack unikernel has "--ipv4-only=BOOL" and "--ipv6-only=BOOL" parameters, so a unikernel binary could support both Internet Protocol versions, while the operator can decide which protocol version to use. I.e. now there are both development-time (stackv4 vs stackv6 vs stackv4v6) choices, as well as the run-time choice (via boot parameter).

I'm keen to remove the stackv4 & stackv6 in future versions, and always develop with dual stack (leaving it to configuration & startup time to decide whether to enable ipv4 and ipv6).

Please also note that the default IPv4 network configuration no longer uses as default gateway (since there was no way to unset the default gateway

For unikernel developers, there are some API changes in the Mirage module

  • New "v4v6" types for IP protocols and stacks
  • The ipv6_config record was adjusted in the same fashion as the ipv4_config type: it is now a record of a network (V6.Prefix.t) and gateway (V6.t option)

Some parts of the Mirage_key module were unified as well:

  • Arp.ip_address is available (for a dual Ipaddr.t)
  • Arg.ipv6_address replaces Arg.ipv6 (for an Ipaddr.V6.t)
  • Arg.ipv6 replaces Arg.ipv6_prefix (for a Ipaddr.V6.Prefix.t)
  • and V6.gateway are available, mirroring the V4 submodule

If you're ready to experiment with the dual stack: below is a diff for our basic network example (from mirage-skeleton/device-usage/network) replacing IPv4 with a dual stack, and the tlstunnel unikernel commit changed tlstunnel from IPv4 stack to dual stack.

diff --git a/device-usage/network/ b/device-usage/network/
index c425edb..eabc9d6 100644
--- a/device-usage/network/
+++ b/device-usage/network/
@@ -4,9 +4,9 @@ let port =
   let doc = ~doc:"The TCP port on which to listen for
incoming connections." ["port"] in
   Key.(create "port" Arg.(opt int 8080 doc))

-let main = foreign ~keys:[Key.abstract port] "Unikernel.Main" (stackv4
@-> job)
+let main = foreign ~keys:[Key.abstract port] "Unikernel.Main"
(stackv4v6 @-> job)

-let stack = generic_stackv4 default_network
+let stack = generic_stackv4v6 default_network

 let () =
   register "network" [
diff --git a/device-usage/network/
index 5d29111..1bf1228 100644
--- a/device-usage/network/
+++ b/device-usage/network/
@@ -1,19 +1,19 @@
 open Lwt.Infix

-module Main (S: Mirage_stack.V4) = struct
+module Main (S: Mirage_stack.V4V6) = struct

   let start s =
     let port = Key_gen.port () in
-    S.listen_tcpv4 s ~port (fun flow ->
-        let dst, dst_port = S.TCPV4.dst flow in
+    S.listen_tcp s ~port (fun flow ->
+        let dst, dst_port = S.TCP.dst flow in (fun f -> f "new tcp connection from IP %s on port %d"
-                  (Ipaddr.V4.to_string dst) dst_port);
- flow >>= function
+                  (Ipaddr.to_string dst) dst_port);
+ flow >>= function
         | Ok `Eof -> (fun f -> f "Closing connection!");
-        | Error e -> Logs.warn (fun f -> f "Error reading data from
established connection: %a" S.TCPV4.pp_error e); Lwt.return_unit
+        | Error e -> Logs.warn (fun f -> f "Error reading data from
established connection: %a" S.TCP.pp_error e); Lwt.return_unit
         | Ok (`Data b) ->
           Logs.debug (fun f -> f "read: %d bytes:\n%s" (Cstruct.len b)
(Cstruct.to_string b));
-          S.TCPV4.close flow
+          S.TCP.close flow

     S.listen s

Other bug fixes include (in and adapt to charrua 1.3.0 and arp 2.3.0 changes (


Announcing MirageOS 3.9.0

By Martin Lucina, Hannes Mehnert

MirageOS 3.9.0 release

We are pleased to announce the release of MirageOS 3.9.0.

Our last release announcement was for MirageOS 3.6.0, so we will also cover changes since 3.7.x and 3.8.x in this announcement.

New features:

  • The Xen backend has been re-written from scratch to be based on Solo5, and now supports PVHv2 on Xen 4.10 or higher, and QubesOS 4.0.
  • As part of this re-write, the existing Mini-OS based implementation has been retired, and all non-UNIX backends now use a unified OCaml runtime based on ocaml-freestanding.
  • OCaml runtime settings settable via the OCAMLRUNPARAM environment variable are now exposed as unikernel boot parameters. For details, refer to #1180.

Security posture improvements:

  • With the move to a unified Solo5 and ocaml-freestanding base MirageOS unikernels on Xen gain several notable improvements to their overall security posture such as SSP for all C code, W^X, and malloc heap canaries. For details, refer to the mirage-xen 6.0.0 release announcement.

API breaking changes:

  • Several Xen-specific APIs have been removed or replaced, unikernels using these may need to be updated. For details, refer to the mirage-xen 6.0.0 release announcement.

Other notable changes:

  • Mirage_runtime provides event loop enter and exit hook registration (#1010).
  • All MirageOS backends now behave similarly on a successful exit of the unikernel: they call exit with the return value 0, thus at_exit handlers are now executed (#1011).
  • The unix backend used a toplevel exception handler, which has been removed. All backends now behave equally with respect to exceptions.
  • Please note that the Mirage_net.listen function still installs an exception handler, which will be removed in a future release. The out of memory exception is no longer caught by Mirage_net.listen (#1036).
  • To reduce the number of OPAM packages, the mirage-*-lwt packages are now deprecated. Mirage_net (and others) now use Lwt.t directly, and their buffer type is Cstruct.t (#1004).
  • OPAM files generated by mirage configure now include opam build and installation instructions, and also an URL to the Git origin (#1022).

Known issues:

  • mirage configure fails if the unikernel is under version control and no origin remote is present (#1188).
  • The Xen backend has issues with event delivery if built with an Alpine Linux GCC toolchain. As a work-around, please use a Fedora or Debian based toolchain.


  • Thanks to Roger Pau Monné, Andrew Cooper and other core Xen developers for help with understanding the specifics of how Xen PVHv2 works, and how to write an implementation from scratch.
  • Thanks to Marek Marczykowski-Górecki for help with the QubesOS specifics, and for forward-porting some missing parts of PVHv2 to QubesOS version of Xen.
  • Thanks to @palainp on Github for help with testing on QubesOS.


TLS 1.3 support for MirageOS

By Hannes Mehnert

We are pleased to announce that TLS 1.3 support for MirageOS is available. With mirage 3.7.7 and tls 0.12 the Transport Layer Security (TLS) Protocol Version 1.3 is available in all MirageOS unikernels, including on our main website. If you're reading this, you've likely established a TLS 1.3 connection already :)

Getting there was some effort: we now embed the Coq-verified fiat library (from fiat-crypto) for the P-256 elliptic curve, and the F*-verified hacl library (from Project Everest) for the X25519 elliptic curve to establish 1.3 handshakes with ECDHE.

Part of our TLS 1.3 stack is support for pre-shared keys, and 0 RTT. If you're keen to try these features, please do so and report any issues you encounter to our issue tracker.

We are still lacking support for RSA-PSS certificates and EC certificates, post-handshake authentication, and the chacha20-poly1305 ciphersuite. There is also a minor symbol clash with the upstream F* C bindings which we are aware of. We will continue to work on these, and patches are welcome.


Hackers and climate activists join forces in Leipzig

By Damien Leloup

Hackers and climate activists join forces in Leipzig

By Damien Leloup, special correspondent, Le Monde. Originally published by Le Monde on December 30, 2019. English translation by the MirageOS Core Team.

The Chaos Communication Congress, the world's largest self-managed event dedicated to IT security, hosted its 36th edition this weekend in Germany.

In front of Leipzig station, around fifty students and high school students are gathered. It's Sunday, but the local branch of the Fridays for Future movement, which organizes demonstrations on Fridays at the call of activist Greta Thunberg, made an exception to its usual calendar to take advantage of the presence, a few kilometers from there, of the Chaos Communication Congress (CCC).

Organized each year for thirty-six years, this gigantic gathering of hackers and activists - 18,000 participants, dozens of conference talks around the clock - invades the Leipzig convention center for four days, in an atmosphere that is both anarcho-libertarian and very family oriented. For the first time, the slogan of the 2019 edition is devoted to the environment: “Resource exhaustion”, a reference both to a computer attack technique and to the preservation of the planet.

"It makes sense: it's a major issue, and the environmental movement is a highlight of the year, isn't it?", notes, hair dyed pink and megaphone in hand, Rune, one of the organizers of the event. “In any case, we are very happy that the CCC opened its doors to us and supports us."

The conference program gave pride of place to ecological themes and organizations such as Fridays for Future or Extinction Rebellion. These themes were all features in the main talks. The audience for the event, marked on the far left, is quite sensitive to environmental issues. Fridays for Future's review of the year was sold out; the track where some scientists explained how they build their climate models was full and was not able to host all the attendees.

Safety of power plants and the right to repair

But if the CCC has given a lot of space to environmental issues, it has done it in its own way. In this mecca of cyber-security, we could for example discover long lists of vulnerabilities affecting the on-board systems used to manage the turbines of power plants. Do not panic: "These flaws do not block a power plant, or cut the power of a city," relativized Radu Motspan, the author of the study. Some of them have been corrected; for others, it is up to plant managers to carry out verifications. The researcher and his associates produced a small turnkey guide to help them: “No need to hire expensive consultants, you can do everything yourself."

This “do it yourself” spirit, omnipresent in hacker culture in general and at the CCC in particular, easily lends itself to an environmental translation. The collective Runder Tisch Reparatur, which campaigns for the adoption at European level of a "right to repair", was thus invited for the first time to the conference. The philosophy of the movement, which aims above all to reduce the amount of waste produced by obsolescence whether or not it is programmed, is very similar to that of the free software movement, say Eduard and Erik, who run the stand of the association. "An object that you cannot repair does not really belong to you," they believe, just as the promoters of free software believe that software that you cannot modify yourself deprives you of certain freedoms.

But the main issue, at the heart of many talks during the four days of the CCC, is that of the energy impact of the Internet. No one in the aisles of the Leipzig congress center plans to reduce their use of the Internet; but everyone concedes that the network consumes a lot of electricity unnecessarily, or uses too much fossil energy. "There are simple things to do to improve the carbon footprint of a site or service," said Chris Adams, environmental activist and member of the Green Web Foundation. “If your service uses Amazon Web Service [AWS, a very popular cloud service], you can choose the data center you want to use, for example. The one assigned to you by default may be in a country that produces little renewable electricity and uses a lot of coal for its power plants… ”

Existing non-digital systems (like boilers) already have ways to function more efficiently, such as off-peak hours at night, when electricity is both cheaper and more eco-friendly. There are equivalent options for more modern, digital systems, for instance: Chris Adams advocates the use of the Low Carbon Kubernetes Scheduler, an orchestration tool which allows to optimise the power consumption of a server in order to reduce its environmental impact.

Safety and minimum consumption, get the best of both worlds

Despite everything, the “greenest” electricity remains that which we do not consume in the first place. There too, promising solutions exist: Hannes Mehnert, a German computer scientist, presented at the opening of the CCC the MirageOS project, an operating system for ultra-minimalist servers, coded in a language renowned for its lightness, and which runs each process in a dedicated virtual machine. A radical approach - and reserved for connoisseurs - which allows the software to embed only the bare minimum of lines of code in each compiled version. "Reducing complexity mathematically reduces the number of calculation operations required," explains Mehnert. Result: "A carbon footprint that drops drastically, with ten times less computing power used by the processor, and up to twenty-five times less memory used", according to his measurements.

Above all, and this is a strong argument at a conference dedicated to computer security, minimalism is also a real advantage in terms of potential vulnerabilities: the more compact the code is, the less it risks containing flaws or errors. And such systems are also better protected and safer than more conventional systems, as they are not vulnerable to memory-safety issues.

But the collaboration between environmentalists and privacy advocates seemed much broader than just focusing on technical issues. That mix was everywhere: for instance the corridor walls had graffiti concerned with the the excessive consumption of CO2 on the planet close to others highlighting the fact that every human being generates, on average, 1.7 MB of data per second. The posters of the anarchist or anti-fascist movements were also mixed with the flyers of the collective Hackers against Climate Change, which attracted the curious with a joke typical of the place: "cli / mate crisis is real", in reference to Club-Mate, an ubiquitous drink at the event.

This community of views between hackers and climate activists comes as little surprise in Germany, where both movements are very present, and even less in Leipzig, the flagship city of the former GDR where pre-Internet mass surveillance tools from the Stasi were also directed against environmental activists in the 1980s. Some environmental movements feel naturally close to the anarchist spirit of the German hackers of the Chaos Computer Club, which organizes the CCC: a talk by Extinction Rebellion detailed the security measures they took to ensure their privacy, and how they didn't depend on third-party tools from Facebook, Google or Amazon - responsible, in their eyes, both for mass surveillance and green washing.

However, in this atmosphere of global collaboration some questions remained unanswered. In some specific cases, better IT security can also consume more resources. A talk dedicated to encrypted messaging thus presented many tools which make it possible to reinforce the confidentiality of exchanges, but also require using more computing power, to encrypt or decrypt messages, or even request the sending of large quantities of data to scramble the origin or volume of a message. This first CCC under the sign of environmental protection did not really address this contradiction - pending, perhaps, a next edition?


Announcing Irmin 2.0.0

By Anil Madhavapeddy, Thomas Gazagnaire

We are pleased to announce Irmin 2.0.0, a major release of the Git-like distributed branching and storage substrate that underpins MirageOS. We began the release process for all the components that make up Irmin back in May 2019, and there have been close to 1000 commits since Irmin 1.4.0 release back in June

  1. To celebrate this milestone, we have a new logo and opened a dedicated website:

You can read more details about the new features in the Irmin v2 blog post. Enjoy the new release, and stay tuned for the upcoming Wodan integration in 2020 that will be a stable filesystem for the hypervisor targets for MirageOS that do not have a conventional OS kernel underneath them!


Announcing MirageOS 3.6.0

By Martin Lucina

MirageOS 3.6.0 release

We are pleased to announce the release of MirageOS 3.6.0. This release updates MirageOS to support Solo5 0.6.0 and later.

New features:

  • Support for the Solo5 spt (sandboxed process tender) target via mirage configure -t spt. The spt target runs MirageOS unikernels in a minimal strict seccomp sandbox on Linux x86_64, aarch64 and ppc64le hosts.
  • Support for the Solo5 application manifest, enabling support for multiple network and block storage devices on the hvt, spt and muen targets. The genode and virtio targets are still limited to using a single network or block storage device.
  • Several notable security enhancements to Solo5 targets, such as enabling stack smashing protection throughout the toolchain by default and improved page protections on some targets. For details, please refer to the Solo5 0.6.0 release notes.

Additional user-visible changes:

  • Solo5 0.6.0 has removed the compile-time specialization of the solo5-hvt tender. As a result, a solo5-hvt binary is no longer built at mirage build time. Use the solo5-hvt binary installed in your $PATH by OPAM to run the unikernel.
  • mirage build now produces silent ocamlbuild output by default. To get the old behaviour, run with --verbose or set the log level to info or debug.
  • New functions Mirage_key.is_solo5 and Mirage_key.is_xen, analogous to Mirage_key.is_unix.

Thanks to Hannes Mehnert for help with the release engineering for MirageOS 3.6.0.


MirageOS security advisory 02: mirage-xen < 3.3.0

By Thomas Leonard

MirageOS Security Advisory 02 - grant unshare vulnerability in mirage-xen

  • Module: mirage-xen
  • Announced: 2019-04-25
  • Credits: Thomas Leonard, Mindy Preston
  • Affects: mirage-xen < 3.3.0, mirage-block-xen < 1.6.1, mirage-net-xen < 1.10.2, mirage-console < 2.4.2, ocaml-vchan < 4.0.2, ocaml-gnt (no longer supported)
  • Corrected: 2019-04-22: mirage-xen 3.4.0, 2019-04-05: mirage-block-xen 1.6.1, 2019-04-02: mirage-net-xen 1.10.2, 2019-03-27: mirage-console 2.4.2, 2019-03-27: ocaml-vchan 4.0.2

For general information regarding MirageOS Security Advisories, please visit


MirageOS is a library operating system using cooperative multitasking, which can be executed as a guest of the Xen hypervisor. Virtual machines running on a Xen host can communicate by sharing pages of memory. For example, when a Mirage VM wants to use a virtual network device provided by a Linux dom0:

  1. The Mirage VM reserves some of its memory for this purpose and writes an entry to its grant table to say that dom0 should have access to it.
  2. The Mirage VM tells dom0 (via XenStore) about the grant.
  3. dom0 asks Xen to map the memory into its address space.

The Mirage VM and dom0 can now communicate using this shared memory. When dom0 has finished with the memory:

  1. dom0 tells Xen to unmap the memory from its address space.
  2. dom0 tells the Mirage VM that it no longer needs the memory.
  3. The Mirage VM removes the entry from its grant table.
  4. The Mirage VM may reuse the memory for other purposes.

Problem Description

Mirage removes the entry by calling the gnttab_end_access function in Mini-OS. This function checks whether the remote domain still has the memory mapped. If so, it returns 0 to indicate that the entry cannot be removed yet. To make this function available to OCaml code, the stub_gntshr_end_access C stub in mirage-xen wrapped this with the OCaml calling conventions. Unfortunately, it ignored the return code and reported success in all cases.


A malicious VM can tell a MirageOS unikernel that it has finished using some shared memory while it is still mapped. The Mirage unikernel will think that the unshare operation has succeeded and may reuse the memory, or allow it to be garbage collected. The malicious VM will still have access to the memory.

In many cases (such as in the example above) the remote domain will be dom0, which is already fully trusted. However, if a unikernel shares memory with an untrusted domain then there is a problem.


No workaround is available.


Returning the result from the C stub required changes to the OCaml grant API to deal with the result. This turned out to be difficult because, for historical reasons, the OCaml part of the API was in the ocaml-gnt package while the C stubs were in mirage-xen, and because the C stubs are also shared with the Unix backend.

We instead created a new grant API in mirage-xen, migrated all existing Mirage drivers to use it, and then dropped support for the old API. mirage-xen 3.3.0 added support for the new API and 3.4.0 removed support for the old one.

The recommended way to upgrade is:

opam update
opam upgrade mirage-xen

Correction details

The following PRs were part of the fix:


You can find the latest version of this advisory online at

This advisory is signed using OpenPGP, you can verify the signature by downloading our public key from a keyserver (gpg --recv-key 4A732D757C0EDA74), downloading the raw markdown source of this advisory from GitHub and executing gpg --verify 02.txt.asc.


MirageOS Spring 2019 hack retreat roundup

By Hannes Mehnert

MirageOS Spring Hack Retreat, Marrakesh 2019

Early March 2019, 31 MirageOS hackers gathered again in Marrakesh for our bi-annual hack retreat. We'd like to thank our amazing hosts, and everyone who participated on-site or remotely, and especially those who wrote up their experiences.

On this retreat, we ate our own dogfood, and used our MirageOS DHCP, recursive DNS resolver, and CalDAV unikernels as isolated virtual machines running on a PC Engines APU with FreeBSD as host system. The CalDAV server persisted its data in a git repository on the host system, using the raw git protocol for communication, the smart HTTP protocol could have been used as well. Lynxis wrote a detailed blog post about our uplink situation.

Lots of interesting discussions took place, code was developed, knowledge was exchanged, and issues were solved while we enjoyed the sun and the Moroccan food. The following list is not exhaustive, but gives an overview what was pushed forward.


Imagelib is a library that can parse several image formats, and eye-of-mirage uses it to display those images in a framebuffer.

During the retreat, imagelib was extended with support for the BMP format, it's build system was revised to split off Unix-dependent functionality, and preliminary support for the GIF format was implemented.


ActivityPub is an open, decentralized social networking protocol, as used by mastodon. It provides a client/server API for creating, updating, and deleting content, and a federated server-to-server API for notifications and content delivery. During the retreat, an initial prototype of a protocol implementation was drafted.


Opam, the OCaml package manager, was extended in several directions:


Work was started on a new utility to install as many opam packages as possible on a machine (there just wasn't enough choice with opam-builder, opamcheck and opam-check-all). It uses opam-lib and Z3 to accomplish this.


Conex is used for signing community repositories, esp. the opam-repository. Any opam package author can cryptographically sign their package releases, and users can verify that the downloaded tarball and build instructions are identical to what the author intended.

Conex has been developed since 2015, but is not yet widely deployed. We extended opam-publish to invoke the conex_targets utility and sign before opening a pull request on the opam-repository.


The simple mail transfer protocol is an Internet standard for sending and receiving eMail. Our OCaml implementation has been improved, and it is possible to send eMails from OCaml code now.


The hypertext transfer protocol is an Internet standard widely used for browsing the world wide web. HTTP 1.1 is a line-based protocol which was specified 20 years ago. HTTP2 is an attempt to fix various shortcomings, and uses a binary protocol with multiplexing, priorities, etc. An OCaml implementation of HTTP2 has been actively worked on in Marrakesh.


Irmin is a distributed database that follows the same design principles as git. Soon, Irmin 2.0 will be released, which includes GraphQL, HTTP, chunk support, and can use the git protocol for interoperability. Irmin provides a key-value interface for MirageOS.

OCaml compiler

Some hints on type errors for int literals and int operators were developed and merged to the OCaml compiler.

# 1.5 +. 2;;
Error: This expression has type int but an expression was expected of type
       Hint: Did you mean `2.'?

# 1.5 + 2.;;
  ^^^ ^
Error: This expression has type float but an expression was expected of type
Line 1, characters 4-5:
  Hint: Did you mean to use `+.'?

Also, the whole program dead code elimination PR was rebased onto trunk.

BGP / lazy trie

The mrt-format library which can parse multi-threaded routing toolkit traces, has been adapted to the modern OCaml ecosystem. The border gateway protocol (BGP) library was slightly updated, one of its dependencies, lazy-trie was adapted to the modern ecosystem as well.


Xen provides several modes for virtualization. MirageOS's first non-Unix target was the para-virtualized (PV) mode for Xen, which does not require hardware support from the hypervisor's host operating system but has a weak security profile (static mapping of addresses, large attack surface). However, PV mode provides an attractive target for unikernels because it provides a simple software-based abstraction for dealing with drivers and a simple memory model; this is in contrast to hardware-virtualization mode, which provides greater security but requires more work from the guest OS.

A more modern virtualization mode combining the virtues of both approaches is PVH (formerly referred to as HVMLite), which is not yet supported by MirageOS. Marek Marczykowski-Górecki from the QubesOS project visited to help us bring PVH support to the unikraft project, a common platform for building unikernels which we hope to use for MirageOS's Xen support in the future.

During the retreat, lots of bugs porting MirageOS to PVH were solved. It boots and crashes now!

Learn OCaml as a unikernel

The platform learn OCaml embeds an editor, top-level, and exercises into a HTTP server, and allows students to learn OCaml, and submit solutions via the web interface, where an automated grader runs unit tests etc. to evaluate the submitted solutions. Teachers can assign mandatory exercises, and have an overview how the students are doing. Learn OCaml used to be executable only on a Unix host, but is now beeing ported into a MirageOS unikernel, executable as a standalone virtual machine.

Network device driver (ixy)

The ixy network driver supports Intel 82599 network interface cards, and is implemented in OCaml. Its performance has been improved, including several failing attempts which degraded its performance. Also, it has been integrated into the mirage tool and is usable as a mirage-net implementation.

DNS client API

Our proposed API is described here. Unix, Lwt, and MirageOS implementations are already available.

mirage-http unified HTTP API

Since we now have two HTTP servers, cohttp and httpaf, in OCaml and MirageOS available, the new interface mirage-http provides a unified interface, and also supports connection upgrades to websockets.

cstruct capabilities

We use cstruct, a wrapper around OCaml's Bigarray, quite a lot in MirageOS. Until now, cstruct is a readable and writable byte array. We used phantom types to add capabilities to the interface to distinct read-only and write-only buffers.


An OCaml implementation to apply unified diffs. This code has been extracted from conex (since we found some issues in it), and still needs to be fixed.

Statistical memory profiler

Since 2016, Jacques-Henri Jourdan has been working on a statistical memory profiler for OCaml (read the OCaml 2016 paper). An Emacs user interface is available since some years. We integrated statmemprof into MirageOS unikernels using the statmemprof-mirage library, marshal the data via TCP, and provide a proxy that communicates with Emacs over a Unix domain socket, and the unikernel.


P2Pcollab is a collection of composable libraries implementing protocols for P2P collaboration. So far various P2P gossip protocols has been implemented. At this retreat the focus was on a gossip-based publish-subscribe dissemination protocol. Future plans include building P2P unikernels and adding P2P pub/sub sync functionality to Irmin.


MirageOS security advisory 01: netchannel 1.10.0

By Mindy Preston

MirageOS Security Advisory 01 - memory disclosure in mirage-net-xen

  • Module: netchannel
  • Announced: 2019-03-21
  • Credits: Thomas Leonard, Hannes Mehnert, Mindy Preston
  • Affects: netchannel = 1.10.0
  • Corrected: 2019-03-20 1.10.1 release

For general information regarding MirageOS Security Advisories, please visit


MirageOS is a library operating system using cooperative multitasking, which can be executed as a guest of the Xen hypervisor. Virtual devices, such as a network device, share memory between MirageOS and the hypervisor. To maintain adequate performance, the virtual device managing network communication between MirageOS and the Xen hypervisor maintains a shared pool of pages and reuses them for write requests.

Problem Description

In version 1.10.0 of netchannel, the API for handling network requests changed to provide higher-level network code with an interface for writing into memory directly. As part of this change, code paths which exposed memory taken from the shared page pool did not ensure that previous data had been cleared from the buffer. This error resulted in memory which the user did not overwrite staying resident in the buffer, and potentially being sent as part of unrelated network communication.

The mirage-tcpip library, which provides interfaces for higher-level operations like IPv4 and TCP header writes, assumes that buffers into which it writes have been zeroed, and therefore may not explicitly write some fields which are always zero. As a result, some packets written with netchannel v1.10.0 which were passed to mirage-tcpip with nonzero data will have incorrect checksums calculated and will be discarded by the receiver.


This issue discloses memory intended for another recipient and corrupts packets. Only version 1.10.0 of netchannel is affected. Version 1.10.1 fixes this issue.

Version 1.10.0 was available for less than one month and many upstream users had not yet updated their own API calls to use it. In particular, no version of qubes-mirage-firewall or its dependency mirage-nat compatible with version 1.10.0 was released.


No workaround is available.


Transmitting corrupt data and disclosing memory is fixed in version 1.10.1.

The recommended way to upgrade is:

opam update
opam upgrade netchannel

Or, explicitly:

opam upgrade
opam reinstall netchannel=1.10.1

Affected releases (version 1.10.0 of netchannel and mirage-net-xen) have been marked uninstallable in the opam repository.

Correction details

The following list contains the correction revision numbers for each affected branch.

Memory disclosure on transmit:

master: 6c7a13a5dae0f58dcc0653206a73fa3d8174b6d2

1.10.0: bd0382eabe17d0824c8ba854ec935d8a2e5f7489



You can find the latest version of this advisory online at

This advisory is signed using OpenPGP, you can verify the signature by downloading our public key from a keyserver (gpg --recv-key 4A732D757C0EDA74), downloading the raw markdown source of this advisory from GitHub and executing gpg --verify 01.txt.asc.


Announcing MirageOS 3.5.0

By Hannes Mehnert

MirageOS 3.5.0 release

We are happy to announce our MirageOS 3.5.0 release. We didn't announce post 3.0.0 releases too well -- that's why this post tries to summarize the changes in the MirageOS ecosystem over the past two years. MirageOS consists of over 100 opam packages, lots of which are reused in other OCaml projects and deployments without MirageOS. These opam packages are maintained and developed further by lots of developers.

On the OCaml tooling side, since MirageOS 3.0.0 we did several major changes:

  • moved most packages to dune (formerly jbuilder) and began using dune-release for smooth developer experience and simple releases,
  • require opam to be version 2.0.2 or later, allowing pin-depends in pin-depends allows you to depend on a development branch of any opam package for your unikernel,
  • adjusted documentation to adhere to odoc requirements,
  • the mirage command-line utility now emits lower and upper bounds of opam packages, allowing uncompromising deprecation of packages,
  • support for OCaml 4.06.0 (and above), where safe-string is enabled by default. Strings are immutable now!!,
  • remove usage of result package, which has incorporated into Pervasives since OCaml 4.03.0.

The 3.5.0 release contains several API improvements of different MirageOS interfaces - if you're developing your own MirageOS unikernels, you may want to read this post to adjust to the new APIs.

MirageOS interface API changes:

  • mirage-clock has the type t constrained to unit as of 2.0.0;
  • mirage-protocols renames the ETHIF module type to the clearer ETHERNET. As of 2.0.0 it also contains keep-alive support, complies with recent TCP/IP layering rework (see below), and IPv4 now supports reassembly and fragmentation;
  • mirage-net reflects revised layering API as of 2.0.0 (see below);
  • mirage-kv has a revised API and introduction of a read-write key-value store (see below).

Major changes

Key-value store

We improved the key-value store API, and added a read-write store. There is also ongoing work which implements the read-write interface using irmin, a branchable persistent storage that can communicate via the git protocol. Motivations for these changes were the development of CalDAV, but also the development of wodan, a flash-friendly, safe and flexible filesystem. The goal is to EOL the mirage-fs interface in favour of the key-value store.

Major API improvements (in this PR, since 2.0.0):

  • The key is now a path (list of segments) instead of a string
  • The value type is now a string
  • The new function list : t -> key -> (string * [Value|Dictionary], error) result io was added
  • The function get : t -> key -> (value, error) result io is now provided (used to be named read and requiring an offset and length parameter)
  • The functions last_modified : t -> key -> (int * int64, error) result io and digest : t -> key -> (string, error) result io have been introduced
  • The function size was removed.
  • The signature RW for read-write key-value stores extends RO with three functions set, remove, and batch

There is now a non-persistent in-memory implementation of a read-write key-value store available. Other implementations (such as crunch, mirage-kv-unix, mirage-fs, tar have been adapted, as well as clients of mirage-kv (dns, cohttp, tls)).


The IPv4 implementation now has support for fragment reassembly. Each incoming IPv4 fragment is checked for the "more fragments" and "offset" fields. If these are non-zero, the fragment is processed by the fragment cache, which uses a least recently used data structure of maximum size 256kB content shared by all incoming fragments. If there is any overlap in fragments, the entire packet is dropped (avoiding security issues). Fragments may arrive out of order. The code is heavily unit-tested. Each IPv4 packet may at most be in 16 fragments (to minimise CPU DoS with lots of small fragments), the timeout between the first and last fragment is 10 seconds.

The layering and allocation discipline has been revised. ethernet (now encapsulating and decapsulating Ethernet) and arp (the address resolution protocol) are separate opam packages, and no longer part of tcpip.

At the lowest layer, mirage-net is the network device. This interface is implemented by our different backends (xen, solo5, unix, macos, and vnetif). Some backends require buffers to be page-aligned when they are passed to the host system. This was previously not really ensured: while the abstract type page_aligned_buffer was required, write (and writev) took the abstract buffer type (always constrained to Cstruct.t by mirage-net-lwt). The mtu (maximum transmission unit) used to be an optional connect argument to the Ethernet layer, but now it is a function which needs to be provided by mirage-net.

The Mirage_net.write function now has a signature that is explicit about ownership and lifetime: val write : t -> size:int -> (buffer -> int) -> (unit, error) result io. It requires a requested size argument to be passed, and a fill function which is called with an allocated buffer, that satisfies the backend demands. The fill function is supposed to write to the buffer, and return the length of the frame to be send out. It can neither error (who should handle such an error anyways?), nor is it in the IO monad. The fill function should not save any references to the buffer, since this is the network device's memory, and may be reused. The writev function has been removed.

The Ethernet layer does encapsulation and decapsulation now. Its write function has the following signature: val write: t -> ?src:macaddr -> macaddr -> Ethernet.proto -> ?size:int -> (buffer -> int) -> (unit, error) result io. It fills in the Ethernet header with the given source address (defaults to the device's own MAC address) and destination address, and Ethernet protocol. The size argument is optional, and defaults to the MTU. The buffer that is passed to the fill function is usable from offset 0 on. The Ethernet header is not visible at higher layers.

The IP layer also embeds a revised write signature: val write: t -> ?fragment:bool -> ?ttl:int -> ?src:ipaddr -> ipaddr -> Ip.proto -> ?size:int -> (buffer -> int) -> buffer list -> (unit, error) result io. This is similar to the Ethernet signature - it writes the IPv4 header and sends a packet. It also supports fragmentation (including setting the do-not-fragment bit for path MTU discovery) -- whenever the payload is too big for a single frame, it is sent as multiple fragmented IPv4 packets. Additionally, setting the time-to-live is now supported, meaning we now can implement traceroute! The API used to include two functions, allocate_frame and write, where only buffers allocated by the former should be used in the latter. This has been combined into a single function that takes a fill function and a list of payloads. This change is for maximum flexibility: a higher layer can either construct its header and payload, and pass it to write as payload argument (the buffer list), which is then copied into the buffer(s) allocated by the network device, or the upper layer can provide the callback fill function to assemble its data into the buffer allocated by the network device, to avoid copying. Of course, both can be used - the outgoing packet contains the IPv4 header, and possibly the buffer until the offset returned by fill, and afterwards the payload.

The TCP implementation has preliminary keepalive support.


  • MirageOS 3.0.0 used the 0.2.0 release of solo5
  • The ukvm target was renamed to hvt, where solo5-hvt is the monitoring process
  • Support for FreeBSD bhyve and OpenBSD VMM hypervisor (within the hvt target)
  • Support for ARM64 and KVM
  • New target, a separation kernel developed in SPARK/Ada
  • New target GenodeOS, an operating system framework using a microkernel
  • Debugger support: attach gdb in the host system for improved debugging experience
  • Core dump support
  • Drop privileges on OpenBSD and FreeBSD
  • Block device write fixes (in mirage-block-solo5)


The default random device from the OCaml standard library is now properly seeded using mirage-entropy. In the future, we plan to make the fortuna RNG the default random number generator.

Argument passing to unikernels

The semantics of arguments passed to a MirageOS unikernel used to vary between different backends, now they're the same everywhere: all arguments are concatenated using the whitespace character as separator, and split on the whitespace character again by parse-argv. To pass a whitespace character in an argument, the whitespace now needs to be escaped: --hello=foo\ bar.

Noteworthy package updates

More features and bugfixes

You may also want to read the MirageOS 3.2.0 announcement and the MirageOS 3.3.0 announcement.

Next steps

We are working on further changes which revise the mirage internal build system to dune. At the moment it uses ocamlbuild, ocamlfind, pkg-config, and make. The goal of this change is to make MirageOS more developer-friendly. On the horizon we have MirageOS unikernel monorepos, incremental builds, pain-free cross-compilation, documentation generation, ...

Several other MirageOS ecosystem improvements are on the schedule for 2019, including an irmin 2.0 release, a seccomp target for Solo5, and easier deployment and multiple interface in Solo5.


Visiting the camels - MirageOS spring hack retreat 2018

By Stefanie Schirmer

Original posted on linse's blog.

Jemaa El Fnaa Image by Luc Viatour /

In March 2018, I attended my first MirageOS hack retreat in Morrocco. MirageOS is a library operating system which allows everyone to build very small, specialized operating system kernels that are intended to run directly on the virtualization layer. The application code itself is the guest operating system kernel, and can be deployed at scale without the need for an extra containerization step in between. It is written in OCaml and each kernel is built only with exactly the code that is necessary for the particular application. A pretty different approach from traditional operating systems. Linux feels huge all of a sudden.

I flew in from New York via Casablanca to Marrakesh, and then took a cab to the city center, to the main square, Jemaa El Fnaa. At Cafe de France, Hannes was picking me up and we walked back through the labyrinth of the Medina to the hostel Riad "Priscilla" where we lived with about 20 MirageOS folks, two turtles and a dog. We ate some food, and there were talks about Mirage's quickcheck-style fuzzing library Crowbar, and an API realized on top of a message queue written in OCaml.

Coming from compiler construction in Haskell and building "stateless" services for information retrieval in Scala, I have a good grasp of functional programming. The funny problem is I don't know much about OCaml yet.

At Etsy, I was part of the Core Platform team where we first used hhvm (Facebook's hip-hop virtual machine) on the API cluster, and then advocated to use their gradually typed "hack" language to introduce typing to the gigantic PHP codebase at Etsy. Dan Miller and I added types to the codebase with Facebook's hackificator, but then PHP 7 added the possibility of type annotations and great speedups, and PHP's own static analyzer phan was developed by Rasmus Lerdorf and Andrew Morrison to work with PHP's types. We abandoned the hackification approach. Why is this interesting? These were my first encounters with OCaml! The hack typechecker is written in OCaml, and Dan and I have read it to understand the gradual typing approach. Also, we played with pfff, a tool written in OCaml that allows structural edits on PHP programs, based on the abstact syntax tree. I made a list to translate between Haskell and OCaml syntax, and later Maggie Zhou and I used pfff to unify the syntax of several hundred endpoints in Etsy's internal API.

At the MirageOS retreat, I started my week reading "Real World OCaml", but got stuck because the examples did not work with the buildsystem used in the book. Stephen helped me to find a workaround, I made a PR to the book but it was closed since it is a temporary problem. Also, I started reading about OCaml's "lwt" library for concurrent programming. The abbreviation stands for lightweight threads and the library provides a monadic way to do multithreading, really similar to twitter futures in Scala. Asynchronous calls can be made in a thread, which then returns at some point when the call was successful or failed. We can do operations "inside" lwt with bind (>>=) in the same way we can flatMap over Futures in scala. The library also provides ways to run multiple threads in sequence or in parallel, and to block and wait. In the evening, there was a talk about a high-end smart card that based on a private start value can provide a succession of keys. The hardware is interesting, being the size of a credit card it has a small keypad and a screen. Some banks use these cards already (for their TAN system?), and we all got a sample card to play with.

One day I went swimming with Lix and Reynir, which was quite the adventure since the swimming pool was closed and we were not sure what to do. We still made it to the part that was still open, swam a lot and then got a cake for Hannes birthday which lead to a cake overflow since there were multiple cakes and an awesome party with candles, food and live music already. :D Thanks everyone for organizing!! Happy birthday Hannes!

I started reading another book, "OCaml from the very beginning", and working through it with Kugg. This book was more focused on algorithms and the language itself than on tooling and libraries, and the exercises were really fun to solve. Fire up OCaml's REPL utop and go! :D

At the same time I started reading the code for solo5 to get an understanding of the underlying hypervisor abstraction layer and the backends we compile to. This code is really a pleasure to read. It is called solo5 because of MirageOS's system calls, initially a set of 5 calls to the hypervisor, called hypercalls which sounds really futuristic. :D

So that's the other fun problem: I don't know too much about kernel programming yet. I did the Eudyptula (Linux kernel) challenge, an email-based challenge that sends you programming quests to learn about kernel programming. Over the course of the challenge, I've made my own Linux kernel module that says "Hello world!" but I have not built anything serious yet.

The next things I learned were configuring and compiling a MirageOS unikernel. Hannes showed me how this works. The config system is powerful and can be tailored to the unikernel we are about to build, via a config file. After configuring the build, we can build the kernel for a target backend of our choice. I started out with compiling to Unix, which means all network calls go through unix pipes and the unikernel runs as a simple unix binary in my host system, which is really useful for testing.

The next way to run MirageOS that I tried was running it in ukvm. For this setup you have to change the networking approach so that you can talk from the host system to you unikernel inside ukvm. In Linux you can use the Tun/Tap loopback interface for networking to wire up this connection.

We had a session with Jeremie about our vision for MirageOS which was super fun, and very interesting because people have all kinds of different backgrounds but the goals are still very aligned.

Another thing I learned was how to look at network traffic with wireshark. Sidney and I had previously recorded a TLS handshake with tcpdump and looked at the binary data in the pcap file with "hexfiend" next to Wikipedia to decode what we saw. Derpeter gave me a nice introduction about how to do this with wireshark, which knows about most protocols already and will do the decoding of the fields for us. We talked about all layers of the usual stack, other kinds of internet protocols, the iptables flow, and bgp / peeringDB. Quite interesting and I feel I have a pretty good foundational understanding about how the internet actually works now.

During the last days I wanted to write a unikernel that does something new, and I thought about monitoring, as there is no monitoring for MirageOS yet. I set up a grafana on my computer and sent some simple data packets to grafana from a unikernel, producing little peaks in a test graph. Reynir and I played with this a bit and restructured the program.

After this, the week was over, I walked back to Jemaa el Fnaa with Jeremie, I feel I learned a ton and yet am still at the very beginning, excited what to build next. On the way back I got stuck in a weird hotel in Casablanca due to the flight being cancelled, where I bumped into a Moroccan wedding and met some awesome travelling women from Brazil and the US who also got stuck. All in all a fun adventure!


MirageOS running on the ESP32 embedded chip

By Anil Madhavapeddy

Now that the winter holiday break is over, we are starting to see the results of winter hacking among our community.

The first great hack for 2018 is from Sadiq Jaffer, who got OCaml booting on a tiny and relatively new CPU architecture -- the Espressif ESP32. After proudly demonstrating a battery powered version to the folks at OCaml Labs, he then proceeded to clean it up enough tha it can be built with a Dockerfile, so that others can start to do a native code port and get bindings to the networking interface working.

Read all about it on Sadiq's blog, and thanks for sharing this with us, Sadiq!

We also noticed that another OCaml generic virtual machine for even smaller microcontrollers has shown up on GitHub. This, combined with some functional metaprogramming, may mean that 2018 is the year of OCaml in all the tiny embedded things...


MirageOS Winter 2017 hack retreat roundup

By Hannes Mehnert

MirageOS Winter Hack Retreat, Marrakesh 2017

This winter, 33 people from around the world gathered in Marrakesh for a Mirage hack retreat. This is fast becoming a MirageOS tradition, and we're a little sad that it's over already! We've collected some trip reports from those who attended the 2017 winter hack retreat, and we'd like to thank our amazing hosts, organisers and everyone who took the time to write up their experiences.

We, the MirageOS community, strongly believe in using our own software: this website has been a unikernel since day one^W^W it was possible to run MirageOS unikernels. In Marrakesh we used our own DHCP and DNS server without trouble. There are many more services under heavy development (including git, ssh, ...), which we're looking forward to using soon ourselves.

Several atteendees joined for the second or third time in Marrakesh, and brought their own projects, spanning over graphql, reproducible builds (with application to qubes-mirage-firewall, see holger's report and Gabriel's OCaml fixes for build path variation). A stream of improving error messages in the OCaml compiler (based on Arthur Charguéraud PR) was prepared and merged (PR 1496, PR 1501, PR 1505, PR 1510, and PR 1534). Our OCaml git implementation was rewritten to support git push properly, and this PR was merged. Other projects of interest are awa-ssh, anonymity profiles in DHCP, and fixes to the deployment troubles of our website. There is now a mirage PNG viewer integrated into Qubes and a password manager. Some getting started notes were written down as well as the new learning about MirageOS website.

A huge fraction of the Solo5 contributors gathered in Marrakesh as well and discussed the future, including terminology, the project scope, and outlined a roadmap for merging branches in various states. Adrian from the Muen project joined the discussion, and in the aftermath they are now running their website using MirageOS on top of the Muen separation kernel.

A complete list of fixes and discussions is not available, please bear with us if we forgot anything above. A sneak preview: there will be another retreat in March 2018 in Marrakesh. Following are texts written by individual participants about their experience.

Mindy Preston

I came to Marrakesh for the hack retreat with one goal in mind: documentation. I was very pleased to discover that Martin Keegan had come with the same goal in mind and fresher eyes, and so I had some time to relax, enjoy Priscilla and the sun, photograph some cats, and chat about projects both past and future. In particular, I was really pleased that there's continued interest in building on some of the projects I've worked on at previous hack retreats.

On the way to the first hack retreat, I did some work applying Stephen Dolan's then-experimental American Fuzzy Lop instrumentation to testing the mirage-tcpip library via mirage-net-pcap. (A post on this was one of the first Canopy entries! At this hack retreat, I did a short presentation on the current state of this work:

  • AFL instrumentation was released in OCaml 4.05; switches with it enabled by default are available in opam (opam sw 4.05.0+afl)
  • crowbar for writing generative tests powered by AFL, with an experimental staging branch that shows OCaml code for regenerating failing test cases
  • a companion ppx_deriving plugin for automatic generator discovery based on type definitions
  • bun, for integrating afl tests into CI runs

I was lucky to have a lot of discussions about fuzzing in OCaml, some of which inspired further work and suggestions on some current problems in Crowbar. (Special thanks to gasche and armael for their help there!) I'm also grateful to aantron for some discussions on ppx_bisect motivated by an attempt to estimate coverage for this testing workflow. I was prodded into trying to get Crowbar ready to release by these conversations, and wrote a lot of docstrings and an actual README for the project.

juga0 added some extensions to the charrua-core DHCP library started by Christiano Haesbaert a few hack retreats ago. juga0 wanted to add some features to support more anonymity for DHCP clients, so we did some associated work on the rawlink library, and added an experimental Linux DHCP client for charrua-core itself. I got to write a lot of docstrings for this library!

I was also very excited to see the work that cfcs has been doing on building more interesting MirageOS unikernels for use in QubesOS. I had seen static screenshots of mirage-framebuffer in action which didn't do it justice at all; seeing it in person (including self-hosted slides!) was really cool, and inspired me to think about how to fix some ugliness in writing unikernels using the framebuffer. The experimental password manager is something I hope to be using by the next hack retreat. Maybe 2017 really is the year of unikernels on the desktop!

tg, hannes, halfdan, samoht, and several others (sorry if I missed you!) worked hard to get some unikernel infrastructure up and running at Priscilla, including homegrown DHCP and DNS services, self-hosted pastebin and etherpad, an FTP server for blazing-fast local filesharing, and (maybe most importantly!) a local opam mirror. I hope that in future hack retreats, we can set up a local git server using the OCaml git implementation, which got some major improvements during the hack retreat thanks to dinosaure (from the other side of the world!) and samoht.

Finally, the qubes-mirage-firewall got a lot of attention this hack retreat. (The firewall itself incorporates some past hack retreat work by me and talex5.) h01ger worked on the reproducibility of the build, and cfcs did some work on passing ruleset changes to the firewall -- currently, users of qubes-mirage-firewall need to rebuild the unikernel with ruleset changes.

We also uncovered some strangeness and bugs in the handling of Xen block-storage devices, which I was happy to fix in advance of the more intense use of block storage I expect with wodan and irmin in the near future.

Oh yes, and somewhere in there, I did find time to see some cats, eat tajine, wander around the medina, and enjoy all of the wonder that Priscilla, the Queen of the Medina and her lovely hosts have to offer. Thanks to everyone who did the hard work of organizing, feeding, and laundering this group of itinerant hackers!

Ximin Luo

This was my third MirageOS hack retreat, I continued right where I left off last time.

I've had a pet project for a while to develop a end-to-end secure protocol for group messaging. One of its themes is to completely separate the transport and application layers, by sticking an end-to-end secure session layer in between them, with the aim of unifying all the secure messaging protocols that exist today. Like many pet projects, I haven't had much time to work on it recently, and took the chance to this week.

I worked on implementing a consistency checker for the protocol. This allows chat members to verify everyone is seeing and has seen the same messages, and to distinguish between other members being silent (not sending anything) vs the transport layer dropping packets (either accidentally or maliciously). This is built on top of my as-yet-unreleased pure library for setting timeouts, monitors (scheduled tasks) and expectations (promises that can timeout), which I worked on in the previous hackathons.

I also wrote small libraries for doing 3-valued and 4-valued logic, useful for implementing complex control flows where one has to represent different control states like success, unknown/pending, temporary failure, permanent failure, and be able to compose these states in a logically coherent way.

For my day-to-day work I work on the Reproducible Builds, and as part of this we write patches and/or give advice to compilers on how to generate output deterministically. I showed Gabriel Scherer our testing framework with our results for various ocaml libraries, and we saw that the main remaining issue is that the build process embeds absolute paths into the output. I explained our BUILD_PATH_PREFIX_MAP mechanism for stripping this information without negatively impacting the build result, and he implemented this for the ocaml compiler. It works for findlib! Then, I need to run some wider tests to see the overall effect on all ocaml packages. Some of the non-reproducibility is due to GCC and/or GAS, and more analysis is needed to distinguish these cases.

I had very enjoyable chats with Anton Bachin about continuation-passing style, call-cc, coroutines, and lwt; and with Gabriel Scherer about formal methods, proof systems, and type systems.

For fun times I carried on the previous event's tradition of playing Cambio, teaching it to at least half of other people here who all seemed to enjoy it very much! I also organised a few mini walks to places a bit further out of the way, like Gueliz and the Henna Art Cafe.

On the almost-last day, I decided to submerge myself in the souks at 9am or so and explored it well enough to hopefully never get lost in there ever again! The existing data on OpenStreetMap for the souks is actually er, topologically accurate shall we say, except missing some side streets. :)

All-in-all this was another enjoyable event and it was good to be back in a place with nice weather and tasty food!

Martin Keegan

My focus at the retreat was on working out how to improve the documentation. This decomposed into

  • encouraging people to fix the build for the docs system
  • talking to people to find out what the current state of Mirage is
  • actually writing some material and getting it merged

What I learnt was

  • which backends are in practice actually usable today
  • the current best example unikernels
  • who can actually get stuff done
  • how the central configuration machinery of mirage configure works today
  • what protocols and libraries are currently at the coal-face
  • that some important documentation exists in the form of blog posts

I am particularly grateful to Mindy Preston and Thomas Gazagnaire for their assistance on documentation. I am continuing the work now that I am back in Cambridge.

The tone and pace of the retreat was just right, for which Hannes is due many thanks.

On the final day, I gave a brief presentation about the use of OCaml for making part of a vote counting system, focusing on the practicalities and cost of explaining to laymen the guarantees provided by .mli interface files, with an implicit comparison to the higher cost in more conventional programming languages.

The slides for the talk as delivered are here, but it deserves its own blog post.

Michele Orrù

This year's Marrakech experience has been been a bit less productive than past years'. I indulged a bit more chatting to people, and pair programming with them.

I spent some of my individual time time getting my hands dirty with the Jsonm library, hoping that I would have been able to improve the state of my ocaml-letsencrypt library; I also learned how to integrate ocaml API in C, improving and updating the ocaml-scrypt library, used by another fellow mirage user in order to develop its own password manager. Ultimately, I'm not sure either direction I took was good: a streaming Json library is perhaps not the best choice for an application that shares few jsons (samoht should have been selling more his easyjson library!), and the ocaml-scrypt library has been superseeded by the pure implementation ocaml-scrypt-kdf, which supposedly will make the integration in mirage easier.

The overall warm atmosphere and the overall positive attitude of the group make me still think of this experience as a positive learning experience, and how they say: failure the best teacher is.

Reynir Björnsson

For the second time this year (and ever) I went to Marrakech to participate in the MirageOS hack retreat / unconference. I wrote about my previous trip.

The walk from the airport

Unlike the previous trip I didn't manage to meet any fellow hackers at the RAK airport. Considering the annoying haggling taking a taxi usually involves and that the bus didn't show up last time I decided to walk the 5.3 km from the airport to Priscilla (the venue). The walk to Jemaa el-Fnaa (AKA 'Big Square') was pretty straight forward. Immediately after leaving the airport area I discovered every taxi driver would stop and tell me I needed a ride. I therefore decided to walk on the opposite side of the road. This made things more difficult because I then had more difficulties reading the road signs. Anyway, I found my way to the square without any issues, although crossing the streets on foot requires cold blood and nerves of steel.

Once at the square I noticed a big café with lots of lights that I recognized immediately. I went past it thinking it was Café de France. It was not. I spent about 30-40 minutes practicing my backtracking skills untill I finally gave up. I went back to the square in order to call Hannes and arrange a pickup. The two meeting points at the square was some juice stand whose number I couldn't remember and Café de France, so I went looking for the latter. I quickly realized my mistake, and once I found the correct café the way to Priscilla was easy to remember.

All in all I don't recommend walking unless you definitely know the way and is not carrying 12-15 kg of luggage.


Once there I met new and old friends. Some of the old friends I had seen at Bornhack while others I hadn't seen since March. In either case it was really nice to meet them again! As for the new people it's amazing how close you can get with strangers in just a week. I had some surprisingly personal conversations with people I had only met a few days prior. Lovely people!

My goals

Two months prior to the hack retreat I had started work on implementing the ssh-agent protocol. I started the project because I couldn't keep up with Christiano's awa-ssh efforts in my limited spare time, and wanted to work on something related that might help that project. My goals were to work on my ocaml-ssh-agent implementation as well as on awa-ssh.

Before going to Marrakech I had had a stressful week at work. I had some things to wrap up before going to a place without a good internet connection. I therefore tried to avoid doing anything on the computer the first two days. On the plane to Marrakech I had taken up knitting again - something I hadn't done in at least two years. The morning of the first day I started knitting. Eventually I had to stop knitting because I had drunk too much coffee for me to have steady enough hands to continue, so I started the laptop despite my efforts not to. I then looked at awa-ssh, and after talking with Christiano I made the first (and sadly only) contribution to awa-ssh of that trip: The upstream nocrypto library had been changed in a way that required changes to awa-ssh. I rewrote the digest code to reflect the upstream changes, and refactored the code on suggestion by Christiano.

In ocaml-ssh-agent I was already using angstrom for parsing ssh-agent messages. I rewrote the serialization from my own brittle cstruct manipulations to using faraday. This worked great, except I never quite understood how to use the Faraday_lwt_unix module. Instead I'm serializing to a string and then writing that string to the SSH_AUTH_SOCK.

GADT !!!FUN!!!

The ssh-agent is a request-response protocol. Only a certain subset of the responses are valid for each request. I wanted to encode that relationship into the types so that the user of the library wouldn't have to deal with invalid responses. In order to do that I got help by @aantron to implement this with GADTs. The basic idea is a phantom type is added to the request and response types. The phantom type, called request_type, is a polymorphic variant that reflects the kind of requests that are possible. Each response is parameterized with a subset of this polymorphic variant. For example, every request can fail, so Ssh_agent_failure is parameterized with the whole set, while Ssh_agent_identities_answer is parameterized with `Ssh_agent_request_identities, and Ssh_agent_success is parameterized with `Ssh_agent_successable - a collapse of all the request types that can either return success or failure.

This worked great except it broke the typing of my parser - The compiler can't guess what the type parameter should be for the resulting ssh_agent_response. To work around that @gasche helped me solve that problem by introducing an existential type:

    type any_ssh_agent_response = Any_response : 'a ssh_agent_response -> any_ssh_agent_response

Using this I could now write a function unpack_any_response which 'discards' every response that doesn't make sense for a particular request. Its type is the following:

    val unpack_any_response : 'a ssh_agent_request -> any_ssh_agent_response ->
                              ('a ssh_agent_response, string) result

Now I want to write a listen function that takes a handler of type 'a ssh_agent_request -> 'a ssh_agent_response, in other words a handler that can only create valid response types. This unfortunately doesn't type check. The parser returns an existential type any_ssh_agent_request = Any_request : 'req_type ssh_agent_request -> any_ssh_agent_request. This is causing me a problem: the 'req_type existential would escape. I do not know how to solve this problem, or if it's possible to solve it at all. I discussed this issue with @infinity0 after the retreat, and we're not very optimistic. Perhaps someone in #ocaml on Libera Chat might know a trick.

    let listen ((ic, oc) : in_channel * out_channel)
        (handler : 'a Ssh_agent.ssh_agent_request -> 'a Ssh_agent.ssh_agent_response) =
      match Angstrom_unix.parse Ssh_agent.Parse.ssh_agentc_message ic with
      | { len = 0; _ }, Ok (Ssh_agent.Any_request request) ->
        Ok (Ssh_agent.Any_response (handler response))
      | { len; _ }, Ok _ ->
        Error "Additional data in reply"
      | _, Error e ->
        Error e

Ideas for uses of ocaml-ssh-agent

Besides the obvious use in a ssh-agent client in a ssh client, the library could be used to write an ssh-agent unikernel. This unikernel could then be used in Qubes OS in the same way as Qubes Split SSH where the ssh-agent is running in a separate VM not connected to the internet. Furthermore, @cfcs suggested an extension could be implemented such that only identities relevant for a specific host or host key are offered by the ssh-agent. When one connects to e.g. using ssh keys all the available public keys are sent to the server. This allows the server to do finger printing of the client since the set of keys is likely unique for that machine, and may leak information about keys irrelevant for the service (Github). This requires a custom ssh client which may become a thing with awa-ssh soon-ish.

Saying goodbye

Leaving such lovely people is always difficult. The trip to the airport was emotional. It was a chance to spend some last few moments with some of the people from the retreat knowing it was also the last chance this time around. I will see a lot of the participants at 34c3 in 3 weeks already, while others I might not see again in the near future. I do hope to stay in contact with most of them online!

Thank you for yet another great retreat!

Many thanks to everyone involved! The hostel is already booked for another retreat in March 2018...


MirageOS March 2017 hack retreat roundup

By Hannes Mehnert, Gemma Gordon, Anil Madhavapeddy

Cambio, OCaml and Karaoke: MirageOS Hack Retreat, Marrakech 2017

This March, 34 people from around the world gathered in Marrakech for a spring Mirage hack retreat. This is fast becoming a MirageOS tradition, and we're a little sad that it's over already! We've collected some trip reports from those who attended the 2017 Hack Retreat, and we'd like to thank our amazing hosts, organisers and everyone who took the time to write up their experiences. Props go especially to Hannes Mehnert who initiated the event and took care of many of the logistics, and to Gemma Gordon for designing and printing limited edition t-shirts especially for the occasion!

In addition to the reports below, you can find other information online:

Hannes Mehnert

At the retreat, 34 people from all around the world (mainly Western Europe) interested in MirageOS gathered for a week in Marrakech.

Numerous social contacts, political discussions, technical challenges were discussed in smaller and bigger groups. Lots of pull requests were opened and merged - we kept the DSL line busy with git pushes and pulls :) - sometimes overly busy.

In contrast to last year, we organised several events:

  • Body self-awareness workshop (by the resident dancers)
  • Hiking to waterfalls on Sunday
  • Hamam visit on Monday
  • Herbalist visit on Tuesday
  • Talk by the resident dancers on Tuesday
  • A public talk led by Amir on Saturday (highly appreciated, it was announced rather late, only ~10 external people showed up)

Several voluntary presentations on topics of interest to several people:

  • "Reverse engineering MirageOS with radare2 (and IDA pro)" by Alfredo (Alfredo and Chris tried afterwards the link-time optimization branch of OCaml, which does not seem to have any effect at all (there may be something missing from the 4.04.0+trunk+forced_lto switch))
  • "Introduction to base" by Spiros
  • "Solo5" (or rather: what is below the OCaml runtime, xen vs solo5) by Mato
  • "Angstrom intro" by Spiros

After the week in Marrakech, I was sad to leave the place and all the nice people. Fortunately we can interact via the Internet (on IRC, GitHub, Mail, ...) on projects which we started or continued to work on at the retreat.

It was a very nice week, I met lots of new faces. These were real people with interesting stories, and I could finally match email addresses to faces. I was delighted to share knowledge about software I know to other people, and learned about other pieces of software.

My personal goal is to grow a nice and diverse community around MirageOS, and so far I have the feeling that this is coming along smoothly.

Thanks again to everybody for participating (on-site and remote) and special thanks to OCaml Labs for support, and Gemma Gordon for the limited edition t-shirts (design and logistics)!

Ximin Luo

Good people, good food, good weather, what more could you ask for? This year's MirageOS hackathon was a blast, like last year.

I started off the week by giving a monad tutorial to a few people - introducing the terminology around it, the motivation behind it, giving a few concrete examples and exercises, and relating it to some basic category theory.

Since last year, I've been working on-and-off on a group messaging protocol. One of its aims is to completely separate the transport and application layers, by sticking an end-to-end secure session layer in between them. This could help to unify all the messaging protocols that exist today or it could make the problem worse, time will tell how this works out in the end. :)

Another of my interests is to write more code that is obviously-more-secure, using strong type systems that provide compile-time guarantees about what your code can or can't do. As part of bringing these two concepts together, I've been working on writing a pure library for doing scheduled (timed) computations - i.e., to express "do this in X time in the future" then actually do it. This is very important in real world security systems, where you can't wait for too long for certain events to happen, otherwise you'll be susceptible to attacks.

To give the game away, the utility is just a state monad transformer where the state is a schedule data structure that records the tasks to be performed in the future, together with a pure monadic runner that executes these tasks but is triggered by impure code that knows the "real" time. However, implementing the specifics so that user code is composable and still looks (relatively) nice, has taken quite some effort to figure out. There are various other nice properties I added, such as being able to serialise the schedule to disk, so the behaviour is preserved across program shutdowns.

Using this pure lower-level control-flow utility, we can build slightly higher-level utilities, such as a "monitor" (something that runs a task repeatedly, e.g. useful for resending algorithms) or an "expectation" (a promise/future that can time out, and also runs a monitor to repeatedly "try" to succeed, while it is not yet succeeded or failed, which is useful for deferring high-level security properties but not forgetting about them, a very common pattern). I spent much of the week building these things and testing them, and using this practical experience to refine the APIs for the low-level scheduled computations.

I also did some more short-term work to spread type-safe languages to more audiences, packaging OCaml 4.04 for Debian, and also reporting and working around some test failures for rustc 1.15.1 on Debian, earning me the label of "traitor" for a while. :p

I wrote more documentation for my in-progress contribution to the ocaml-lens library, to bring traverse-once "van Laarhoven" lens to OCaml, similar to the ones in Haskell. I had some very interesting discussions with Jens and Rudi on Rust, Haskell, OCaml and various other "cutting-edge" FP research topics. Rudi also gave some useful feedback on my ocaml-lens code as well as some other pure functional utilities that I've been developing for the messaging protocol mentioned above, thanks Rudi!

Viktor and Luk taught us how to play Cambio and we in turn taught that to probably 10 more people around the hostel, including some non-mirage guests of the hostel! It was very enjoyable playing this into the early hours of the morning.

On one of the evenings Jurre and I got drunk and did some very diverse and uncensored karaoke and eventually embarassed^H^H^H^H^H^H^H persuaded a lot of the others to join us in the fun and celebrations. We'll be back next year with more, don't worry!

Michele Orrù

Last summer I started, while being an intern in Paris, a let's encrypt (or rather ACME.

Let's encrypt is a certificate authority which issues signed certificates via an automated service (using the ACME protocol). Even though it is still in the process of being standardized, the first eCA already launched in April 2016, as a low-cost alternative to commercial CAs (where you usually need to provide identity information (passport) for verification).

If you want to run a secure service on your domain, such as HTTPS, STARTTLS in SMTP, IMAPS, ..., you have to generate a private key and a certificate signing request (CSR). You then upload this CSR via HTTP to the let's encrypt server and solve a some "challenge" proposed by the server in order to certify you own the requested domain.

At the time of the hack retreat, the following challenges were supported:

  • TLS (using the SNI extension),
  • DNS (setting a TXT record), or
  • HTTP (replying to a particular request at some ".well_known" url),

In order to reach a working implementation, I had to implement myself a JSON web signature, and a JSON web key library in OCaml.

My goal for the hack retreat was to polish this library, get it up to date with the new internet standards, and present this library to the Mirage community, as I do believe it could be the cornerstone for bootstrapping a unikernel on the internet having encryption by default. I was impressed by the overwhelming interest of the participants and their interest in helping out polishing this library. I spent a lot of time reviewing pull requests and coding with people I had just met. For instance, Reynir ported it to the topkg packager, cleaned up the dependencies and made it possible to have a certificate for multiple domains. Viktor and Luk helped out implementing the DNS challenge. Aaron helped out adding the new internet draft.

While busy reviewing and merging the pull requests, and extending Canopy to automatically renew its certificates (WIP on this feature branch). My library is still not released, but I will likely do an initial release before the end of the month, after some more tests.

This was the second time I attended the hack retreat, and it's been quite different: last year I was mostly helping out people, uncovering bugs and reporting documentation. This time it was other people helping me out and uncovering bugs on my code. The atmosphere and cooperation between the participants was amazing: everybody seemed to have different skills and be pleased to explain their own area of expertise, even at the cost of interrupting their own work. (I'd have to say sorry to Mindy and Thomas for interrupting too often, but they were sooo precious!) I particularly enjoyed the self-organized sessions: some of them, like Ximin's one on monads, even occurred spontaneously!

Mindy Preston

Update 2017: Morocco, Marrakesh, the medina, and Priscilla are still sublime. Thank you very much to Hannes Mehnert for organizing and to the wonderful Queens at Priscilla for creating an excellent space and inviting us to inhabit it.

I tried to spend some time talking with people about getting started with the project and with OCaml. There's still a thirst for good-first-bug which isn't met by "please implement this protocol". People are also eager for intermediate-level contributions; people are less resistant to "please clean up this mess" than I would have expected. I think that figuring out how to make cross-cutting changes in Mirage is still not very accessible, and would be a welcome documentation effort; relatedly, surfacing the set of work we have to do in more self-contained packages would go a long way to filling that void and is probably easier.

People were excited about, and did, documentation work!! And test implementation!! I was so excited to merge all of the PRs improving READMEs, blog entries, docstrings, and all of the other important bits of non-code that we haven't done a good job of keeping up with. It was amazing to see test contributions to our existing repositories, too -- we have our first unit test touching ipv6 in tcpip since the ipv6 modules were added in 2014. :D Related to the previous bullet point, it would be great to point at a few repositories which particularly need testing and documentation attention -- I found doing that kind of work for mirage-tcpip very helpful when I was first getting started, and there's certainly more of it to do there and in other places as well.

I spent a lot less time on install problems this year than last year, and a lot more time doing things like reviewing code, seeing cats, merging PRs, exploring the medina, cutting releases, climbing mountains, and pairing with people on building and testing stuff. \o/

Presentations from folks were a great addition! We got introductions to Angstrom and Base from Spiros, a tour through reversing unikernels with radare2 from Alfredo, and a solo5 walkthrough from Martin. Amir gave a great description of MirageOS, OCaml, and use cases like Nymote and Databox for some of our fellow guests and friends of the hostel. My perception is that we had more folks from the non-Mirage OCaml community this year, and I think that was a great change; talking about jbuilder, Base, Logs, and Conduit from new perspectives was illuminating. I don't have much experience of writing OCaml outside of Mirage and it's surprisingly easy (for me, anyway) to get siloed into the tools we already use and the ways we already use them. Like last year, we had several attendees who don't write much OCaml or don't do much systems programming, and I'm really glad that was preserved -- that mix of perspectives is how we get new and interesting stuff, and also all of the people were nice :)

There were several projects I saw more closely for the first time and was really interested in: g2p's storage, timada's performance harness; haesbaert's awa-ssh; maker's ocaml-acme; and there were tons of other things I didn't see closely but overheard interesting bits and pieces of!

Rereading the aggregated trip report from the 2016 spring hack retreat, it's really striking to me how much of Mirage 3's work started there; from this year's event, I think Mirage 4 is going to be amazing. :)

Viktor Baluch & Luk Burchard:

“Let’s make operating systems great again” – with this in mind we started our trip to Marrakech. But first things first: we are two first year computer science students from Berlin with not a whole lot of knowledge of hypervisors, operating systems or functional programming. This at first seems like a problem… and it turned out it was :). The plan was set, let’s learn this amazing language called OCaml and start hacking on some code, right? But, as you could imagine, it turned out to be different yet even better experience. When we arrived, we received a warm welcome in Marrakech from very motivated people who were happy to teach us new things from their areas of expertise. We wanted to share some of our valuable knowledge as well, so we taught some people how to play Cambio, our favourite card game, and it spread like wildfire (almost everyone was playing it in the second evening). We’re glad that we managed to set back productivity in such a fun way. ;P

Back to what we came to Morocco for: as any programming language, OCaml seems to provide its special blend of build system challenges. Rudi was kind enough to help us navigate the labyrinth of distribution packages, opam, and ocamlfind with great patience and it took us only two days to get it almost right.

Finally having a working installation, we got started by helping Michele with his ocaml-acme package, a client for Let's Encrypt (and other services implementing the protocol). An easy to use and integrate client seemed like one feature that could provide a boost to unikernel adoption and it looked like a good match for us as OCaml beginners since there are many implementations in other programming languages that we could refer to. After three days we finally made our first Open Source OCaml contributions to this MirageOS-related project by implementing the dns-01 challenge.

Hacking away on OCaml code of course wasn’t the only thing we did in Marrakech: we climbed the Atlas mountains to see the seven magic waterfalls (little disclaimer: there are only four). It was not a really productive day but great for building up the spirit which makes the community so unique and special. Seeing camels might also helped a little bit. ;)

One of the most enjoyable things that the retreat provided was the chance for participants to share knowledge through presentations which lead to very interesting conversations like after Amir’s presentation when some artists asked about sense of life and computer systems (by the way, one question is already solved and it is ’42’). We were also very impressed by the power and expressiveness of functional languages which Sprios demonstrated in his parser combinator Angstrom.

Thank you to everyone involved for giving us the experience of an early ‘enlightenment’ about functional programming as first year students and the engaging discussions with so many amazing people! We sure learned a lot and will continue working with OCaml and MirageOS whenever possible.

Hope to see all of you again next time!

Aaron Zauner

I flew from Egypt to Marrakech not sure what to expect, although I'm not new to functional programming, I'm a total OCaml novice and haven't worked on unikernels - but have always been interested in the topic. Hannes invited me to hang out and discuss, and that's exactly what I did. I really enjoyed spending my time with and meeting all of you. Some of you I have known "from the interwebs" for a while, but never met in person, so this was a great opportunity for me to finally get to see some of you in real life. I spent most of my time discussing security topics (everything from cryptography, bootstrapping problems to telco/ mobile security), operating system design and some programming language theory. I got to know the OCaml environment, a bit more about MirageOS and I read quite a few cryptography and operating system security papers.

All of the people I spoke with were very knowledgeble - and I got to see what people exactly work on in MirageOS - which certainly sparked further interest in the project. I've been to Morocco a couple of times but the food we got at Queens of the Medina was by far the best food I've eaten in Morocco so far. I think the mix of nerds and artists living at the Riad was really inspiring for all of us, I was certainly interested in what they were working on, and they seemed to be interested in what all of these freaky hackers were about too. Living together for more than a week gives the opportunity to get to know people not only on a technical level but -- on a personal level, in my opinion we had a great group of people. Giving back to the local community by giving talks on what we're doing at the Hackathon was a great idea, and I enjoyed all of the talks that I've attended. I've been to a few hackathons (and even organized one or two), but this one has certainly been the most enjoyable one for me. People, food, location and the discussions (also Karaoke and learning to play Cambio!) I've had will make me remember the time I spent with you guys for a long time. I hope I'm able to join again at some point (and actually contribute to code not only discussions) in the future. Unfortunately I cannot give any feedback on possible improvements, as I think we had a very well selected group of people and perfect conditions for a Hackathon, could not think of how to organize it better - Thank you Hannes!

Thomas Leonard

This was my second time at the hackathon, and it was great to see everyone and work on Mirage stuff again! I brought along a NUC which provided an additional wireless access point, running a Mirage/Xen DHCP server using haesbaert's charrua library - one of the fruits of last year's efforts.

My goal this year was to update qubes-mirage-firewall to support Mirage 3 and the latest version of mirage-nat, and to add support for NAT of ICMP messages (so that ping works and connection errors are reported). In the process, I converted mirage-nat to use the new parsers in the Mirage 3 version of the tcpip library, which cleaned up the code a lot. It turned out that the firewall stressed these parsers in new ways and we were able to make them more robust as a result. Having Mirage 3 release manager and mirage-nat author yomimono on hand to help out was very useful!

It was great to see so many QubesOS users there this year. Helping them get the firewall installed motivated me to write some proper installation instructions for qubes-test-mirage.

After the hackathon, I also updated mirage-nat to limit the size of the NAT table (using pqwy's lru) and made a new release of the firewall with all the improvements.

ComposMin was looking for a project and I hopefully suggested some tedious upgrading and build system porting work. He accepted!! So, qubes-mirage-skeleton now works with Mirage 3 and mirage-profile has been ported to topkg - something I had previously attempted and failed at.

Rudi gave me an introduction to the new jbuilder build tool and I look forward to converting some of my projects to use it in the near future.

Particularly useful for me personally was the chance discovery that Ximin Luo is a Debian Developer. He signed my GPG key, allowing me to complete a Debian key rollover that I began in May 2009, and thus recover the ability to update my package again.

I also wanted to work on irmin-indexeddb (which allows web applications to store Irmin data in the browser), but ran out of time - maybe next year...

Many thanks to hannesm for organising this!

Amir Chaudhry

This was my first time at the Marrakech hack retreat. I was only there for about half the time (mostly the weekend) and my goal was simply to meet people and understand what their experiences have been. Having missed the inaugural event last year, I wasn't sure what to expect in terms of format/event. What I found was a very relaxed approach with lots of underlying activity. The daily stand ups just before lunch were well managed and it was interesting to hear what people were thinking of working on, even when that included taking a break. The food was even more amazing than I'd been led to believe by tweets :)

Somehow, a few hours after I arrived, Hannes managed to sweet-talk me in to giving a presentation the next day about MirageOS to the artists and dance troupe that normally make use of the venue. Since we'd taken over the place for a week — displacing their normal activities — our host thought it would be helpful if someone explained "what the nerds are doing here". This was an unexpected challenge as getting across the background for MirageOS involves a lot of assumed knowledge about operating system basics, software development, software itself, the differences between end-users and developers, roughly how the internet works, and so on. There's a surprising number of things that we all just 'know', which the average software user has no clue about. I hadn't given a talk to that kind of audience before so I spent half a day scrambling for analogies before settling on one that seemed like it might work — involving houses, broken windows, and the staff of Downton Abbey. The talk led to a bunch of interesting discussions with the artists which everyone got involved with. I think the next time I do this, I might also add an analogy around pizza (I have many ideas on this theme already). If you're interested in the slides themselves (mostly pics), there's a PDF at

I also had time to chat with Mindy about an upcoming talk on MirageOS 3.0, and Martin about future work on Solo5. The talks and demos I saw were really useful too and sharing that knowledge with others in this kind of environment was a great idea. Everyone loved the t-shirts and were especially pleased to see me as it turned out I was bringing many of the medium-sized ones. One of the best things about this trip was putting names and faces to GitHub handles, though my brain regularly got the mapping wrong. :)

Overall, this was an excellent event and now that it's happened twice, I think we can call it a tradition. I'm looking forward to the next one!

Jurre van Bergen

I spent most of my time reading up on functional programming and setting up an developer environment and helped with some small things here and there. I didn't feel confident to do a lot of code yet, but it was a very nice environment to ask questions in, especially as a newcomer to MirageOS and OCaml!

I plan to do more OCaml in my spare time and play more with MirageOS in the future. Maybe someday, we can actually merge in some MirageOS things into Tails. I hope to actually do some OCaml code with people next year! Next to that, there was also some time to relax, climbing the Atlas mountains was a welcome change of scenery after reading through up on functional programming for a couple of days. Will definitely do that again some day!

Next to that, shout out to Viktor and Luke for teaching us how to play Cambio, we had a lot of fun with it the entire retreat in the evenings! I was excited to learn that so many people were actually into karaoke, I hope those who don't will join us next year ;-)

Reynir Björnsson

A work in progress from Reynir is his work on documentation in the toplevel:

As mentioned on the midday talkie talkie I've made a OCaml toplevel directive for querying documentation (if available). It's available here To test it out you can install it with opam pin: opam pin add ocp-index-top

It doesn't depend on opam-lib. opam-lib is yuuuuge and the API is unstable. Instead I shell out to opam directly similar to how ocp-browser works. This means installing the package is less likely to make a mess in your dependencies.

There is one issue I don't know how to fix (see issue #1). When requiring ocp-index-top the compiler-libs and ocp-index.lib libraries are pulled into scope which is not cool and totally unnecessary.

Many thanks to everyone involved! The hackathon is already booked for next year in the same place...


Easy distributed analytics with Irmin 1.0

By Thomas Gazagnaire

I am really happy to announce the release of Irmin 1.0, which fully supports MirageOS 3.0 and which brings a simpler and yet more expressive API. Irmin is a library for designing Git-like distributed databases, with built-in branching, snapshoting, reverting and auditing capabilities. With Irmin, applications can create tailored mergeable datastructures to scale seamlessly. Applications built on top of Irmin include Tezos, a distributed ledger, Datakit, a distributed and reactive key-value store, and cuekeeper, a web-based GTD system. Read "Introducing Irmin: Git-like distributed, branchable storage" for a description of the concepts and high-level architecture of the system.

To install Irmin 1.0:

opam install irmin

The running example in this post will be an imaginary model for collecting distributed metrics (for instance to count network packets). In this model, every node has a unique ID, and uses Irmin to store metrics names and counters. Every node is also a distributed collector and can sync with the metrics of other nodes at various points in time. Users of the application can read metrics for the network from any node. We want the metrics to be eventually consistent.

This post will describe:

  • how to define the metrics as a mergeable data-structures;
  • how to create a new Irmin store with the metrics, the basic operations that are available and how to define atomic operations; and
  • how to create and merge branches.

Mergeable Contents

Irmin now exposes Irmin.Type to create new mergeable contents more easily. For instance, the following type defines the property of simple metrics, where name is a human-readable name and gauge is a metric counting the number of occurences for some kind of event:

type metric = {
  name : string;
  gauge: int64;

First of all, we need to reflect the structure of the type, to automatically derive serialization (to and from JSON, binary encoding, etc) functions:

let metric_t =
  let open Irmin.Type in
  record "metric" (fun name gauge -> { name; gauge })
  |+ field "name"  string (fun t ->
  |+ field "gauge" int64    (fun t -> t.gauge)
  |> sealr

record is used to describe a new (empty) record with a name and a constructor; field describes record fields with a name a type and an accessor function while |+ is used to stack fields into the record. Finally |> sealr seals the record, e.g. once applied no more fields can be added to it.

All of the types in Irmin have such a description, so they can be easily and efficiently serialized (to disk and/or over the network). For instance, to print a value of type metric as a JSON object, one can do:

let print m = "%a\n%!" (Irmin.Type.pp_json metric_t) m

Once this is defined, we now need to write the merge function. The consistency model that we want to define is the following:

  • name : can change if there is no conflicts between branches.

  • gauge: the number of events seen on a branch. Can be updated either by incrementing the number (because events occured) or by syncing with other nodes partial knowledge. This is very similar to conflict-free replicated datatypes and related vector-clock based algorithms. However, in Irmin we keep the actual state as simple as possible: for counters, it is a single integer -- but the user needs to provide an external 3-way merge function to be used during merges.

Similarly to the type definitions, the 3-way merge functions can defined using "merge" combinators. Merge combinators for records are not yet available (but they are planned on the roadmap), so we need to use to map the record definition to a pair:

let merge =
  let open Irmin.Merge in
  like metric_t (pair string counter)
    (fun x ->, x.gauge)
    (fun (name, gauge) -> {name; gauge })
  |> option

The final step to define a mergeable data-structure is to wrap everything into a module satisfying the Irmin.Contents.S signature:

module Metric: Irmin.Contents.S with type t = metric = struct
  type t = metric
  let t = metric_t
  let merge = merge
  let pp = Irmin.Type.pp_json metric_t
  let of_string s =
    Irmin.Type.decode_json metric_t (Jsonm.decoder (`String s))

Creating an Irmin Store

To create a key/value store to store metrics, using the on-disk Git format:

module Store = Irmin_unix.Git.FS.KV(Metric)
let config = Irmin_git.config "/tmp/irmin"
let info fmt = ~author:"Thomas" fmt

Store exposes various functions to create and manipulate Irmin stores. config is used to configure Irmin repositories based on Store. In that example we decided to keep the store state in "/tmp/irmin" (which can be inspected using the usual Git tools). info is the function used to create new commit information: use the usual POSIX clock for timestamps, and can also be tweaked to specify the author name.

The most common functions to create an Irmin store are Store.Repo.create to create an Irmin repository and Store.master to get a handler on the master branch in that repository. For instance, using the OCaml toplevel:

# open Lwt.Infix;;

# let repo = Store.Repo.v config;;
val repo : Store.Repo.t Lwt.t = <abstr>
# let master = repo >>= fun repo -> Store.master repo;;
val master : Store.t Lwt.t = <abstr>

Store also exposes the usual key/value base operations using find and set. All the operations are reflected as Git state. begin
      Store.Repo.v config >>= Store.master >>= fun master ->
      Store.set master
        ~info:(info "Creating a new metric")
        ["vm"; "writes"] { name = "write Kb/s"; gauge = 0L }
      >>= fun () ->
      Store.get master ["vm"; "writes"] >|= fun m ->
      assert (m.gauge = 0L);

Note that Store.set is atomic: the implementation ensures that no data is ever lost, and if someone else is writing on the same path at the same, the operation is retried until it succeeds (see optimistic transaction control). More complex atomic operations are also possible: the API also exposes function to read and write subtrees (simply called trees) instead of single values. Trees are very efficient: they are immutable so all the reads are cached in memory and done only when really needed; and write on disk are only done the final transaction is commited. Trees are also stored very efficiently in memory and on-disk as they are deduplicated. For users of previous releases of Irmin: trees replaces the concept of views, but have a very implementation and usage.

An example of a tree transaction is a custom-defined move function:

let move t src dst =
  Store.with_tree t
    ~info:(info "Moving %a to %a" Store.Key.pp src Store.Key.pp dst)
    [] (fun tree ->
          let tree = match tree with
            | None -> Store.Tree.empty
            | Some tree -> tree
          Store.Tree.get_tree tree src >>= fun v ->
          Store.Tree.remove tree src >>= fun _ ->
          Store.Tree.add_tree tree dst v >>= Lwt.return_some

Creating and Merging Branches

They are two kinds of stores in Irmin: permanent and temporary ones. In Git-speak, these are "branches" and "detached heads". Permanent stores are created from branch names using Store.of_branch (Store.master being an alias to Store.of_branch Store.Branch.master) while temporary stores are created from commit using Store.of_commit.

The following example show how to clone the master branch, how to make concurrent update to both branches, and how to merge them back.

First, let's define an helper function to increment the /vm/writes gauge in a store t, using a transaction:

let incr t =
  let path = ["vm"; "writes"] in
  Store.with_tree ~info:(info "New write event") t path (fun tree ->
      let tree = match tree with
        | None -> Store.Tree.empty
        | Some tree -> tree
      (Store.Tree.find tree [] >|= function
        | None   -> { name = "writes in kb/s"; gauge = 0L }
        | Some x -> { x with gauge = Int64.succ x.gauge })
      >>= fun m ->
      Store.Tree.add tree [] m
      >>= Lwt.return_some

Then, the following program create an empty gauge on master, increment the metrics, then create a tmp branch by cloning master. It then performs two increments in parallel in both branches, and finally merge tmp back into master. The result is a gauge which have been incremented three times in total: the "counter" merge function ensures that the result counter is consistent: see KC's blog post for more details about the semantic of recursive merges.

let () = begin
    Store.Repo.v config >>= Store.master >>= fun master -> (* guage 0 *)
    incr master >>= fun () -> (* gauge = 1 *)
    Store.clone ~src:master ~dst:"tmp" >>= fun tmp ->
    incr master >>= fun () -> (* gauge = 2 on master *)
    incr tmp    >>= fun () -> (* gauge = 2 on tmp *)
    Store.merge ~info:(info "Merge tmp into master") tmp ~into:master
    >>= function
    | Error (`Conflict e) -> failwith e
    | Ok () ->
      Store.get master ["vm"; "writes"] >|= fun m -> "Gauge is %Ld\n%!" m.gauge


Irmin 1.0 is out. Defining new mergeable contents is now simpler. The Irmin API to create stores as also been simplified, as well as operations to read and write atomically. Finally, flexible first-class support for immutable trees has also been added.

Send us feedback on the MirageOS mailing-list or on the Irmin issue tracker on GitHub.


Adding the Qubes target to Mirage

By Mindy Preston

slightly easier unikernels on the desktop

When I got a new laptop in early 2016, I decided to try out this QubesOS all the cool kids were talking about. QubesOS also runs a hypervisor, but it nicely supports running multiple virtual machines for typical user tasks, like looking at cat photos with a web browser, viewing a PDF, listening to music, or patching MirageOS. QubesOS also uses Xen, which means we should be able to even run our MirageOS unikernels on it... right?

The answer is yes, after a fashion. Thomas Leonard did the hard work of writing mirage-qubes, a library that interfaces nicely with the QubesOS management layer and allows MirageOS unikernels to boot, configure themselves, and run as managed by the Qubes management system. That solution is nice for generating, once, a unikernel that you're going to run all the time under QubesOS, but building a unikernel that will boot and run on QubesOS requires QubesOS-specific code in the unikernel itself. It's very unfriendly for testing generic unikernels, and as the release manager for Mirage 3, I wanted to do that pretty much all the time.

The command-line mirage utility was made to automatically build programs against libraries that are specific to a target only when the user has asked to build for that target, which is the exact problem we have! So let's try to get to mirage configure -t qubes.

teach a robot to do human tricks

In order for Qubes to successfully boot our unikernel, it needs to do at least two (but usually three) things:

  • start a qrexec listener, and respond to requests from dom0
  • start a qubes-gui listener, and respond to requests from dom0
  • if we're going to do networking (usually we are), get the network configuration from qubesdb

There's code for doing all of these available in the mirage-qubes library, and a nice example available at qubes-mirage-skeleton. The example at qubes-mirage-skeleton shows us what we have to plumb into a MirageOS unikernel in order to boot in Qubes. All of the important stuff is in We need to pull the code that connects to RExec and GUI:

(* Start qrexec agent, GUI agent and QubesDB agent in parallel *)
   let qrexec = RExec.connect ~domid:0 () in
   let gui = GUI.connect ~domid:0 () in

qrexec and gui are Lwt threads that will resolve in the records we need to pass to the respective listen functions from the RExec and GUI modules. We'll state the rest of the program in terms of what to do once they're connected with a couple of monadic binds:

    (* Wait for clients to connect *)
    qrexec >>= fun qrexec ->
    let agent_listener = RExec.listen qrexec Command.handler in

agent_listener is called much later in the program. It's not something we'll use generally in an adaptation of this code for a generic unikernel running on QubesOS -- instead, we'll invoke RExec.listen with a function that disregards input.

    gui >>= fun gui ->
    Lwt.async (fun () -> GUI.listen gui);

We use gui right away, though. Lwt.async lets us start an Lwt thread that the rest of our program logic isn't impacted by, but needs to be hooked into the event loop. The function we define in this call asks GUI.listen to handle incoming events for the gui record we got from GUI.connect.

qubes-mirage-skeleton does an additional bit of setup:

    Lwt.async (fun () ->
      OS.Lifecycle.await_shutdown_request () >>= fun (`Poweroff | `Reboot) ->
      RExec.disconnect qrexec

This hooks another function into the event loop: a listener which hears shutdown requests from OS.Lifecycle and disconnects RExec when they're heard. The disconnect has the side effect of terminating the agent_listener if it's running, as documented in mirage-qubes.

qubes-mirage-skeleton then configures its networking (we'll talk about this later) and runs a test to make sure it can reach the outside world. Once that's finished, it calls the agent_listener defined above, which listens for commands via RExec.listen.

making mirageos unikernels

Building MirageOS unikernels is a three-phase process:

  • mirage configure: generate unifying your code with the devices it needs
  • make depend: make sure you have the libraries required to build the final artifact
  • make: build your application against the specified configuration

In order to get an artifact that automatically includes the code above, we need to plumb the tasks above into, and the libraries they depend on into make depend, via mirage configure.

let's quickly revisit what impl passing usually looks like

Applications built to run as MirageOS unikernels are written as OCaml functors. They're parameterized over OCaml modules providing implementations of some functionality, which is stated as a module type. For example, here's a MirageOS networked "hello world":

module Main (N: Mirage_net_lwt.S) = struct

  let start (n : N.t)  =
    N.write n @@ Cstruct.of_string "omg hi network" >>= function
    | Error e -> Log.warn (fun f -> f "failed to send message"); Lwt.return_unit
    | Ok () -> (fun f -> f "said hello!"); Lwt.return_unit


Our program is in a module that's parameterized over the module N, which can be any module that matches the module type Mirage_net_lwt.S. The entry point for execution is the start function, which takes one argument of type N.t. This is the usual pattern for Mirage unikernels, powered by Functoria's invocation of otherworldly functors.

But there are other modules which aren't explicitly passed. Since MirageOS version 2.9.0, for example, a Logs module has been available to MirageOS unikernels. It isn't explicitly passed as a module argument to Main, because it's assumed that all unikernels will want to use it, and so it's always made available. The OS module is also always available, although the implementation will be specific to the target for which the unikernel was configured, and there is no module type to which the module is forced to conform.

providing additional modules

Let's look first at fulfilling the qrexec and gui requirements, which we'll have to do for any unikernel that's configured with mirage configure -t qubes.

When we want a module passed to the generated unikernel, we start by making a job. Let's add one for qrexec to lib/

let qrexec = job

and we'll want to define some code for what mirage should do if it's determined from the command-line arguments to mirage configure that a qrexec is required:

let qrexec_qubes = impl @@ object
  inherit base_configurable
  method ty = qrexec
  val name = Name.ocamlify @@ "qrexec_"
  method name = name
  method module_name = "Qubes.RExec"
  method packages = Key.pure [ package "mirage-qubes" ]
  method configure i =
    match get_target i with
    | `Qubes -> R.ok ()
    | _ -> R.error_msg "Qubes remote-exec invoked for non-Qubes target."
  method connect _ modname _args =
      "@[<v 2>\
       %s.connect ~domid:0 () >>= fun qrexec ->@ \
       Lwt.async (fun () ->@ \
       OS.Lifecycle.await_shutdown_request () >>= fun _ ->@ \
       %s.disconnect qrexec);@ \
       Lwt.return (`Ok qrexec)@]"
      modname modname

This defines a configurable object, which inherits from the base_configurable class defined in Mirage. The interesting bits for this configurable are the methods packages, configure, and connect. packages is where the dependency on mirage-qubes is declared. configure will terminate if qrexec_qubes has been pulled into the dependency graph but the user invoked another target (for example, mirage configure -t unix). connect gives the instructions for generating the code for qrexec in

You may notice that connect's strf call doesn't refer to Qrexec directly, but rather takes a modname parameter. Most of the modules referred to will be the result of some functor application, and the previous code generation will automatically name them; the only way to access this name is via the modname parameter.

We do something similar for gui:

let gui = job

let gui_qubes = impl @@ object
  inherit base_configurable
  method ty = gui
  val name = Name.ocamlify @@ "gui"
  method name = name
  method module_name = "Qubes.GUI"
  method packages = Key.pure [ package "mirage-qubes" ]
  method configure i =
    match get_target i with
    | `Qubes -> R.ok ()
    | _ -> R.error_msg "Qubes GUI invoked for non-Qubes target."
  method connect _ modname _args =
      "@[<v 2>\
       %s.connect ~domid:0 () >>= fun gui ->@ \
       Lwt.async (fun () -> %s.listen gui);@ \
       Lwt.return (`Ok gui)@]"
      modname modname

For details on what both gui_qubes and qrexec_qubes are actually doing in their connect blocks and why, talex5's post on building the QubesOS unikernel firewall.

QRExec for nothing, GUI for free

We'll need the connect function for both of these configurables to be run before the start function of our unikernel. But we also don't want a corresponding QRExec.t or GUI.t to be passed to our unikernel, nor do we want to parameterize it over the module type corresponding to either module, since either of these would be nonsensical for a non-Qubes target.

Instead, we need to have take care of this transparently, and we don't want any of the results passed to us. In order to accomplish this, we'll need to change the final invocation of Functoria's register function from Mirage.register:

let qrexec_init = match_impl Key.(value target) [
  `Qubes, qrexec_qubes;
] ~default:Functoria_app.noop

let gui_init = match_impl Key.(value target) [
  `Qubes, gui_qubes;
] ~default:Functoria_app.noop

let register
    ?(argv=default_argv) ?tracing ?(reporter=default_reporter ())
    ?keys ?packages
    name jobs =
  let argv = Some (Functoria_app.keys argv) in
  let reporter = if reporter == no_reporter then None else Some reporter in
  let qubes_init = Some [qrexec_init; gui_init] in
  let init = qubes_init ++ argv ++ reporter ++ tracing in
  register ?keys ?packages ?init name jobs

qrexec_init and gui_init will only take action if the target is qubes; otherwise, the dummy implementation Functoria_app.noop will be used. The qrexec_init and gui_init values are added to the init list passed to register regardless of whether they are the Qubes impls or Functoria_app.noop.

With those additions, mirage configure -t qubes will result in a bootable unikernel! ...but we're not done yet.

how do I networks

MirageOS previously had two methods of IP configuration: automatically at boot via DHCP, and statically at code, configure, or boot. Neither of these are appropriate IPv4 interfaces on Qubes VMs: QubesOS doesn't run a DHCP daemon. Instead, it expects VMs to consult the Qubes database for their IP information after booting. Since the IP information isn't known before boot, we can't even supply it at boot time.

Instead, we'll add a new impl for fetching information from QubesDB, and plumb the IP configuration into the generic_stackv4 function. generic_stackv4 already makes an educated guess about the best IPv4 configuration retrieval method based in part on the target, so this is a natural fit.

Since we want to use QubesDB as an input to the function that configures the IPv4 stack, we'll have to do a bit more work to make it fit nicely into the functor application architecture -- namely, we have to make a Type for it:

type qubesdb = QUBES_DB
let qubesdb = Type QUBES_DB

let qubesdb_conf = object
  inherit base_configurable
  method ty = qubesdb
  method name = "qubesdb"
  method module_name = "Qubes.DB"
  method packages = Key.pure [ package "mirage-qubes" ]
  method configure i =
    match get_target i with
    | `Qubes -> R.ok ()
    | _ -> R.error_msg "Qubes DB invoked for non-Qubes target."
  method connect _ modname _args = Fmt.strf "%s.connect ~domid:0 ()" modname

let default_qubesdb = impl qubesdb_conf

Other than the type qubesdb = QUBES_DB and let qubesdb = Type QUBES_DB, this isn't very different from the previous gui and qrexec examples. Next, we'll need something that can take a qubesdb, look up the configuration, and set up an ipv4 from the lower layers:

let ipv4_qubes_conf = impl @@ object
    inherit base_configurable
    method ty = qubesdb @-> ethernet @-> arpv4 @-> ipv4
    method name = Name.create "qubes_ipv4" ~prefix:"qubes_ipv4"
    method module_name = "Qubesdb_ipv4.Make"
    method packages = Key.pure [ package ~sublibs:["ipv4"] "mirage-qubes" ]
    method connect _ modname = function
      | [ db ; etif; arp ] -> Fmt.strf "%s.connect %s %s %s" modname db etif arp
      | _ -> failwith (connect_err "qubes ipv4" 3)

let ipv4_qubes db ethernet arp = ipv4_qubes_conf $ db $ ethernet $ arp

Notably, the connect function here is a bit more complicated -- we care about the arguments presented to the function (namely the initialized database, an ethernet module, and an arp module), and we'll pass them to the initialization function, which comes from mirage-qubes.ipv4.

To tell mirage configure that when -t qubes is specified, we should use ipv4_qubes_conf, we'll add a bit to generic_stackv4:

let generic_stackv4
    ?group ?config
    ?(dhcp_key = Key.value @@ Key.dhcp ?group ())
    ?(net_key = Key.value @@ ?group ())
    (tap : network impl) : stackv4 impl =
  let eq a b = Key.(pure ((=) a) $ b) in
  let choose qubes socket dhcp =
    if qubes then `Qubes
    else if socket then `Socket
    else if dhcp then `Dhcp
    else `Static
  let p = Functoria_key.((pure choose)
          $ eq `Qubes Key.(value target)
          $ eq `Socket net_key
          $ eq true dhcp_key) in
  match_impl p [
    `Dhcp, dhcp_ipv4_stack ?group tap;
    `Socket, socket_stackv4 ?group [Ipaddr.V4.any];
    `Qubes, qubes_ipv4_stack ?group tap;
  ] ~default:(static_ipv4_stack ?config ?group tap)

Now, mirage configure -t qubes with any unikernel that usees generic_stackv4 will automatically work!

So What?

This means I can configure this website for the Qubes target in my development VM:

4.04.0🐫  (qubes-target) mirageos:~/mirage-www/src$ mirage configure -t qubes

and get some nice invocations of the QRExec and GUI start code, along with the IPv4 configuration from QubesDB:

4.04.0🐫  (qubes-target) mirageos:~/mirage-www/src$ cat
(* Generated by mirage configure -t qubes (Tue, 28 Feb 2017 18:15:49 GMT). *)

open Lwt.Infix
let return = Lwt.return
let run =

let _ = Printexc.record_backtrace true

module Ethif1 = Ethif.Make(Netif)

module Arpv41 = Arpv4.Make(Ethif1)(Mclock)(OS.Time)

module Qubesdb_ipv41 = Qubesdb_ipv4.Make(Qubes.DB)(Ethif1)(Arpv41)

module Icmpv41 = Icmpv4.Make(Qubesdb_ipv41)

module Udp1 = Udp.Make(Qubesdb_ipv41)(Stdlibrandom)

module Tcp1 = Tcp.Flow.Make(Qubesdb_ipv41)(OS.Time)(Mclock)(Stdlibrandom)

module Tcpip_stack_direct1 = Tcpip_stack_direct.Make(OS.Time)(Stdlibrandom)

module Conduit_mirage1 = Conduit_mirage.With_tcp(Tcpip_stack_direct1)

module Dispatch1 = Dispatch.Make(Cohttp_mirage.Server_with_conduit)(Static1)

module Mirage_logs1 = Mirage_logs.Make(Pclock)

let net11 = lazy (
  Netif.connect (Key_gen.interface ())

let time1 = lazy (
  return ()

let mclock1 = lazy (
  Mclock.connect ()

let ethif1 = lazy (
  let __net11 = Lazy.force net11 in
  __net11 >>= fun _net11 ->
  Ethif1.connect _net11

let qubesdb1 = lazy (
  Qubes.DB.connect ~domid:0 ()

let arpv41 = lazy (
  let __ethif1 = Lazy.force ethif1 in
  let __mclock1 = Lazy.force mclock1 in
  let __time1 = Lazy.force time1 in
  __ethif1 >>= fun _ethif1 ->
  __mclock1 >>= fun _mclock1 ->
  __time1 >>= fun _time1 ->
  Arpv41.connect _ethif1 _mclock1

let qubes_ipv411 = lazy (
  let __qubesdb1 = Lazy.force qubesdb1 in
  let __ethif1 = Lazy.force ethif1 in
  let __arpv41 = Lazy.force arpv41 in
  __qubesdb1 >>= fun _qubesdb1 ->
  __ethif1 >>= fun _ethif1 ->
  __arpv41 >>= fun _arpv41 ->
  Qubesdb_ipv41.connect _qubesdb1 _ethif1 _arpv41

let random1 = lazy (
  Lwt.return (Stdlibrandom.initialize ())

let icmpv41 = lazy (
  let __qubes_ipv411 = Lazy.force qubes_ipv411 in
  __qubes_ipv411 >>= fun _qubes_ipv411 ->
  Icmpv41.connect _qubes_ipv411

let udp1 = lazy (
  let __qubes_ipv411 = Lazy.force qubes_ipv411 in
  let __random1 = Lazy.force random1 in
  __qubes_ipv411 >>= fun _qubes_ipv411 ->
  __random1 >>= fun _random1 ->
  Udp1.connect _qubes_ipv411

let tcp1 = lazy (
  let __qubes_ipv411 = Lazy.force qubes_ipv411 in
  let __time1 = Lazy.force time1 in
  let __mclock1 = Lazy.force mclock1 in
  let __random1 = Lazy.force random1 in
  __qubes_ipv411 >>= fun _qubes_ipv411 ->
  __time1 >>= fun _time1 ->
  __mclock1 >>= fun _mclock1 ->
  __random1 >>= fun _random1 ->
  Tcp1.connect _qubes_ipv411 _mclock1

let stackv4_1 = lazy (
  let __time1 = Lazy.force time1 in
  let __random1 = Lazy.force random1 in
  let __net11 = Lazy.force net11 in
  let __ethif1 = Lazy.force ethif1 in
  let __arpv41 = Lazy.force arpv41 in
  let __qubes_ipv411 = Lazy.force qubes_ipv411 in
  let __icmpv41 = Lazy.force icmpv41 in
  let __udp1 = Lazy.force udp1 in
  let __tcp1 = Lazy.force tcp1 in
  __time1 >>= fun _time1 ->
  __random1 >>= fun _random1 ->
  __net11 >>= fun _net11 ->
  __ethif1 >>= fun _ethif1 ->
  __arpv41 >>= fun _arpv41 ->
  __qubes_ipv411 >>= fun _qubes_ipv411 ->
  __icmpv41 >>= fun _icmpv41 ->
  __udp1 >>= fun _udp1 ->
  __tcp1 >>= fun _tcp1 ->
  let config = {Mirage_stack_lwt. name = "stackv4_"; interface = _net11;} in
Tcpip_stack_direct1.connect config
_ethif1 _arpv41 _qubes_ipv411 _icmpv41 _udp1 _tcp1

let nocrypto1 = lazy (
  Nocrypto_entropy_mirage.initialize ()

let tcp_conduit_connector1 = lazy (
  let __stackv4_1 = Lazy.force stackv4_1 in
  __stackv4_1 >>= fun _stackv4_1 ->
  Lwt.return (Conduit_mirage1.connect _stackv4_1)


let conduit11 = lazy (
  let __nocrypto1 = Lazy.force nocrypto1 in
  let __tcp_conduit_connector1 = Lazy.force tcp_conduit_connector1 in
  __nocrypto1 >>= fun _nocrypto1 ->
  __tcp_conduit_connector1 >>= fun _tcp_conduit_connector1 ->
  Lwt.return Conduit_mirage.empty >>= _tcp_conduit_connector1 >>=
fun t -> Lwt.return t

let argv_qubes1 = lazy (
  let filter (key, _) = List.mem key ( snd Key_gen.runtime_keys) in
Bootvar.argv ~filter ()

let http1 = lazy (
  let __conduit11 = Lazy.force conduit11 in
  __conduit11 >>= fun _conduit11 ->
  Cohttp_mirage.Server_with_conduit.connect _conduit11

let static11 = lazy (
  Static1.connect ()

let static21 = lazy (
  Static2.connect ()

let pclock1 = lazy (
  Pclock.connect ()

let key1 = lazy (
  let __argv_qubes1 = Lazy.force argv_qubes1 in
  __argv_qubes1 >>= fun _argv_qubes1 ->
  return (Functoria_runtime.with_argv ( fst Key_gen.runtime_keys) "www" _argv_qubes1)

let gui1 = lazy (
  Qubes.GUI.connect ~domid:0 () >>= fun gui ->
  Lwt.async (fun () -> Qubes.GUI.listen gui);
  Lwt.return (`Ok gui)

let qrexec_1 = lazy (
  Qubes.RExec.connect ~domid:0 () >>= fun qrexec ->
  Lwt.async (fun () ->
  OS.Lifecycle.await_shutdown_request () >>= fun _ ->
  Qubes.RExec.disconnect qrexec);
  Lwt.return (`Ok qrexec)

let f11 = lazy (
  let __http1 = Lazy.force http1 in
  let __static11 = Lazy.force static11 in
  let __static21 = Lazy.force static21 in
  let __pclock1 = Lazy.force pclock1 in
  __http1 >>= fun _http1 ->
  __static11 >>= fun _static11 ->
  __static21 >>= fun _static21 ->
  __pclock1 >>= fun _pclock1 ->
  Dispatch1.start _http1 _static11 _static21 _pclock1

let mirage_logs1 = lazy (
  let __pclock1 = Lazy.force pclock1 in
  __pclock1 >>= fun _pclock1 ->
  let ring_size = None in
  let reporter = Mirage_logs1.create ?ring_size _pclock1 in
  Mirage_runtime.set_level ~default:Logs.Info (Key_gen.logs ());
  Mirage_logs1.set_reporter reporter;
  Lwt.return reporter

let mirage1 = lazy (
  let __qrexec_1 = Lazy.force qrexec_1 in
  let __gui1 = Lazy.force gui1 in
  let __key1 = Lazy.force key1 in
  let __mirage_logs1 = Lazy.force mirage_logs1 in
  let __f11 = Lazy.force f11 in
  __qrexec_1 >>= fun _qrexec_1 ->
  __gui1 >>= fun _gui1 ->
  __key1 >>= fun _key1 ->
  __mirage_logs1 >>= fun _mirage_logs1 ->
  __f11 >>= fun _f11 ->

let () =
  let t =
  Lazy.force qrexec_1 >>= fun _ ->
    Lazy.force gui1 >>= fun _ ->
    Lazy.force key1 >>= fun _ ->
    Lazy.force mirage_logs1 >>= fun _ ->
    Lazy.force mirage1
  in run t

and we can build this unikernel, then send it to dom0 to be booted:

4.04.0🐫  (qubes-target) mirageos:~/mirage-www/src$ make depend
4.04.0🐫  (qubes-target) mirageos:~/mirage-www/src$ make
4.04.0🐫  (qubes-target) mirageos:~/mirage-www/src$ ~/test-mirage www.xen mirage-test

and if we check the guest VM logs for the test VM (which on my machine is named mirage-test, as above), we'll see that it's up and running:

.[32;1mMirageOS booting....[0m
Initialising timer interface
Initialising console ... done.
Note: cannot write Xen 'control' directory
Attempt to open(/dev/urandom)!
Unsupported function getpid called in Mini-OS kernel
Unsupported function getppid called in Mini-OS kernel
2017-02-28 18:29:54 -00:00: INF [net-xen:frontend] connect 0
2017-02-28 18:29:54 -00:00: INF [qubes.db] connecting to server...
gnttab_stubs.c: initialised mini-os gntmap
2017-02-28 18:29:54 -00:00: INF [qubes.db] connected
2017-02-28 18:29:54 -00:00: INF [net-xen:frontend] create: id=0 domid=2
2017-02-28 18:29:54 -00:00: INF [net-xen:frontend]  sg:true gso_tcpv4:true rx_copy:true rx_flip:false smart_poll:false
2017-02-28 18:29:54 -00:00: INF [net-xen:frontend] MAC: 00:16:3e:5e:6c:0e
2017-02-28 18:29:54 -00:00: INF [ethif] Connected Ethernet interface 00:16:3e:5e:6c:0e
2017-02-28 18:29:54 -00:00: INF [arpv4] Connected arpv4 device on 00:16:3e:5e:6c:0e
2017-02-28 18:29:54 -00:00: INF [udp] UDP interface connected on
2017-02-28 18:29:54 -00:00: INF [tcpip-stack-direct] stack assembled: mac=00:16:3e:5e:6c:0e,ip=
2017-02-28 18:29:56 -00:00: INF [dispatch] Listening on http://localhost/

And if we do a bit of firewall tweaking in sys-firewall to grant access from other VMs:

[user@sys-firewall ~]$ sudo iptables -I FORWARD -d -i vif+ -j ACCEPT

we can verify that things are as we expect from any VM that has the appropriate software -- for example:

4.04.0🐫  (qubes-target) mirageos:~/mirage-www/src$ wget -q -O - ht.137.3.16|head -1
<!DOCTYPE html>

What's Next?

The implementation work above leaves a lot to be desired, noted in the comments to the original pull request. We welcome further contributions in this area, particularly from QubesOS users and developers! If you have questions or comments, please get in touch on the mirageos-devel mailing list or on our IRC channel at #mirage on !


Size matters: how Mirage got smaller and less magical

By Hannes Mehnert

Size matters: how Mirage got smaller and less magical

In this article, some technical background and empirical evidence is given how we reduced the lines of code in Mirage3, which has about 25% fewer lines of code than Mirage2, while providing more features.

Mirage does a fair amount of code generation since its initial release to extend target-agnostic unikernels to target-specific virtual machine images (or Unix binaries). Until Mirage 2.7, string concatenation was used heavily. Since the Mirage 2.7.0 release (February 2016), it is based on functoria, "a DSL to describe a set of modules and functors, their types and how to apply them in order to produce a complete application". The code generated by Mirage3 is less complex than the Mirage2 one and contains up to 45% fewer lines of code.

Generating code considered harmful

Code generated by a program with intricate control flow and automatically generated identifier names is difficult to understand by a human - in case the generated code is incorrect and needs to be debugged (or the compiler chokes on it with an error message pointing in the middle of intricate generated code). It is also a burden on the developer, since generated code should not be part of the version control system, thus the build system needs to include another step. If the code generator is buggy, or not easily extendible for new features, developers may want to manually modify the generated code - which then turns into a release nightmare, since you need to maintain a set of patches on top of generated code, while the code generator may is developed alongside. Generating code is best avoided - maybe there is a feature in the programming language to solve the boilerplate without code generators.

Having said this, there's nothing wrong with LISP macros or MetaOCaml.

Mirage uses code generation to complete backend-agnostic unikernels with the required boilerplate to compile for a specific backend - by selecting the network device driver, the console, the network stack, and other devices - taking user-supplied configuration arguments into account. In Mirage, the OCaml TCP/IP stack requires any network device which implements the Mirage_net.S module type.

At the end of the day, some mechanism needs to be in place which links the mirage-net-solo5 library if compiling for Solo5 (or mirage-net-xen if compiling for xen, or mirage-net-unix for Unix, or mirage-net-macosx for MacOSX). This can be left to each unikernel developer, which would require having the same boilerplate code all over, which needs to be updated if a new backend becomes available (Mirage2 knew about Xen, Unix, and MacOSX, Mirage3 extends this with Solo5 and Qubes). Instead, the mirage tool generates this boilerplate by knowing about all supported devices, and which library a unikernel has to link for a device depending on the target and command line arguments. That's not exactly the ideal solution. But it works good enough for us right now (more or less). A single place - the mirage tool - needs to be extended whenever a new backend becomes available.

Device initialisation - connect

Devices may depend on each other, e.g. a TCP stack requires a monotonic clock and a random number generator, which influences the initialisation order. Mirage generates the device initialisation startup code based on the configuration and data dependencies (which hopefully form an acyclic graph). Mirage2 allowed to handle initialisation errors (the type of connect used to be unit -> [ `Ok of t | `Error of error ] io), but calls to connect were automatically generated, and the error handler always spit out an error message and exited. Becaus the error was generic, Mirage2 didn't know how to properly print it, and instead failed with some incomprehensible error message. Pretty printing errors is solved in Mirage3 by our re-work of errors, which now use the result type, are extendible, and can be pretty printed. Calls to connect are automatically generated, and handling errors gracefully is out of scope for a unikernel -- where should it get the other 2 network devices promised at configuration time from, if they're not present on the (virtual) PCI bus?

The solution we discussed and implemented (also in functoria) was to always fail hard (i.e. crash) in connect : unit -> t. This lead to a series of patches for all implementors of connect, where lots of patches removed control flow complexity (and less complex test cases, see e.g. mirage-net-unix, or tcpip). Lots of common boilerplate (like or_error, which throws an exception if connect errored) could be removed.

Comparing the generated between Mirage 2.9.1 and 3.0.0 for various unikernels on both unix and xen code reductions up to 45% (diffs are here)

  • console (device-usage) xen: +35 -41 (now 81) unix: +32 -39 (now 80)
  • block (device-usage) xen: +36 -45 (now 87) unix: +34 -44 (now 86)
  • kv_ro (device-usage) xen: +34 -59 (now 75) unix: +39 -51 (now 86)
  • network (device-usage) xen: +82 -134 (now 178) unix: +79 -133 (now 177)
  • conduit_server (device-usage) xen: +86 -152 (now 200) unix: +84 -213 (now 199)
  • dhcp (applications) xen: +44 -51 (now 93) unix: +41 -49 (now 92)
  • dns (applications) xen: +86 -143 (now 190) unix: +83 -141 (now 189)
  • static_website_tls (applications) xen: +97 -176 (now 230) unix: +108 -168 (now 237)
  • xen: +122 -171 (now 223) unix: +65 -85 (now 133)
  • btc-pinata xen: +119 -155 (now 217) unix: +64 -73 (now 127)
  • canopy xen: +106 -180 (now 245) unix: +61 -106 (now 159)

Workflow, phase separation, versioned opam dependencies

The workflow to build a unikernel used to be mirage configure followed by make. During the configure phase, a Makefile was generated with the right build and link commands (depending on configuration target and other parameters). Mirage2 installed opam packages and system packages as a side effect during configuration. This lead to several headaches: you needed to have the target-specific libraries installed while you were configuring (you couldn't even test the configuration for xen if you didn't have xen headers and support libraries installed). Reconfiguration spawned yet another opam process (which even if it does not install anything since everything required is already installed, takes some time since the solver has to evaluate the universe) - unless the --no-opam option was passed to mirage configure.

A second issue with the Mirage2 approach was that dependent packages were listed in the unikernel, and passed as string to opam. When version constraints were included, this lead either shell (calling out opam) or make (embedding the packages in the Makefile) or both to choke. Being able to express version constraints for dependencies in was one of the most wanted features for Mirage3. It is crucial for further development (to continue allowing API breakage and removing legacy): a unikernel author, and the mirage tool, can now embed versioned dependencies onto device interfaces. Instead of a garbled error message from mirage trying to compile a unikernel where the libraries don't fit the generated code, opam will inform which updates are necessary.

In a first rampage (functoria) instead of manual executions of opam processes, an opam package file was generated by mirage at configuration time for the given target. This allowed to express version constraints in each file (via the package function). This change also separated the configuration phase, the dependency installation phase, and the build phase - which included delayed invocations of pkg-config to pass parameters to ld. A mess, especially if your goal is to generate Makefiles which run both on GNU make and BSD make.

A second approach (functoria) digged a bit deeper down the rabbit hole, and removed complex selection and adjustment of strings to output the Makefile, by implementing this logic in OCaml (and calling out to ocamlbuild and ld). Removing an uneeded layer of code generation is easier to read and understand, less code, and includes stronger guarantees. More potential errors are caught during compile time, instead of generating (possible ill-formed) Makefiles. Bos is a concise library interacting with basic operating system services, and solves once and for all common issues in that area, such as properly escaping of arguments.

Mirage3 contains, instead of a single configure_makefile function which generated the entire makefile, the build and link logic is separated into functions, and only a simplistic makefile is generated which invokes mirage build to build the unikernel, and expects all dependent libraries to be installed (e.g. using make depend, which invokes opam) -- no need for delaying pkg-config calls anymore.

This solution has certainly less complex string concatenation, and mirage has now a clearer phase distinction - configure, depend, compile & link. (This workflow (still) lacks a provisioning step (e.g. private key material, if provided as static binary blob, needs to be present during compilation atm), but can easily be added later.) There are drawbacks: the mirage utility is now needed during compilation and linking, and needs to preserve command line arguments between configuration and build phase. Maybe the build step should be in the opam file, then we would need to ensure unique opam package names and we would need to communicate to the user where the binary got built and installed.

Other functionality removed or replaced

The first commit to mirage is from 2004, back then opam was an infant. Mirage2 ensured that a not-too-ancient version of OCaml is installed (functoria contained a similar piece of code). Mirage3 relies on opam to require a certain OCaml version (at the moment 4.03).

Mirage and functoria were developed while support libraries were not yet available - worth mentioning bos (mentioned above), fpath, logs, and astring. Parts of those libraries were embedded in functoria, and are now replaced by the libraries. (See mirage#703 and functoria#84 in case you want to know the details.)

Functoria support for OCaml <4.02 has been dropped, also astring is now in use. Mirage support for OCaml <4.01 has been dropped from Mirage.

Some C bits and pieces, namely str, bignum, and libgcc.a, are no longer linked and part of every unikernel. This is documented in mirage#544 and mirage#663.


The overall statistics of Mirage3 look promising: more libraries, more contributors, less code, uniform error treatment, unified logging support. Individual unikernels contain slightly less boilerplate code (as shown by these unified diffs).

The binary sizes of the above mentioned examples (mirage-skeleton, nqsb, Canopy, pinata) between Mirage2 and Mirage3 results on both Unix and Xen only in small differences (in the range of kilobytes). We are working on a performance harness to evaluate the performance of flambda intermediate language in OCaml and dead code elimination. These should decrease the binary size and improve the performance.


Announcing MirageOS 3.0.0

By Mindy Preston

We're excited to announce MirageOS 3.0! MirageOS is a modern, modular library operating system that allows the creation of small, secure, legacy-free services. MirageOS applications can be compiled to run as self-contained virtual machines (a few MB in size) on Xen or KVM hosts, FreeBSD's bhyve, or even as regular Unix processes (allowing access to regular debugging tools). The system libraries themselves can be reused in traditional applications, just like any other software library.

Full release notes are available on GitHub. If you're interested in getting started with MirageOS 3 right away, you might be interested in the revamped guide to getting started, a small collection of example unikernels, or the porting guide for updating Mirage 2.x unikernels to Mirage 3.

Here's a summary of the things in MirageOS 3 that we're most excited about:


MirageOS 3.0 is the first release that integrates the solo5 targets, virtio and ukvm, fully with the mirage front-end tool. Now you can mirage configure -t ukvm, build a unikernel, and run directly with the generated ukvm-bin! We've updated the "hello world" tutorial to reflect our excitement about ukvm -- the ukvm target is considerably easier to interface with and configure than xen was, and for a lot of users this will be a clearer path toward operational deployment of unikernels.

For a lot more information on the Solo5 targets, see the earlier blog post announcing solo5, Unikernel Monitors: Extending Minimalism Outside of the Box, and the very readable solo5 repository README. You can also read how to run solo5 unikernels on FreeBSD via bhyve.

Playing More Nicely with OPAM

MirageOS 3 has a much richer interface for dealing with the package manager and external library dependencies. A user can now specify a version or range of versions for a package dependency, and the mirage front-end tool will construct a custom opam file including both those package dependencies and the ones automatically generated from mirage configure. mirage will also consider version constraints for its own packages -- from now on, opam should notice that releases of mirage are incompatible with your unikernel.

For more information on dealing with packages and dependencies, the documentation for the Functoria.package function will likely be of use. The PRNG device-usage example in mirage-skeleton demonstrates some useful invocations of package.

Amazing Docs

Thanks to a lot of hard work, a fully interlinked set of module documentation is now automatically generated by odig and available for your reading pleasure at the MirageOS central documentation repository. While documentation was previously available for most modules, it was scattershot and often required having several disconnected pages open simultaneously. We hope you'll find the new organization more convenient. The documentation generation system is still in beta, so please report issues upstream if you run across rendering issues or have other feedback.

Result-y Errors

The module types provided by MirageOS 3 replace the previous error paradigm (a combination of exceptions and directly returning polymorphic variants) with one that uses the Result module included in OCaml 4.03 and up. A notable exception is when problems occur during the unikernel's initialization (i.e., in connect functions), where unikernels will now fail hard as soon as they can. The goal of these changes is to surface errors when the application cares about them, and to not present any uninitialized or unstable state to an application at start time.

The MirageOS 3 module types define a core set of likely errors for each module type (see the mirage-flow module type for an example), which can be extended by any given implementation. Module types now specify that each implementation must include a pretty-printer that can handle all emitted error types. Functions that return a success type when they run as expected return a (success, error) Result.t, which the caller can print with pp_error if the value is an Error.

For more background on the result type, see the Rresult library which defines further useful operations on Result.t and is used widely in MirageOS libraries. A more in-depth explanation of errors in Mirage 3 is also available.

Logs Where You Want Them

MirageOS version 2.9.0 included automatic support for logging via the Logs and Mirage_logs library, but by default logs were always printed to the console and changing the log reporter was cumbersome. In MirageOS 3, you can send logs to a consumer of syslog messages with syslog_udp, syslog_tcp, or with the full authentication and encryption provided by ocaml-tls using syslog_tls. For more information, see the excellent writeup at

Disaggregated Module Type Definitions

Breaking all of the MirageOS 3.0 APIs showed us that keeping them all in the same place made updates really difficult. There's now an additional set of packages which contain the definitions for each set of module types (e.g. mirage-fs for the FS module type, mirage-block for the BLOCK module type, etc). A few module types had some additional useful code that was nicely functorized over the module type in question, so we've bundled that code in the module type packages as well. Documentation for all of the module type packages is available at the Mirage documentation hub.

We hope that this change combined with the opam workflow changes above will result in much less painful API changes in the future, as it will be possible for unikernel authors to target specific versions more readily.

Clockier Clocks, Timelier Time

In older MirageOS versions, we noticed that we were often having to deduce a span of time from having taken two wall-clock samples of the current time. In MirageOS 3, you have your choice of two types of clock - MCLOCK, which provides a monotonically increasing clock reflecting the time elapsed since the clock started, and PCLOCK, which provides a traditional POSIX wall-clock time. Most previous users of CLOCK were able to migrate to the more-honest, less-complicated MCLOCK. For an example of both clocks, see the speaking clock. You may also be interested in an example of converting existing code from CLOCK to MCLOCK.

MCLOCK provides a nice interface for dealing with time at a nanosecond granularity. The TIME module type has been updated to expect an int64 number of nanoseconds, rather than a float, as an argument to its function sleep. For those of us who don't think in nanoseconds, the Duration library provides convenient functions for translating from and to more familiar units like seconds.

Build System Shift

Mirage 3.0 has many, many more packages than before, and so we turned to OCaml Labs to help us to scale up our package management. In many but not all MirageOS packages, we've replaced oasis with topkg, the "transitory OCaml software packager". topkg is a lighter layer over the underlying ocamlbuild. Using topkg has allowed us to remove several thousand lines of autogenerated code across the MirageOS package universe, and let our release manager automate a significant amount of the MirageOS 3 release process. We hope to continue benefitting from the ease of using topkg and topkg-care.

Not all packages are using topkg yet -- if you see one that isn't, feel free to submit a pull request!

Less Code, Better Behavior

There's more in MirageOS 3 than we can fit in one blog post without our eyes glazing over. The release notes for mirage version 3.0.0 are a nice summary, but you might also be interested in the full accounting of changes for every package released as a part of the MirageOS 3 effort; links for each library are available at the end of this post.

Across the package universe, a net several thousand lines of code were removed as part of MirageOS 3. Many were autogenerated build-time support files removed in the transition from oasis to topkg. Others were small support modules like Result, which had previously been replicated in many places and were replaced by a reference to a common implementation. Some large implementations (like the DHCP client code in mirage-tcpip) were replaced by smaller, better implementations in common libraries (like charrua-core).

For example, ocaml-fat had 1,280 additions and 10,265 deletions for a net of -8,985 lines of code; version 0.12.0 jettisoned a custom in-memory block device in favor of using the in-memory block device provided by Mirage_block_lwt.Mem, removed several thousand lines of autogenerated OASIS code, removed several custom error-case polymorphic variants, and lost a custom result module. The mirage repository itself netted -8,490 lines of code while adding all of the features above!

A number of improvements were made to mirage to limit the number of unnecessary build artifacts and reduce the amount of unnecessary code linked into unikernels. Modules you're unlikely to use like Str are no longer included in the OCaml runtime. MirageOS 3 is also the first to drop support for OCaml 4.02.3, meaning that all supported compilers support the flambda compiler extension and a number of related optimization opportunities.

Very many people were involved in making the MirageOS package universe smaller and better than it was before. We'd like to thank, in a particular alphabetical order, the following people who contributed code, suggestions, bug reports, comments, mailing lists questions and answers, and other miscellaneous help:

  • Aaron Cornelius
  • Amir Chaudhry
  • Andrew Stuart
  • Anil Madhavapeddy
  • Ashish Agarwal
  • Balraj Singh
  • Cedric Cellier
  • Christiano Haesbaert
  • Daniel Bünzli
  • Dan Williams
  • Dave Scott
  • David Kaloper
  • David Sheets
  • Enguerrand Decorne
  • Eugene Bagdasaryan
  • Federico Gimenez
  • Gabriel de Perthuis
  • Gabriel Jaldon
  • Gabriel Radanne
  • Gemma Gordon
  • Hannes Mehnert
  • Ian Campbell
  • Jochen Bartl
  • John P. McDermott
  • Jon Ludlam
  • Kia
  • Leo White
  • Leonid Rozenberg
  • Liang Wang
  • Madhuri Yechuri
  • Magnus Skjegstad
  • Martin Lucina
  • Matt Gray
  • Mindy Preston
  • Nick Betteridge
  • Nicolas Ojeda Bar
  • Nik Sultana
  • Pablo Polvorin
  • Petter A. Urkedal
  • Qi LI
  • Ramana Venkata
  • Ricardo Koller
  • Richard Mortier
  • Rudi Grinberg
  • Sean Grove
  • Takayuki Imada
  • Thomas Gazagnaire
  • Thomas Leonard
  • Vincent Bernardoff
  • Vittorio Cozzolino
  • GitHub user waldyrious
  • Wassim Haddad
  • Jeremy Yallop

Please let us know if you notice someone (including yourself) is missing so we can add them and apologize! We're happy to remove or change your listed name if you'd prefer as well. Names were taken from metadata on commit messages and e-mail headers.


Irmin 0.12 with portable filesystem watching released

By Thomas Gazagnaire

Development of the Irmin Git-like data store continues (see here for an introduction). We are releasing Irmin 0.12.0 which brings support for native file-system watchers to greatly improve the performance of watches on the datastore.

Previously, an Irmin application that wanted to use watches would setup file-system scanning/polling by doing:

  let () = Irmin_unix.install_dir_polling_listener 1.0

which would scan the .git/refs directory every second. This worked in practice but was unpredictably latent (if unlucky you might wait for a full second for the watch callbacks to trigger), and disk/CPU intensive as we were scanning the full storage directory every second to detect file changes. In the cases where the store had 1000s of tags, this could easily saturate the CPU. And in case you were wondering, there are increasing number of applications (such as DataKit) that do create thousands of tags regularly, and Canopy that need low latency for interactive development.

In the new 0.12.0 release, you need to use:

   let () = Irmin_unix.set_listen_dir_hook ()

and the Irmin storage will do "the right thing". If you are on Linux, and have the inotify OPAM package installed, it will use libinotify to get notified by the kernel on every change and re-scan the whole directory. On OSX, if you have the osx-fsevents OPAM package installed, it will do the same thing using the OSX FSEvents.framework. The portable compatibility layer between inotify and fsevents comes via the new irmin-watcher package that has been released recently as well. This may also come in useful for other tools that require portable OCaml access to filesystem hierarchies.

If you are using Irmin, please do let us know how you are getting on via the mailing list and report any bugs on the issue tracker.


MirageOS Summer 2016 hack retreat roundup

By Gemma Gordon, Anil Madhavapeddy

Our first Cambridge-based MirageOS hack retreat took place yesterday - and what a fantastic day it was! The torrential rain may have halted our punting plans, but it didn't stop progress in the Old Library! Darwin College was a fantastic venue, complete with private islands linked by picturesque wooden bridges and an unwavering wifi connection.

People naturally formed groups to work on similar projects, and we had a handful of brand new users keen to get started with OCaml and Mirage. The major tasks that emerged were:

  • new hypervisor target: the integration of the Solo5 KVM-based hypervisor backend, bringing the number of officially supported targets up to 3 (Xen, Unix and KVM)
  • build system template: establishing a new topkg template for MirageOS libraries, to prepare us for building a unified API documentation bundle that works across all the entire project.
  • CPU portability: improving ARM support via a better base OS image.
  • libraries breadth: hacking on all the things to fill in the blanks, such as btree support for bare-metal Irmin, or a peer-to-peer layer for the DataKit.

We'll write about all of this in more detail, but for now here are the hack retreat notes hot off the press...

Solo5/MirageOS integration (KVM-based backend)

Progress on the Solo5 project has been steaming ahead since January, and this was the perfect opportunity to get everyone together to plan its integration with MirageOS. Dan Williams from IBM Research flew over to join us for the week, and Martin Lucina headed to Cambridge to prepare for the upstreaming of the recent Solo5 work. This included deciding on naming and ownership of the repositories, detailing the relationships between repositories and getting ready to publish the mirage-solo5 packages to OPAM. Mindy Preston, our MirageOS 3.0 release manager, and Anil Madhavapeddy and Thomas Gazagnaire (OPAM minions) were on hand to help plan this smoothly.

See their updates from the day on Canopy and related blog posts:

Onboarding new MirageOS/OCaml users

Our tutorials and onboarding guides really needed a facelift and an update, so Gemma Gordon spent the morning with some of our new users to observe their installation process and tried to pinpoint blockers and areas of misunderstanding. Providing the simple, concise instructions needed in a guide together with alternatives for every possible system and version requirement is a tricky combination to get right, but we made some changes to the installation guide that we hope will help. The next task is to do the same for our other popular tutorials, reconfigure the layout for easy reading and centralise the information as much as possible between the OPAM, MirageOS and OCaml guides. Thank you to Marwan Aljubeh for his insight into this process.

Other industrial users are also steaming ahead with their own MirageOS deployments. Amir Chaudhry spent the hackathon blogging about NFV Platforms with MirageOS unikernels, which details how Ericsson Silicon Valley has been using MirageOS to build lightweight routing kernels.


Thomas Gazagnaire was frenetically converting functoria, mirage, mirage-types and mirage-console to use topkg, and the feedback prompted fixes and a new release from Daniel Buenzli.

ARM and Cubieboards

Ian Campbell implemented a (slightly hacky) way to get Alpine Linux onto some Cubieboard2 boxes and provided notes on his process, including how to tailor the base for KVM and Xen respectively.

Meanwhile, Qi Li worked on testing and adapting simple-nat and mirage-nat to provide connectivity control for unikernels on ARM Cubieboards to act as network gateways.

Hannes Mehnert recently published a purely functional ARP package and continued refining it (with code coverage via bisect-ppx) during the hackathon.

MirageOS 3.0 API changes

Our MirageOS release manager, Mindy Preston, was on hand to talk with everyone about their PRs in preparation for the 3.0 release along with some patches for deprecating out of date code. There has been a lot of discussion on the development list. One focus was to address time handling properly in the interfaces: Matthew Gray came up from London to finish up his extensive revision of the CLOCK interface, and Hannes developed a new duration library to handle time unit conversions safely and get rid of the need for floating point handling. We are aiming to minimise the dependency on floating point handling in external interfaces to simplify compilation to very embedded hardware that only has soft floats (particularly for something as ubiquitous as time handling).

Error logging

Thomas Leonard continued with the work he started in Marrakech by updating the error reporting patches (also here) to work with the latest version of MirageOS (which has a different logging system based on Daniel Buenzlis Logs). See the original post for more details.

Ctypes 0.7.0 release

Jeremy released the foreign function interface library Ctypes 0.7.0 which, along with bug fixes, adds the following features:

  • Support for bytecode-only architectures (#410)
  • A new sint type corresponding to a full-range C integer and updated errno support for its use (#411)

See the full changelog online.

P2P key-value store over DataKit

KC Sivaramakrishnan and Philip Dexter took on the challenge of grabbing the Docker DataKit release and started building a distributed key-value store that features flexible JSON synching and merging. Their raw notes are in a Gist -- get in touch with them if you want to help hack on the sync system backed by Git.

Developer experience improvements

The OCaml Labs undergraduate interns are spending their summers working on user improvements and CI logs with MirageOS, and used the time at the hackathon to focus on these issues.

Ciaran Lawlor is working on an editor implementation, specifically getting the IOcaml kernel working with the Hydrogen plugin for the Atom editor. This will allow developers to run OCaml code directly in Atom, and eventually interactively build unikernels!

Joel Jakubovic used Angstrom (a fast parser combinator library developed by Spiros Eliopoulos) to ANSI escape codes, usually displayed as colours and styles into HTML for use in viewing CI logs.

Windows Support

Most of the Mirage libraries already work on Windows thanks to lots of work in the wider OCaml community, but other features don't have full support yet.

Dave Scott from Docker worked on ocaml-wpcap: a ctypes binding to the Windows winpcap.dll which lets OCaml programs send and receive ethernet frames on Windows. The ocaml-wpcap library will hopefully let us run the Mirage TCP/IP stack and all the networking applications too.

David Allsopp continued his OPAM-Windows support by fine-tuning the 80 native Windows OCaml versions - these will hopefully form part of OPAM 2.0. As it turns out, he's not the only person still interested in being able to run OCaml 3.07...if you are, get in touch!

General Libraries and utilities

Olivier Nicole is working on an implementation of macros in OCaml and started working on the HTML and XML templates using this system. The objective is to have the same behaviour as the Pa_tyxml syntax extension, but in a type-safe and more maintainable way without requiring PPX extensions. This project could be contributed to the development of Ocsigen once implemented.

Nick Betteridge teamed up with Dave Scott to look at using ocaml-btree as a backend for Irmin/xen and spent the day looking at different approaches.

Anil Madhavapeddy built a Docker wrapper for the CI system and spun up a big cluster to run OPAM bulk builds. Several small utilities like jsontee and an immutable log collection server and bulk build scripts will be released in the next few weeks once the builds are running stably, and be re-usable by other OPAM-based projects to use for their own tests.

Christophe Troestler is spending a month at OCaml Labs in Cambridge this summer, and spent the hack day working on implementing a library to allow seamless application switching from HTTP to FastCGI. Christophe has initiated work on a client and server for this protocol using CoHTTP so that it is unikernel-friendly.


MirageOS Summer 2016 hack retreat announcement, and talk roundup

By Gemma Gordon, Anil Madhavapeddy

As summer starts to shine over an obstinately rainy England, we are organising the second MirageOS hack retreat in Cambridge! It will be held on Weds 13th July at the lovely Darwin College from 9am-11pm, with snacks, teas, coffees and a servery lunch provided (thanks to sponsorship from Docker and OCaml Labs).

Anyone is welcome at all skill levels, but we'd appreciate you filling out the Doodle so that we can plan refreshments. We will be working on a variety of projects from improving ARM support, to continuous integration tests, the new Solo5 backend and improving the suite of protocol libraries. If you have something in particular that interests you, please drop a note to the mailing list or check out the full list of Pioneer Projects.

Some other events of note recently:

  • After several years of scribing awesome notes about our development, Amir has handed over the reigns to Enguerrand. Enguerrand joined OCaml Labs as an intern, and has built an IRC-to-Git logging bot which records our meetings over IRC and commits them directly to a repository which is available online. Thanks Amir and Enguerrand for all their hard work on recording the growing amount of development in MirageOS. Gemma Gordon has also joined the project and been coordinating the meetings. The next one is in a few hours, so please join us on #mirage on Libera Chat IRC at 4pm British time if you would like to participate or are just curious!

  • Our participation in the Outreachy program for 2016 has begun, and the irrepressible Gina Marie Maini (aka wiredsister) has been hacking on syslogd, mentored by Mindy Preston. She has already started blogging (about syslog and OCaml love), as well as podcasting with the stars. Welcome to the crew, Gina!

  • The new Docker for Mac and Docker for Windows products have entered open beta! They use a number of libraries from MirageOS (including most of the network stack) and provide a fast way of getting started with containers and unikernel builds on Mac and Windows. You can find talks about it at the recent JS London meetup and my slides I also spoke at OSCON 2016 about it, but those videos aren't online yet.

There have also been a number of talks in the past couple of months about MirageOS and its libraries:

  • Dan Williams from IBM Research delivered a paper at USENIX HotCloud 2016 about Unikernel Monitors. This explains the basis of his work on Solo5, which we are currently integrating into MirageOS as a KVM-based boot backend to complement the Xen port. You can also find his talk slides online.
  • Amir Chaudhry has given several talks and demos recently: check out his slides and detailed writeups about GlueCon 2016 and CraftConf 2016 in particular, as they come with instructions on how to reproduce his Mirage/ARM on-stage demonstrations of unikernels.
  • Sean Grove is speaking at Polyconf 2016 next week in Poland. If you are in the region, he would love to meet up with you as well -- his talk abstract is below
    With libraries like Mirage, js_of_ocaml, & ARM compiler output OCaml apps can operate at such a low level we don't even need operating systems on the backend anymore (removing 15 million lines of memory-unsafe code)
    • while at the same time, writing UI's is easier & more reliable than ever before, with lightweight type-checked code sharing between server, browser clients, & native mobile apps. We'll look at what's enabled by new tech like Unikernels, efficient JS/ARM output, & easy host interop.


MirageOS Spring 2016 hack retreat!

By Gemma Gordon

Hack retreat Trip Reports

We're looking forward to the next MirageOS hack retreat already! We've collected some reports from those who were present at our 2016 Spring hack retreat to share our excitement! Thanks to the folks who put in the time and effort to organize the event and our wonderful hosts, and a huge thanks to everyone who documented their hack retreat experience!

More information is also available at the Canopy site developed and used for information sharing during the hack retreat!

Trip Report

by David Kaloper


Last month, the MirageOS community saw its first community-organized, international hackathon. It took place between 11th and 16th March 2016. The venue? Rihad Priscilla, Marrakech, Morocco.

The place turned out to be ideal for a community building exercise. A city bursting with life, scents and colors, a relaxed and friendly hostel with plenty of space, warm and sunny weather -- all the elements of a good get-together were there. This is where some 25 hackers from all over the world convened, with various backgrounds and specialties, all sharing an interest in MirageOS.

Not wanting to limit ourselves to breaking only those conventions, we added another layer: the hackathon was set up as a classical anti-conference, with a bare minimum of structure, no pre-defined program, and a strong focus on one-on-one work, teaching, and collaboration.

As this was the first hackathon, this time the focus was on building up the nascent community that already exists around MirageOS. Faces were put to online handles, stories were exchanged, and connections were forged. Meeting in person helped bring a new level of cohesion to the online community around the project, as witnessed by the flurry of online conversations between people that were present, and have continued after the event ended.

One particularly useful (however inglorious) activity proved to be introducing people to the tool chain. Even though the MirageOS website has a documentation section with various documents on the architecture of the project, technical blog posts and a series of examples to get newcomers started, a number of people found it difficult to juggle all the concepts and tools involved. Where is the line dividing ocamlfind from opam and what exactly constitutes an OCaml library? What is the correct way to declare dependencies not covered by the declarative configuration language? When should one use add_to_opam_packages, and when add_to_ocamlfind_libraries? Will the mirage tool take care of installing missing packages so declared?

Although these questions either have answers scattered throughout the docs, or are almost obvious to an experienced MirageOS developer, such getting-started issues proved to be an early obstacle for a number of hackathon participants. While our project documentation certainly could -- and will! -- be improved with the perspective of a new developer in mind, this was an opportunity to help participants get a more comprehensive overview of the core tooling in an efficient, one-to-one setting. As a result, we saw a number of developers go from trying to get the examples to compile to making their own unikernels within a day, something pretty awesome to witness!

Another fun thread was dogfooding the network stack. Network itself was provided by our venue Priscilla, but we brought our own routers and access points. DHCP on site was served by Charrua, which stood up to the task admirably. We were able to access arbitrary domains on the Internet, almost all of the time!

A group of hackers had a strong web background, and decided to focus their efforts there. Perhaps the most interesting project to come out of this is Canopy. Canopy is best described as the first dynamic offering in the space of static web site generators! It combines Irmin with TyXML, COW, and Mirage HTTP, to create a simple, one-stop solution for putting content on the web. A Canopy unikernel boots, pulls a series of markdown files from a git repository, renders them, and serves them via HTTP. Expect more to come in this space, as Canopy has already proved to be a handy tool to simply put something on the web.

At the same time, the atmosphere was conducive for discussing how OCaml in general, and MirageOS in particular, fits in the web development ecosystem. As a language originally honed in different contexts, it's the opinion of a number of practicing web developers that the current OCaml ecosystem is not as conducive to supporting their day-to-day work as it could be. These brainstorming sessions led to a writeup which tries to summarize the current state and plot the course forward.

Another group of hackers was more focused on security and privacy technology. MirageOS boasts its own cryptographic core and a TLS stack, providing a solid base for development of cryptographic protocols. We saw coordinated work on improving the cryptographic layer; implementations of a few key-derivation functions (Scrypt and PBKDF); and even a beginning of an IKEv2 implementation.

A further common topic was networking, which is not entirely surprising for a network-centric unikernel platform. Amidst the enthusiasm, hackers in attendance started several projects related to general networking. These include a SWIM membership protocol implementation, the beginnings of telnet for Mirage, SOCKS4 packet handling, DNS wildcard matching, Charrua updates, and more.

In between these threads of activity, people used the time to get general MirageOS work done. This resulted in lots of progress including: making AFL, already supported by OCaml, run against MirageOS unikernels; a comprehensive update of error reporting across the stack; a concentrated push to move away from Camlp4 and adopt PPX; and producing a prototype unikernel displaying rich terminal output via telnet.

Partially motivated by the need to improve the experience of building and running unikernels, a number of hackers worked on improvements to error reporting and logging. Improving the experience when things go wrong will be an important part of helping folks make unikernels with MirageOS.

For more, and less structured, details of what went on, check out the blog some of us kept, or the meeting notes from the few short morning meetings we had.

It seems that when surrounded by like-minded, skilled people, in a pleasant atmosphere, and with absolutely nothing to do, people's curiosity will reliably kick in. In between lieing in the sun (sunscreen was a hot commodity!), sinking into the midday heat, and talking to other hackers, not a single person failed to learn, practice, or produce something new.

In this way, the first MirageOS hackathon was a resounding success. Friendships were forged, skills shared, and courses plotted. And although the same venue has already been booked for the next year's event, there is ongoing chit-chat about cutting the downtime in half with a summer edition!


MirageOS hackathon in Marrakech

Text and images by Enguerrand Decorne

Setting up and settling in

The first MirageOS hackathon was held from March 11th-16th 2016, at Priscilla, Queen of the Medina, Marrakech. It successfully gathered around 30 Mirage enthusiasts, some already familiar with the MirageOS ecosystem, and others new to the community. People travelled from Europe and further afield for a week of sun, tajine and hacking.

Main room

Getting to the guesthouse was an adventure, and once there we prepared by quickly setting up a nice internet hotspot then organised groups to head to the souk to meet new arrivals. Soon enough the guest house was filled with people, and various new projects and ideas began to emerge. Having a few books and experienced OCaml developers around helped the OCaml newcomers get stuck in, and it didn't take long to get their first unikernel or OCaml library up and running. Daily meetings were arranged at noon on the rooftop in order to allow the exchange of project ideas and questions, and we used the hackathon notepad to loosely pair projects and people together. Our DHCP server enabled extensive dogfooding and successfully fulfilled our project-testing needs.

Participants found a wide range of activities to keep themselves occupied during the event: contributing to the MirageOS Pioneer Projects, starting new projects and libraries, improving the MirageOS ecosystem and core components, discussing new ideas... or simply enjoying the sun, delicious tajine, or walking around Marrakech itself. Some expeditions were also (non)organised during the week, allowing sightseeing of the nicest local spots, or negotiating with local stallholders to get the best prices on souvenirs and fresh fruits to enjoy during hard hacking sessions.


My week inside the camel's nest

A few days before heading up to Marrakech (in a very non-organised fashion, having been offered a hackathon place only two days before!) the idea of writing some kind of notebook using Mirage had been floating around - we wanted to be able to allow people inside the hackathon to exchange ideas, and those not physically present to be kept updated about progress. I decided to write a simple blog unikernel, Canopy which relies on Irmin's capabilities to synchronise remote git repositiories. By describing new pages in a format similar to Jekyll (and using Markdown) on a git repository, new content pushed there would be pulled to the website and displayed there nicely. This allowed every participant to report on their current projects, and see the content displayed on the notepad after a simple git push.

The project was well received and new ideas started to emerge in order to turn it into a CMS enabling users to easily describe new website with a simple git repository. A huge thank you to Michele for his awesome contributions, as well as everyone involved with answering questions about the Mirage ecosystem along the way. This project also allowed me to dive a little further inside various libraries, report a few issues, discuss features and discover new concepts... A week well spent that I would be glad to repeat at the next MirageOS hackathon :)



This hackathon was a huge success and allowed the MirageOS community to combine sun and high productivity in a crazy yet very relaxing week. We hope (and plan) to see more events like this, so anyone interested in OCaml, Mirage - expert or not - is more than welcome to join us next time!


MirageOS + OCaml Newcomers

by Alfredo and Sonia

Our experience in Marrakesh was great. We really enjoyed the place, the weather, the food, the people and the atmosphere! I think the setting was a great win, there was lot of open space where you could find a quiet spot for yourself to concentrate while programming, as well as a place with lots of people coding, or a place where you could be talking about anything while enjoying the sun, or just hang out and get lost for a while in the nice Marrakesh's old city.

We had already learnt some OCaml, but we both are quite new to both OCaml and MirageOS, so we decided to work on a project with low entry barrier so we could get in the loop more easily. Nevertheless we had to invest some time getting more familiar with the specifics of the OCaml environment (libraries, packaging, testing frameworks, etc.). Hannes kindly helped us getting started, showing us a library (ocaml-hkdf) we could use to understand this all better, and from here we could start writing some code. Having most of the authors (Thomas, David, Hannes...) of the libraries we used (nocrypto, cstruct, alcotest, opam...) there with us was also a win. Finally we managed to release a pair of libraries with key derivation functions (ocaml-pbkdf and ocaml-scrypt-kdf), so we are quite happy with the outcome.

The only downside of the hackathon we can think of, if any, is that we didn't get too deep into the MirageOS specifics (something we are surely willing to fix!), but we wanted to stay focused to keep productive and had enough new things to learn.

Hackathon Projects

by Ximin Luo

Here's a list of things I did during the hackathon:

  • Read into ocaml-tls and ocaml-otr implementations, as well as David's "nqsb" TLS paper
  • Talked with David about developing a general pattern for implementing protocols, that allows one to compose components more easily and consistently. He pointed me to many resources that I could learn from and build on top of.
  • Read documents on "Extensible Effects", "Freer Monads" and "Iteratee pattern" by Oleg Kiselyov.
  • Read documents and source code of the Haskell Pipes library by Gabriel Gonzalez.
  • Sent some PRs to Hannes' jackline IM client, for better usability under some graphical environments.
  • Showed some people my ocaml-hello "minimal build scripts" example, and my ocaml emacs scripts.
  • Tested the "solo5" system that runs mirageos on kvm as an alternative to xen.

I'm continuing with the following work in my spare time:

  • Read documents and source code of the opam monadlib library with a view to extending this and unifying it with other libraries such as lwt.
  • Using the approach of the Haskel Pipes library to develop a general protocol handler framework. I'm experimenting initially in Haskell but I'd also like to do it in OCaml when the ideas are more solid.

In terms of the event it was great - everything worked out very well, I don't have any suggestions for improvements :)


MirageOS security advisory 00: mirage-net-xen

By Hannes Mehnert

MirageOS Security Advisory 00 - memory disclosure in mirage-net-xen

  • Module: mirage-net-xen
  • Announced: 2016-05-03
  • Credits: Enguerrand Decorne, Thomas Leonard, Hannes Mehnert, Mindy Preston
  • Affects: mirage-net-xen <1.4.2
  • Corrected: 2016-01-08 1.5.0 release, 2016-05-03 1.4.2 release

For general information regarding MirageOS Security Advisories, please visit

Hannes published a blog article about the analysis of this issue.


MirageOS is a library operating system using cooperative multitasking, which can be executed as a guest of the Xen hypervisor. Virtual devices, such as a network device, share memory between MirageOS and the hypervisor. MirageOS allocates and grants the hypervisor access to a ringbuffer containing pages to be sent on the network device, and another ringbuffer with pages to be filled with received data. A write on the MirageOS side consists of filling the page with the packet data, submitting a write request to the hypervisor, and awaiting a response from the hypervisor. To correlate the request with the response, a 16bit identifier is used.

Problem Description

Generating this 16bit identifier was not done in a unique manner. When multiple pages share an identifier, and are requested to be transmitted via the wire, the first successful response will mark all pages with this identifier free, even those still waiting to be transmitted. Once marked free, the MirageOS application fills the page for another chunk of data. This leads to corrupted packets being sent, and can lead to disclosure of memory intended for another recipient.


This issue discloses memory intended for another recipient. All versions before mirage-net-xen 1.4.2 are affected. The receiving side uses a similar mechanism, which may lead to corrupted incoming data (eventually even mutated while being processed).

Version 1.5.0, released on 8th January, already assigns unique identifiers for transmission. Received pages are copied into freshly allocated buffers before passed to the next layer. When 1.5.0 was released, the impact was not clear to us. Version 1.6.1 now additionally ensures that received pages have a unique identifier.


No workaround is available.


The unique identifier is now generated in a unique manner using a monotonic counter.

Transmitting corrupt data and disclosing memory is fixed in versions 1.4.2 and above.

The recommended way to upgrade is:

opam update
opam upgrade mirage-net-xen

Or, explicitly:

opam upgrade
opam reinstall mirage-net-xen=1.4.2

Affected releases have been marked uninstallable in the opam repository.

Correction details

The following list contains the correction revision numbers for each affected branch.

Memory disclosure on transmit:

master: 47de2edfad9c56110d98d0312c1a7e0b9dcc8fbf

1.4: ec9b1046b75cba5ae3473b2d3b223c3d1284489d

Corrupt data while receiving:

master: 0b1e53c0875062a50e2d5823b7da0d8e0a64dc37

1.4: 6daad38af2f0b5c58d6c1fb24252c3eed737ede4



You can find the latest version of this advisory online at

This advisory is signed using OpenPGP, you can verify the signature by downloading our public key from a keyserver (gpg --recv-key 4A732D757C0EDA74), downloading the raw markdown source of this advisory from GitHub and executing gpg --verify


Introducing Functoria

By Gabriel Radanne

For the last few months, I've been working with Thomas on improving the mirage tool and I'm happy to present Functoria, a library to create arbitrary MirageOS-like DSLs. Functoria is independent from mirage and will replace the core engine, which was somewhat bolted on to the tool until now.

This introduces a few breaking changes so please consult the breaking changes page to see what is different and how to fix things if needed. The good news is that it will be much more simple to use, much more flexible, and will even produce pretty pictures!


For people unfamiliar with MirageOS, the mirage tool handles configuration of mirage unikernels by reading an OCaml file describing the various pieces and dependencies of the project. Based on this configuration it will use opam to install the dependencies, handle various configuration tasks and emit a build script.

A very simple configuration file looks like this:

open Mirage
let main = foreign "Unikernel.Main" (console @-> job)
let () = register "console" [main $ default_console]

It declares a new functor, Unikernel.Main, which take a console as an argument and instantiates it on the default_console. For more details about unikernel configuration, please read the hello-world tutorial.


A much demanded feature has been the ability to define so-called bootvars. Bootvars are variables whose value is set either at configure time or at startup time.

A good example of a bootvar would be the IP address of the HTTP stack. For example, you may wish to:

  • Set a good default directly in the
  • Provide a value at configure time, if you are already aware of deployment conditions.
  • Provide a value at startup time, for last minute changes.

All of this is now possible using keys. A key is composed of:

  • name — The name of the value in the program.
  • description — How it should be displayed/serialized.
  • stage — Is the key available only at runtime, at configure time, or both?
  • documentation — This is not optional, so you have to write it.

Imagine we are building a multilingual unikernel and we want to pass the default language as a parameter. The language parameter is an optional string, so we use the `opt` and `string` combinators. We want to be able to define it both at configure and run time, so we use the stage `Both. This gives us the following code:

let lang_key =
  let doc =
      ~doc:"The default language for the unikernel." [ "l" ; "lang" ]
  Key.(create "language" Arg.(opt ~stage:`Both string "en" doc))

Here, we defined both a long option --lang, and a short one -l, (the format is similar to the one used by Cmdliner). In the unikernel, the value is retrieved with Key_gen.language ().

The option is also documented in the --help option for both mirage configure (at configure time) and ./my_unikernel (at startup time).

       -l VAL, --lang=VAL (absent=en)
           The default language for the unikernel.

A simple example of a unikernel with a key is available in mirage-skeleton in the `hello` directory.

Switching implementation

We can do much more with keys, for example we can use them to switch devices at configure time. To illustrate, let us take the example of dynamic storage, where we want to choose between a block device and a crunch device with a command line option. In order to do that, we must first define a boolean key:

let fat_key =
  let doc =
      ~doc:"Use a fat device if true, crunch otherwise." [ "fat" ]
  Key.(create "fat" Arg.(opt ~stage:`Configure bool false doc))

We can use the `if_impl` combinator to choose between two devices depending on the value of the key.

let dynamic_storage =
  if_impl (Key.value fat_key)
    (kv_ro_of_fs my_fat_device)

We can now use this device as a normal storage device of type kv_ro impl! The key is also documented in mirage configure --help:

       --fat=VAL (absent=false)
           Use a fat device if true, crunch otherwise.

It is also possible to compute on keys before giving them to if_impl, combining multiple keys in order to compute a value, and so on. For more details, see the API and the various examples available in mirage and mirage-skeleton.

Switching keys opens various possibilities, for example a generic_stack combinator is now implemented in mirage that will switch between socket stack, direct stack with DHCP, and direct stack with static IP, depending on command line arguments.

Drawing unikernels

All these keys and dynamic implementations make for complicated unikernels. In order to clarify what is going on and help to configure our unikernels, we have a new command: describe.

Let us consider the console example in mirage-skeleton:

open Mirage

let main = foreign "Unikernel.Main" (console @-> job)
let () = register "console" [main $ default_console]

This is fairly straightforward: we define a Unikernel.Main functor using a console and we instantiate it with the default console. If we execute mirage describe --dot in this directory, we will get the following output.

A console unikernel

As you can see, there are already quite a few things going on! Rectangles are the various devices and you'll notice that the default_console is actually two consoles: the one on Unix and the one on Xen. We use the if_impl construction — represented as a circular node — to choose between the two during configuration.

The key device handles the runtime key handling. It relies on an argv device, which is similar to console. Those devices are present in all unikernels.

The mirage device is the device that brings all the jobs together (and on the hypervisor binds them).

Data dependencies

You may have noticed dashed lines in the previous diagram, in particular from mirage to Unikernel.Main. Those lines are data dependencies. For example, the bootvar device has a dependency on the argv device. It means that argv is configured and run first, returns some data — an array of string — then bootvar is configured and run.

If your unikernel has a data dependency — say, initializing the entropy — you can use the ~deps argument on Mirage.foreign. The start function of the unikernel will receive one extra argument for each dependency.

As an example, let us look at the `app_info` device. This device makes the configuration information available at runtime. We can declare a dependency on it:

let main =
  foreign "Unikernel.Main" ~deps:[abstract app_info] (console @-> job)

A unikernel with info

The only difference with the previous unikernel is the data dependency — represented by a dashed arrow — going from Unikernel.Main to Info_gen. This means that Unikernel.Main.start will take an extra argument of type Mirage_info.t which we can, for example, print:

name: console
libraries: [functoria.runtime; lwt.syntax; mirage-console.unix;
            mirage-types.lwt; mirage.runtime; sexplib]
packages: [functoria.0.1; lwt.2.5.0; mirage-console.2.1.3; mirage-unix.2.3.1;

The complete example is available in mirage-skeleton in the `app_info` directory.


Since we have a way to draw unikernels, we can now observe the sharing between various pieces. For example, the direct stack with static IP yields this diagram:

A stack unikernel

You can see that all the sub-parts of the stack have been properly shared. To be merged, two devices must have the same name, keys, dependencies and functor arguments. To force non-sharing of two devices, it is enough to give them different names.

This sharing also works up to switching keys. The generic stack gives us this diagram:

A dynamic stack unikernel

If you look closely, you'll notice that there are actually three stacks in the last example: the socket stack, the direct stack with DHCP, and the direct stack with IP. All controlled by switching keys.

All your functors are belong to us

There is more to be said about the new capabilities offered by functoria, in particular on how to define new devices. You can discover them by looking at the mirage implementation.

However, to wrap up this blog post, I offer you a visualization of the MirageOS website itself (brace yourself). Enjoy!

Thanks to Mort, Mindy, Amir and Jeremy for their comments on earlier drafts.


Run Mirage Unikernels on KVM/QEMU with Solo5

By Dan Williams

I'm excited to announce the release of Solo5! Solo5 is essentially a kernel library that bootstraps the hardware and forms a base (similar to Mini-OS) from which unikernels can be built. It runs on fully virtualized x86 hardware (e.g., KVM/QEMU), using virtio device interfaces.

Importantly, Solo5 is integrated (to some extent) with the MirageOS toolstack, so the Solo5 version of the Mirage toolstack can build Mirage unikernels that run directly on KVM/QEMU instead of Xen. As such, Solo5 can be considered an alternative to Mini-OS in the Mirage stack. Try it out today!

In the rest of this post, I'll give a bit of motivation about why I think the lowest layer of the unikernel is interesting and important, as well as a rough overview of the steps I took to create Solo5.

Why focus so far down the software stack?

When people think about Mirage unikernels, one of the first things that comes to mind is the use of a high-level language (OCaml). Indeed, the Mirage community has invested lots of time and effort producing implementations of traditional system components (e.g., an entire TCP stack) in OCaml. The pervasive use of OCaml contributes to security arguments for Mirage unikernels (strong type systems are good) and is an interesting design choice well worth exploring.

But underneath all of that OCaml goodness is a little kernel layer written in C. This layer has a direct impact on:

  • What environments the unikernel can run on. Mini-OS, for example, assumes a paravirtualized (Xen) machine, whereas Solo5 targets full x86 hardware virtualization with virtio devices.

  • Boot time. "Hardware" initialization (or lack of it in a paravirtualized case) is a major factor in achieving the 20 ms unikernel boot times that are changing the way people think about elasticity in the cloud.

  • Memory layout and protection. Hardware "features" like page-level write protection must be exposed by the lowest layer for techniques like memory tracing to be performed. Also, software-level strategies like address space layout randomization require cooperation of this lowest layer.

  • Low-level device interfacing. As individual devices (e.g., NICs) gain virtualization capabilities, the lowest software layer is an obvious place to interface directly with hardware.

  • Threads/events. The low-level code must ensure that device I/O is asynchronous and/or fits with the higher-level synchronization primitives.

The most popular existing code providing this low-level kernel layer is called Mini-OS. Mini-OS was (I believe) originally written as a vehicle to demonstrate the paravirtualized interface offered by Xen for people to have a reference to port their kernels to and as a base for new kernel builders to build specialized Xen domains. Mini-OS is a popular base for MirageOS, ClickOS, and other unikernels. Other software that implements a unikernel base include Rumprun and OSv.

I built Solo5 from scratch (rather than adapting Mini-OS, for example) primarily as an educational (and fun!) exercise to explore and really understand the role of the low-level kernel layer in a unikernel. To provide applications, Solo5 supports the Mirage stack. It is my hope that Solo5 can be a useful base for others; even if only at this point to run some Mirage applications on KVM/QEMU!

Solo5: Building a Unikernel Base from Scratch

At a high level, there are roughly 3 parts to building a unikernel base that runs on KVM/QEMU and supports Mirage:

  • Typical kernel hardware initialization. The kernel must know how to load things into memory at the desired locations and prepare the processor to operate in the correct mode (e.g., 64-bit). Unlike typical kernels, most setup is one-time and simplified. The kernel must set up a memory map, stack, interrupt vectors, and provide primitives for basic memory allocation. At its simplest, a unikernel base kernel does not need to worry about user address spaces, threads, or many other things typical kernels need.

  • Interact with virtio devices. virtio is a paravirtualized device standard supported by some hypervisors, including KVM/QEMU and Virtualbox. As far as devices go, virtio devices are simple: I was able to write (very simple/unoptimized) virtio drivers for Solo5 drivers from scratch in C. At some point it may be interesting to write them in OCaml like the Xen device drivers in Mirage, but for someone who doesn't know OCaml (like me) a simple C implementation seemed like a good first step. I should note that even though the drivers themselves are written in C, Solo5 does include some OCaml code to call out to the drivers so it can connect with Mirage.

  • Appropriately link Mirage binaries/build system. A piece of software called mirage-platform performs the binding between Mini-OS and the rest of the Mirage stack. Building a new unikernel base means that this "cut point" will have lots of undefined dependencies which can either be implemented in the new unikernel base, stubbed out, or reused. Other "cut points" involve device drivers: the console, network and block devices. Finally, the mirage tool needs to output appropriate Makefiles for the new target and an overall Makefile needs to put everything together.

Each one of these steps carries complexity and gotchas and I have certainly made many mistakes when performing all of them. The hardware initialization process is needlessly complex, and the overall Makefile reflects my ignorance of OCaml and its building and packaging systems. It's a work in progress!

Next Steps and Getting Involved

In addition to the aforementioned clean up, I'm currently exploring the boot time in this environment. So far I've found that generating a bootable iso with GRUB as a bootloader and relying on QEMU to emulate BIOS calls to load the kernel is, by the nature of emulation, inefficient and something that should be avoided.

If you find the lowest layer of the unikernel interesting, please don't hesitate to contact me or get involved. I've packaged the build and test environment for Solo5 into a Docker container to reduce the dependency burden in playing around with it. Check out the repo for the full instructions!

I'll be talking about Solo5 at the upcoming 2016 Unikernels and More: Cloud Innovators Forum event to be held on January 22, 2016 at SCALE 14X in Pasadena, CA USA. I look forward to meeting some of you there!

Discuss this post on

Thanks to Amir, Mort, and Jeremy, for taking the time to read and comment on earlier drafts.


Introducing Charrua — a DHCP implementation

By Christiano Haesbaert

Almost every network needs to support DHCP (Dynamic Host Configuration Protocol), that is, a way for clients to request network parameters from the environment. Common parameters are an IP address, a network mask, a default gateway and so on.

DHCP can be seen as a critical security component, since it deals usually with unauthenticated/unknown peers, therefore it is of special interest to run a server as a self-contained MirageOS unikernel.

Charrua is a DHCP implementation written in OCaml and it started off as an excuse to learn more about the language. While in development it got picked up on the MirageOS mailing lists and became one of the Pioneer Projects.

The name Charrua is a reference to the, now extinct, semi-nomadic people of southern South America — nowadays it is also used to refer to Uruguayan nationals. The logic is that DHCP handles dynamic (hence nomadic) clients.

The library is platform agnostic and works outside of MirageOS as well. It provides two main modules: Dhcp_wire and Dhcp_server.


Dhcp_wire provides basic functions for dealing with the protocol, essentially marshalling/unmarshalling and helpers for dealing with the various DHCP options.

The central record type of Dhcp_wire is a pkt, which represents a full DHCP packet, including layer 2 and layer 3 data as well as the many possible DHCP options. The most important functions are:

val pkt_of_buf : Cstruct.t -> int -> [> `Error of string | `Ok of pkt ]
val buf_of_pkt : pkt -> Cstruct.t

pkt_of_buf takes a Cstruct.t buffer and a length and it then attempts to build a DHCP packet. Unknown DHCP options are ignored, invalid options or malformed data are not accepted and you get an `Error of string.

buf_of_pkt is the mirror function, but it never fails. It could for instance fail in case of two duplicate DHCP options, but that would imply too much policy in a marshalling function.

The DHCP options from RFC2132 are implemented in dhcp_option. There are more, but the most common ones look like this:

type dhcp_option =
  | Subnet_mask of Ipaddr.V4.t
  | Time_offset of int32
  | Routers of Ipaddr.V4.t list
  | Time_servers of Ipaddr.V4.t list
  | Name_servers of Ipaddr.V4.t list
  | Dns_servers of Ipaddr.V4.t list
  | Log_servers of Ipaddr.V4.t list


Dhcp_server Provides a library for building a DHCP server and is divided into two sub-modules: Config, which handles the building of a suitable DHCP server configuration record and Input, which handles the input of DHCP packets.

The logic is modelled in a pure functional style and Dhcp_server does not perform any IO of its own. It works by taking an input packet, a configuration and returns a possible reply to be sent by the caller, or an error/warning:


type result = 
| Silence                 (* Input packet didn't belong to us, normal nop event. *)
| Reply of Dhcp_wire.pkt  (* A reply packet to be sent on the same subnet. *)
| Warning of string       (* An odd event, could be logged. *)
| Error of string         (* Input packet is invalid, or some other error ocurred. *)

val input_pkt : Dhcp_server.Config.t -> Dhcp_server.Config.subnet ->
   Dhcp_wire.pkt -> float -> result
(** input_pkt config subnet pkt time Inputs packet pkt, the resulting action
    should be performed by the caller, normally a Reply packet is returned and
    must be sent on the same subnet. time is a float representing time as in
    Unix.time or MirageOS's Clock.time. **)

A typical main server loop would work by:

  1. Reading a packet from the network.
  2. Unmarshalling with Dhcp_wire.pkt_of_buf.
  3. Inputting the result with Dhcp_server.Input.input_pkt.
  4. Sending the reply, or logging the event from the Dhcp_server.Input.input_pkt call.

A mainloop example can be found in mirage-skeleton:

  let input_dhcp c net config subnet buf =
    let open Dhcp_server.Input in
    match (Dhcp_wire.pkt_of_buf buf (Cstruct.len buf)) with
    | `Error e -> Lwt.return (log c (red "Can't parse packet: %s" e))
    | `Ok pkt ->
      match (input_pkt config subnet pkt (Clock.time ())) with
      | Silence -> Lwt.return_unit
      | Warning w -> Lwt.return (log c (yellow "%s" w))
      | Error e -> Lwt.return (log c (red "%s" e))
      | Reply reply ->
        log c (blue "Received packet %s" (Dhcp_wire.pkt_to_string pkt));
        N.write net (Dhcp_wire.buf_of_pkt reply)
        >>= fun () ->
        log c (blue "Sent reply packet %s" (Dhcp_wire.pkt_to_string reply));

As stated, Dhcp_server.Input.input_pkt does not perform any IO of its own, it only deals with the logic of analyzing a DHCP packet and building a possible answer, which should then be sent by the caller. This allows a design where all the side effects are controlled in one small chunk, which makes it easier to understand the state transitions since they are made explicit.

At the time of this writing, Dhcp_server.Input.input_pkt is not side effect free, as it manipulates a database of leases, this will be changed in the next version to be pure as well.

Storing leases in permanent storage is also unsupported at this time and should be available soon, with Irmin and other backends. The main idea is to always return a new lease database for each input, or maybe just the updates to be applied, and in this scenario, the caller would be able to store the database in permanent storage as he sees fit.


This project started independently of MirageOS and at that time, the best configuration I could think of was the well known ISC dhcpd.conf. Therefore, the configuration uses the same format but it does not support the myriad of options of the original one.

  type t = {
    addresses : (Ipaddr.V4.t * Macaddr.t) list;
    subnets : subnet list;
    options : Dhcp_wire.dhcp_option list;
    hostname : string;
    default_lease_time : int32;
    max_lease_time : int32;

  val parse : string -> (Ipaddr.V4.Prefix.addr * Macaddr.t) list -> t
  (** [parse cf l] Creates a server configuration by parsing [cf] as an ISC
      dhcpd.conf file, currently only the options at [sample/dhcpd.conf] are
      supported. [l] is a list of network addresses, each pair is the output
      address to be used for building replies and each must match a [network
      section] of [cf]. A normal usage would be a list of all interfaces
      configured in the system *)

Although it is a great format, it doesn't exactly play nice with MirageOS and OCaml, since the unikernel needs to parse a string at runtime to build the configuration, this requires a file IO backend and other complications. The next version should provide OCaml helpers for building the configuration, which would drop the requirements of a file IO backend and facilitate writing tests.

Building a simple server

The easiest way is to follow the mirage-skeleton DHCP README.


The next steps would be:

  • Provide helpers for building the configuration.
  • Expose the lease database in an immutable structure, possibly a Map, adding also support/examples for Irmin.
  • Use Functoria to pass down the configuration in mirage-skeleton. Currently it is awkward since the user has to edit and, with Functoria we would be able to have it much nicer and only touch
  • Convert MirageOS DHCP client code to use Dhcp_wire, or perhaps add a client logic functionality to Charrua.

Finishing words

This is my first real project in OCaml and I'm more or less a newcomer to functional programming as well, my background is mostly kernel hacking as an ex-OpenBSD developer. I'd love to hear how people are actually using it and any problems they're finding, so please do let me know via the issue tracker!

Prior to this project I had no contact with any of the MirageOS folks, but I'm amazed about how easy the interaction and communication with the community has been, everyone has been incredibly friendly and supportive. I'd say MirageOS is a gold project for anyone wanting to work with smart people and hack OCaml.

My many thanks to Anil, Richard, Hannes, Amir, Scott, Gabriel and others. Thanks also to Thomas and Christophe for comments on this post. I also would like to thank my employer for letting me work on this project in our hackathons.


By Amir Chaudhry

Unikernels are specialised single address space machine images that are constructed by using library operating systems. With MirageOS, we've taken a clean-slate approach to unikernels with a focus on safety. This involved writing protocol libraries from the ground up and it also afforded the ability to use clean, modern APIs.

Other unikernel implementations have made trade-offs different to those made by MirageOS. Some excel at handling legacy applications by making the most of existing OS codebases rather than building clean-slate implementations. Some target a wide array of possible environments, or environments complementary to those supported by MirageOS currently. All of these implementations ultimately help developers construct unikernels that match their specific needs and constraints.

As word about unikernels in general is spreading, more people are trying to learn about this new approach to programming the cloud and embedded devices. Since information is spread across multiple sites, it can be tricky to know where to get an overview and how to get started quickly. So to help people get on board, there's a new community website at!

The community site aims to collate information about the various projects and provide a focal point for early adopters to understand more about the technology and become involved in the projects themselves.

Over time, it will also become a gathering place for common infrastructure to form and be shared across projects. Early examples of this include the scripts for booting on Amazon EC2, which began with MirageOS contributors but were used and improved by Rump Kernel contributors. You can follow the email threads where the script was first proposed and ultimately provided EC2 support for Rumprun. Continuing to work together to make such advances will ease the process of bringing in new users and contributors across all the projects.

Please do visit the site and contribute stories about how you're using and improving unikernels!

Edit: discuss this post on

Thanks to Anil, Jeremy and Mindy for comments on an earlier draft.


Videos from around the world!

By Amir Chaudhry

Word about Unikernels and MirageOS is spreading and as the community grows, more people have been giving talks at user groups and conferences. Below are a selection of those that have been recorded, which alone is about 5 hours of content. The topics are wide ranging and include discussions about where unikernels fit in the ecosystem, all the way down to networking topics.

I hope you enjoy these and if you'd like to give a talk somewhere or share one of your videos, please do get in touch!

Videos of recent talks

Anil Madhavapeddy at Esper Technologies - May 2015
'Unikernels: Functional Infrastructure with MirageOS'

Russell Pavlicek at SouthEast LinuxFest - June 2015
'Next Generation Cloud'

Russ has also been at many other Linuxfests this year!

Amir Chaudhry at PolyConf - July 2015

There's more information about this talk in the blog post at:

Hannes Mehnert at Source_Code Berlin - Aug 2015
'Leaving legacy behind — A clean-slate approach to operating systems'

Steve Jones at DevopsDays Pittsburgh - Aug 2015
'The Incredible Shrinking Operating System!'

Mindy Preston at Strangeloop - Sep 2015
'Non-Imperative Network Programming'

Mindy also wrote a post that has some information and links:

Matt Bajor at Strangeloop - Sep 2015
'Look ma, no OS! Unikernels and their applications'

Gareth Rushgrove at Operability - Sep 2015
'Taking the Operating System out of Operations'

Garett Smith at CityCode - Oct 2015
'Rainbows and Unikernels'


Getting Started Screencasts

By Amir Chaudhry

We put together some quick screencasts to make it easier for people to get started with MirageOS. They're all in a playlist below, which is around 10 minutes in total.

There are currently 4 videos which walk through some of the typical steps. The first three cover installation, building a 'hello world', and building a Xen unikernel on an Ubuntu machine. The fourth video gives an overview of the development workflows that are possible with OPAM and Git.

These should give everyone a clear idea of what it's like to work with the tools before leaping in and installing things!

If anyone would like to help us make more of these screencasts, please do get in touch on the mailing list — I've also listed it as one of our many Pioneer Projects!


Organized chaos: managing randomness

By David Kaloper

This post gives a bit of background on the Random Number Generator (RNG) in the recent MirageOS v2.5 release.

First we give background about why RNGs are really critical for security. Then we try to clarify the often-confused concepts of "randomness" and "entropy", as used in this context. Finally, we explore the challenges of harvesting good-quality entropy in a unikernel environment.

Playing dice

Security software must play dice.

It must do so to create secrets, for example. Secrets can then serve as the keys that protect communication, like the Diffie-Hellman key exchanged between two TLS endpoints. Proof of the knowledge of a particular secret can be used to verify the identity of someone on the Internet, as in the case of verifying the possession of the secret RSA key associated with an X.509 certificate. As an attacker guessing a secret can have disastrous consequences, it must be chosen in a manner that is realistically unpredictable by anyone else — we need it to be random.

There are other reasons to use randomness. A number of algorithms require a unique value every time they are invoked and badly malfunction when this assumption is violated, with random choice being one way to provide a value likely to be unique. For example, repeating the k-parameter in DSA digital signatures compromises the secret key, while reusing a GCM nonce negates both confidentiality and authenticity. Other algorithms are probabilistic, in that they generate random values before operating on an input, and then store the chosen values in the output, such as the OAEP padding mode for RSA. This is done in order to confuse the relationship between the input and the output and defeat a clever attacker who tries to manipulate the input to gain knowledge about secrets by looking at the output. Still other algorithms pick random numbers to internally change their operation and hide the physical amount of time they need to execute, to avoid revealing information about the secrets they operate on. This is known as blinding, and is one way to counter timing side-channel attacks.

Randomness is therefore quite pervasive in a security context. In fact, many cryptographic algorithms are designed under the assumption of a readily available source of randomness, termed a random oracle. The security analysis of those algorithms is conditional on the oracle; we know that they have certain security characteristics, like the difficulty of guessing the correct message or impersonating somebody, only given an ideal random oracle.

And so security software has a problem here. Computers are inherently deterministic, made to behave reproducibly given a known program and starting state. How to go about solving this?

Random failures

Before taking a look at how we try to solve this problem, let's instead consider what happens if we fail to do so. There is even a Wikipedia page about this, which is a nice starting point. Some of the highlights:

The first public release of Netscape's original SSL, version 2.0, was broken several months after its release. The weakness was in initializing the RNG with the current time, the process ID and the parent process ID of the browser. The time stamp can be guessed to a certain precision, leaving only its sub-second part and the two PIDs unknown. This relatively small unknown space of initial values can be brute-forced.

About a decade later, Debian patched their version of OpenSSL and reduced RNG initialization to the current PID. As a result, only 32767 random sequences were possible. This flaw went undetected for two years and became known as the Debian fiasco. Personal reports indicate that some of the 32767 distinct secret keys that could be generated with OpenSSL on a Debian system during that time are still in circulation.

Computing the largest common divisor of a pair of numbers is much faster than discovering all the prime divisors of a particular number. RSA public keys contain a number, and secret keys contain its factors. An RSA key is usually generated by randomly picking the factors. If a pool of keys was generated with a heavily biased random number generator, such that factors are likely to repeat, it is possible to search for common factors in all pairs and crack the affected keys, a technique which produces spectacular results.

Recently, a bitcoin application for Android was discovered to be downloading its random initial value from a website. It wasn't even necessary to intercept this unencrypted traffic, because the website started serving a redirect page and the Android application was left initializing its RNG with the text of the redirection message. It therefore started generating the same private ECDSA key and the associated bitcoin address for every affected user, an issue which reportedly cost some users their bitcoins.

Playstation 3 game signatures can be forged. Sony reused a single k-parameter, which is supposed to be "unique, unpredictable and secret", for every ECDSA signature they made. This lead to complete compromise of the signing keys. Admittedly, this is not really an RNG problem in itself, but it shows where such a malfunction can lead.

These are only some of the most spectacular failures related to random numbers. For example, it is widely known in security circles that RNGs of embedded devices tend to be predictable, leading to widespread use of weak keys on routers and similar equipment, amongst other things. So when implementing a unikernel operating system, you don't want to end up on that Wikipedia page either.

Random sequences and stuff

But what are random numbers, really? Intuitively, we tend to think about them as somehow "dancing around", or being "jiggly" in a sense. If we have a software component that keeps producing random outputs, these outputs form a sequence, and we hope this to be a random sequence.

But such a thing is notoriously difficult to define. The above linked page opens with the following quote:

A random sequence is a vague notion... in which each term is unpredictable to the uninitiated and whose digits pass a certain number of tests traditional with statisticians.

The intuitive jigglyness is captured by statistical randomness. We require each output, taken independently, to come from the same distribution (and in fact we want it to be the uniform distribution). That is, when we take a long sequence of outputs, we want them to cover the entire range, we want them to cover it evenly, and we want the evenness to increase as the number of outputs increases — which constitutes a purely frequentist definition of randomness. In addition, we want the absence of clear patterns between outputs. We don't want the sequence to look like 7, 8, 9, 10, ..., even with a bit of noise, and we don't want correlation between outputs. The problem here is that no-one really knows what "having patterns" means; it is entirely possible that we only searched for patterns too simple, and that in fact there is a pattern that fully explains the sequence lurking just around the complexity corner.

Nonetheless, there is a well established battery of tests to check statistical randomness of RNG outputs, called the Diehard Tests, and serves as the de-facto standard for testing random number generators. Here's the beginning of a certain sequence that passes the test with flying colors:

3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5, 8, 9, 7, 9, 3, 2, 3, 8, 4, 6, 2, 6, 4, 3, 3, 8, 3, ...

We still would not recommend using digits of π as a secret key. Neither would we recommend releasing software for everyone to study, which uses that sequence to generate the secrets. But what went wrong?

The other concept of randomness. Roughly, a random sequence should not be predictable to anyone with any knowledge other than the sequence itself. In other words, it cannot be compressed no matter how much we try, and in the extreme, this means that it cannot be generated by a program. While the latter restriction is obviously a little too strong for our purpose, it highlights a deep distinction in what people mean by being "random". Jumping around is one thing. Being actually unpredictable is a wholly different matter.

There are many other simple mathematical processes which generate sequences with high statistical randomness. Many of those are used to produce "random" sequences for various purposes. But these are still completely deterministic processes that exhibit random behaviour only in the statistical sense. Instead of being random, they are pseudo-random, and we call such generators Pseudo-Random Number Generators (PRNGs).

We can look for something approaching a "true" random sequence in nature. The current agreement is that the nature of quantum processes is random in this sense, and random sequences based on this idea are readily available for download. Or we can use the microphone and keep recording; the lowest-order bits of the signal are pretty unpredictable. But we cannot write a program to generate an actually random sequence.

Still, we need to compromise. The real problem of common PRNGs is that knowing the rule and observing some of the outputs is enough to predict the rest of the sequence. The entire future behavior of Mersenne twister, one of the most commonly used generators in various programming packages, can be predicted after observing only 624 outputs in a row. A step up from such a process is a Cryptographically Secure Pseudo-Random Number Generator (CSPRNG). Their key property is that it is computationally prohibitively expensive to distinguish their outputs from a "true" random sequence. This also means that it is computationally prohibitively expensive to reconstruct their internal state, just by looking at their outputs. In a sense, someone trying to predict the outputs can not take shortcuts, and is instead forced to perform the laborious task of starting the generator with all the possible states and checking if the output matches the observed sequence. This is how we can quantify a CSPRNG unpredictability: it takes trying about half of all the possibilities to guess the state.

MirageOS' security stack contains a CSPRNG, a design called Fortuna. What it really does, is encrypt the simple sequence 0, 1, 2, 3, ... with AES (AES-CTR) using a secret key. This makes it as resistant to prediction as AES is to known-plaintext attacks. After each output, it generates a bit more, hashes that, and uses the result as the next key. This is not to improve the statistical randomness, as it is already guaranteed by AES. Rather, it's a form of forward secrecy: an attacker who learns the secret key at some point would need to perform the preimage attack on the hash function to figure out the earlier key and reconstruct the earlier outputs.


Although resistant to prediction based solely on the outputs, just like any other software RNG, Fortuna is still just a deterministic PRNG. Its entire output is as unpredictable as its initial value, which we call the seed. From the information perspective, a PRNG can only transform what was unpredictable about its initial seed into an equally unpredictable sequence. In other words, we typically use PRNGs to stretch the unpredictability inherent in the initial seed into an infinite stream. The best PRNGs do not give out more hints about their starting position, but they can never out-race the amount of unpredictability that they started with.

We often call this quality of unpredictability entropy. In a sense, by employing an algorithmic generator, we have just shifted the burden of being unpredictable to the beginning. But now we're cornered and have to search for entropy in the only place where a computer can find it: in the physical world.

A typical (kernel-level) RNG-system reaches out into the world around it through hardware interaction: as hardware events happen, various drivers tend to emit small packets of data, such as the time, or hardware-specific state. These events are a product of the user interactions with the keyboard and mouse, of network packets arriving at an interface, of the hard drive asserting interrupts to signal the end of a DMA transfer, and the like. They are combined together and used to seed the internal (CS-)PRNG.

In fact, describing them as a seed from which the entire sequence is unfolded is a deliberate oversimplification: what really happens is that the PRNG is continuously fed with random events, which change its state as they arrive, and the requests for random bytes are served from the PRNG. The PRNG is used to "mix" the unpredictability inherent in its input, that is, to smooth out various timestamps and similar values into a statistically well-behaved sequence.

Do Virtual Machines Dream of Electric Sheep?

Our problem here is that a virtual machine (VM) in a typical configuration barely sees any physical hardware. Users do not interact with VMs in server scenarios using a directly-connected keyboard and mouse. VMs make use of a virtualized network interface and virtualized disks. Even the CPU features can be intercepted and virtualized. Virtual environments are entropy-starved.

This is a known problem and various analyses of the weakness of random outputs in virtual environments have been published. The problem is especially severe right after boot. The gradual trickle of unpredictability from hardware events slowly moves the pseudo-random stream into an increasingly unpredictable state, but at the very start, it still tends to be fairly predictable. Typically, operating systems store some of their PRNG output on shutdown and use it to quickly reseed their PRNG on the next boot, in order to reuse whatever entropy was contained in its state. Unfortunately, it is common to boot several machines from the same system image, or from a pristine image lacking a seed, making random outputs in a virtual machine vulnerable to prediction close to the startup phase.

To help solve these problems, we employ several sources of entropy in MirageOS unikernels. The case of a Unix executable is simple, as we reuse the system's own RNG, as exposed via /dev/urandom, as the source of our entropy. This is because the kernel is in a much better position to enter an unpredictable state than any single process running under its supervision. The case of Xen unikernels is harder. Here, we group the entropy sources into those that originate within the unikernel itself, and those that originate externally.

In the external case, we again rely on the kernel interacting with the hardware, but this time it's the dom0 kernel. We have a background service, Xentropyd, which runs in dom0, reads the RNG and serves its output to other domains through the Xen Console. The problem is that in many scenarios, like hosting on popular cloud providers, we cannot expect this degree of cooperation from dom0. A bigger problem is that although most of the code is present, we haven't fully fleshed out this design and it remains disabled in MirageOS 2.5.0

So we need to be able to achieve unpredictability relying purely on what is available inside a unikernel. A unikernel has no direct exposure to the hardware, but it is of course interacting with the outside world. To tap into this ambient entropy, we have to continuously sample all inter-event timings in its event loop. This process is analogous to what happens in a full-blown OS kernel, except our events lack the extra hardware context, and our timers are potentially less granular (for example, on ARM). This makes our interaction-based events somewhat more predictable, or in other words, they have a little less entropy.

Recent Intel chips come with an on-die random generator, which ultimately derives from thermal readings, and is available through RDRAND and (more directly) RDSEED instructions. The community has expressed concern that relying exclusively on this generator might not be a wise choice: it could silently malfunction, and its design is hidden in the hardware, which raises concerns about potential intentional biases in the output — a scheme not unheard of. However, since entropy is additive, its output can never reduce whatever unpredictability the system already has. Therefore, if available, we continuously sample this on-die RNG, and inject its outputs into our PRNG.

The combination of event timings and a built-in RNG does have good unpredictability in the long run, especially if our unikernel is running on a multi-tenant host and competing for CPU with other instances. But the entropy in each individual event is still relatively low: we can assume that a determined attacker can guess each individual time stamp up to a certain precision that we don't know, but which is potentially quite high. This creates the following problem: imagine that an attacker knows the current PRNG state, and can measure the time of the next event, but not with sufficient precision to know the last two bits of the timestamp. To this attacker, our event contains two bits of entropy. If we immediately update the PRNG, the attacker only has to observe some of the output and check four candidate states against it, to fully recover knowledge about the state and negate our entropy addition. On the other hand, if we decide to wait and try to accumulate many more events before updating the PRNG, we keep generating a fully predictable sequence in the meantime.

And here is where Fortuna really shines. It keeps accumulating events in a number of internal pools in a round-robin fashion. These pools are constantly being activated, but with an exponentially decreasing frequency. The pools activated too frequently are wasted, but one of them is activated with just the right frequency to contain enough entropy to make it prohibitively expensive for an attacker to enumerate all the possibilities. This design was shown to be within a constant factor from optimal entropy use, and in particular, scales robustly with the actual amount of entropy inherent in the input events.

This leaves us with the problem of boot-time entropy. Not only can the saved random seed be reused by cloning the disk image, but in many cases, a MirageOS unikernel is running without such storage at all!

Following the design of Whirlwind RNG, we employ an entropy bootstrapping loop. It's an iterated computation, which measures the time it took to perform the previous iteration, and then performs the amount of work that depends on the time, many times over. In this way, it creates a feedback loop with a fragile dependency on any non-determinism in the physical execution on the CPU, such as any contention or races in the CPU state. Even on ARM, which currently uses a less fine-grained timer and whose design is not as parallel as Intel's, this yields an initial value which varies wildly between boots. We use this value to kickstart the PRNG, giving it quick divergence, and ensuring that the state is unpredictable from the very start.

Parting words

While some of our techniques (in particular bootstrapping on ARM) need a little more exposure before we place our full confidence in them — and users should probably avoid generating long-term private keys in unikernels running on bare Xen just yet — the combination of boostrapping, continuous reseeding, and robust accumulation gives us a hopefully comprehensive solution to generating randomness in a unikernel environment.

We intend to re-evaluate the effectiveness of this design after getting some experience with how it works in the wild. To this end, we particularly appreciate the community feedback and you can reach us through our mailing list, or hop onto libera chat and join #mirage.

Thanks to Daniel, Mort and Amir for their comments on earlier drafts.


Easy HTTPS Unikernels with mirage-seal

By Mindy Preston, Hannes Mehnert

Unikernels: HTTP -> HTTPS

Building a static website is one of the better-supported user stories for MirageOS, but it currently results in an HTTP-only site, with no capability for TLS. Although there's been a great TLS stack available for a while now, it was a bit fiddly to assemble the pieces of TLS, Cohttp, and the MirageOS frontend tool in order to construct an HTTPS unikernel. With MirageOS 2.5, that's changed! Let's celebrate by building an HTTPS-serving unikernel of our very own.


Get a Certificate

To serve HTTPS, we'll need a certificate to present to clients (i.e., browsers) for authentication and establishing asymmetric encryption. For just testing things out, or when it's okay to cause a big scary warning message to appear for anyone browsing a site, we can just use a self-signed certificate. Alternatively, the domain name registrar or hosting provider for a site will be happy to sell (or in some cases, give!) a certificate -- both options are explained in more detail below.

Whichever option you choose, you'll need to install certify to get started (assuming you'd like to avoid using openssl). To do so, pin the package in opam:

opam pin add certify
opam install certify


It's not strictly necessary to get someone else to sign a certificate. We can create and sign our own certificates with the selfsign command-line tool. The following invocation will create a secret key in secrets/server.key and a public certificate for the domain in secrets/server.pem. The certificate will be valid for 365 days, so if you choose this option, it's a good idea set a calendar reminder to renew it if the service will be up for longer than that. The key generated will be a 2048-bit RSA key, although it's possible to create certificates valid for different lengths -- check selfsign --help for more information.

selfsign -c secrets/server.pem -k secrets/server.key -d 365 totallyradhttpsunikernel.example

We can now use this key and certificate with mirage-seal! See "Packaging Up an HTTPS Site with Mirage-Seal" below.

Signed by Someone Else

Although there are many entities that can sign a certificate with different processes, most have the following in common:

1) you generate a request to have a certificate made for a domain 2) the signing entity requests that you prove your ownership over that domain 3) once verified, the signing entity generates a certificate for you

Generating a Certificate-Signing Request

No matter whom we ask to sign a certificate, we'll need to generate a certificate signing request so the signer knows what to create. The csr command-line tool can do this. The line below will generate a CSR (saved as server.csr) signed with a 2048-bit RSA key (which will be saved as server.key), for the organization "Rad Unikernel Construction, Ltd." and the common name "totallyradhttpsunikernel.example". For more information on csr, try csr --help.

csr -c server.csr -k server.key totallyradhttpsunikernel.example "Rad Unikernel Construction, Ltd."

csr will generate a server.csr that contains the certificate signing request for submission elsewhere.


My domain is registered through the popular registrar, who happen to give a free TLS certificate for one year with domain registration, so I elected to have them sign a certificate for me (Gandi did not pay a promotional consideration for this mention). Most of this process is managed through their web GUI and a fairly large chunk is automatically handled behind the scenes. Here's how you can do it too:

Log in to the web interface available through the registrar's website. You can start the certificate signing process from the "services" tab, which exposes an "SSL" subtab. Click that (Gandi doesn't need to know that we intend only to support TLS, not SSL). Hit the "Get an SSL Certificate" button. Standard SSL is fine. Even if you're entitled to a free certificate, it will appear that you need to pay here; however at checkout, the total amount due will be 0 in your preferred currency. Ask for a single address and, if you want to pay nothing, a valid period of 1 year.

Copy the content of the certificate-signing request you generated earlier and paste it into the web form. Gandi will also ask you to identify your TLS stack; unfortunately ocaml-tls isn't in the drop-down menu, so choose OTHER (and perhaps send them a nice note asking them to add the hottest TLS stack on the block to their list). Click "submit" and click through the order form.

If you're buying a certificate for a domain you have registered through Gandi (via the registered account), the rest of the process is pretty automatic. You should shortly receive an e-mail with a subject like "Procedure for the validation of your Standard SSL certificate", which explains the process in more detail, but really all you need to do is wait a while (about 30 minutes, for me). After the certificate has been generated, Gandi will notify you by e-mail, and you can download your certificate from the SSL management screen. Click the magnifying glass next to the name of the domain for which you generated the cert to do so.

Once you've downloaded your certificate, you may also wish to append the intermediate certificates. Here's a help page on gathering intermediate certificates. Equipped with the intermediate certificates, append them to the signed certificate downloaded for your site to provide a full certificate chain:

cat signed_cert.pem intermediate_certs.pem > server.pem

Another free TLS certificate provider is StartSSL. During online registration, StartSSL will generate a TLS client certificate for you. This is used for authentication of yourself towards their service.

You need to validate that you own the domain you want to request a certificate for. This is done via the "Validations Wizard", which lets you choose to validate a domain via "Domain Name Validation". There you enter your domain name, and receive an eMail with a token which you have to enter into the web interface.

Once done, run csr to create a key and a certificate signing request. Go to the "Certificates Wizard", select "Web Server SSL/TLS Certificate", skip the generation of the private key (you already generated one with csr), copy and paste your certificate signing request (only the public key of that CSR is used, everything else is ignored), select a domain name, and immediately receive your certificate.

Make sure to also download their intermediate CA certificate, and append them:

cat intermediate.pem cert.pem > server.pem

Packaging Up an HTTPS Site with Mirage-Seal

Equipped with a private key and a certificate, let's make an HTTPS unikernel! First, use opam to install mirage-seal. If opam or other MirageOS tooling aren't set up yet, check out the instructions for getting started.

opam install mirage-seal

mirage-seal has a few required arguments.

  • --data: one directory containing all the content that should be served by the unikernel. Candidates for such a directory are the top-level output directory of a static site generator (such as public for octopress), the DocumentRoot of an Apache configuration, or the root of an nginx configuration.
  • --keys: one directory containing the certificate (server.pem) and key (server.key) for the site.

There are also a number of configurable parameters for IP settings. By default, mirage-seal will use DHCP to configure the network at boot. To set static IP information, use the --ip, --nm, and --gw arguments.

You'll find more thorough documentation by looking at mirage-seal --help or mirage-seal's README file.

To build a Xen unikernel, select the Xen mode with -t xen. In full, for a unikernel that will configure its network via DHCP:

mirage-seal --data=/home/me/coolwebsite/public --keys=/home/me/coolwebsite/secrets -t xen

mirage-seal will then generate a unikernel mir-seal.xen and a Xen configuration file seal.xl in the current working directory. To boot it and open the console (on a machine running Xen), invoke xl create on the configuration file with the -c option:

sudo xl create seal.xl -c

Via the console, we can see the sealed unikernel boot and obtain an IP through DHCP. Congratulations -- you made a static site unikernel browsable over HTTPS!


Reviewing the Bitcoin Pinata

By Hannes Mehnert

TL;DR: Nobody took our BTC. Random people from the Internet even donated into our BTC wallet. We showed the feasibility of a transparent self-service bounty. In the style of Dijkstra: security bounties can be a very effective way to show the presence of vulnerabilities, but they are hopelessly inadequate for showing their absence.

What are you talking about?

Earlier this year, we released a Bitcoin Piñata. The Piñata was a security bounty containing 10 BTC and it's been online since 10th February 2015. Upon successful mutual authentication, where the Piñata has only a single trust anchor, it sends the private key to the Bitcoin address.

It is open source, and exposes both the client and server side of ocaml-tls, running as an 8.2MB MirageOS unikernel. You can see the code manifest to find out which libraries are involved. We put this online and invited people to attack it.

Any approach was permitted in attacking the Piñata: the host system, the MirageOS TCP/IP stack, our TLS, X.509 and ASN.1 implementations, as well as the Piñata code. A successful attacker could do whatever they want with the BTC, no questions asked (though we would notice the transaction).

The exposed server could even be short-circuited to the exposed client: you could proxy a TLS connection in which the (encrypted!) secret was transmitted via your machine.

This post summarises what we've seen so far and what we've learned about attempts people have made to take the BTC.


There were 50,000 unique IP addresses who accessed the website. 1000 unique IP addresses initiated more than 20,000 TLS connections to the Piñata, trying to break it. Cumulative numbers of the HTTP and TLS accesses are shown in the diagram:

Cumulative Piñata accesses

There were more than 9000 failing and 12000 successful TLS sessions, comprised of short-circuits described earlier, and our own tests.

No X.509 certificate was presented in 1200 of the failed TLS connections. Another 1000 failed due to invalid input as the first bytes. This includes attempts using telnet — I'm looking at you, please give key (on 10th February at 16:00) and hi give me teh btcs (on 11th February at 05:57)!

We are not talking to everybody

Our implementation first parses the record version of a client hello, and if it fails, an unknown record version is reported. This happened in 10% of all TLS connections (including the 1000 with invalid input in the last section).

Another big class, 6%, were attempted Heartbeat packets (popular due to Heartbleed), which we do not implement.

Recently, issues in the state machines of TLS implementations were published in smacktls (and CCS injection). 3% of the Piñata connections received an unexpected handshake record at some point, which the Piñata handled correctly by shutting down the connection.

In 2009, the renegotiation attack on the TLS protocol was published, which allowed a person in the middle to inject prefix bytes, because a renegotiated handshake was not authenticated with data from the previous handshake. OCaml-TLS closes a connection if the renegotiation extension is not present, which happened in 2% of the connections. Another 2% did not propose a ciphersuite supported by OCaml-TLS; yet another 2% tried to talk SSL version 3 with us, which we do not implement (for good reasons, such as POODLE).

In various other (old versions of) TLS implementations, these connections would have been successful and insecure!

Attempts worth noting

Interesting failures were: 31 connections which sent too many or too few bytes, leading to parse errors.

TLS requires each communication partner who authenticates themselves to present a certificate. To prove ownership of the private key of the certificate, a hash of the concatenated handshake records needs to be signed and transmitted over the wire. 22 of our TLS traces had invalid signatures. Not verifying such signatures was the problem of Apple's famous goto fail.

Another 100 failure traces tested our X.509 validation: The majority of these failures (58) sent us certificates which were not signed by our trust anchor, such as CN=hacker/emailAddress=hacker@hacker and CN=Google Internal SNAX Authority and various Apple and Google IDs -- we're still trying to figure out what SNAX is, Systems Network Architecture maybe?

Several certificates contained invalid X.509 extensions: we require that a server certificate does not contain the BasicConstraints = true extension, which marks this certificate as certificate authority, allowing to sign other certificates. While not explicitly forbidden, best practices (e.g. from Mozilla) reject them. Any sensible systems administrator would not accept a CA as a server certificate.

Several other certificates were self-signed or contained an invalid signature: one certificate was our client certificate, but with a different RSA public key, thus the signature on the certificate was invalid; another one had a different RSA public key, and the signature was zeroed out.

Some certificates were not of X.509 version 3, or were expired. Several certificate chains were not pairwise signed, a common attack vector.

Two traces contained certificate structures which our ASN.1 parser rejected.

Another two connections (both initiated by ourselves) threw an exception which lead to shutdown of the connection: there was an out-of-bounds access while parsing handshake records. This did not lead to arbitrary code execution.


The BTC Piñata was the first transparent self-service bounty, and it was a success: people showed interest in the topic; some even donated BTC; we enjoyed setting it up and running it; we fixed a non-critical out of bounds access in our implementation; a large fraction of our stack has been covered by the recorded traces.

There are several points to improve a future Piñata: attestation that the code running is the open sourced code, attestation that the service owns the private key (maybe by doing transactions or signatures with input from any user).

There are several applications using OCaml-TLS, using MirageOS as well as Unix:

  • mirage-seal compiles to a unikernel container which serves a given directory over https;
  • tlstunnel is a (stud like) TLS proxy, forwarding to a backend server;
  • jackline is a (alpha version) terminal-based XMPP client;
  • conduit is an abstraction over network connections -- to make it use OCaml-TLS, set CONDUIT_TLS=native.

Again, a big thank you to IPredator for hosting our BTC Piñata and lending us the BTC!


MirageOS v2.5 with full TLS support

By Amir Chaudhry, Thomas Gazagnaire

Today we're announcing the new release of MirageOS v2.5, which includes first-class support for SSL/TLS in the MirageOS configuration language. We introduced the pure OCaml implementation of transport layer security (TLS) last summer and have been working since then to improve the integration and create a robust framework. The recent releases allow developers to easily build and deploy secure unikernel services and we've also incorporated numerous bug-fixes and major stability improvements (especially in the network stack). The full list of changes is available on the releases page and the breaking API changes now have their own page.

Over the coming week, we'll share more about the TLS stack by diving into the results of the Bitcoin Piñata, describing a new workflow for building secure static sites, and discussing insights on entropy in virtualised environments.

In the rest of this post, we'll cover why OCaml-TLS matters (and link to some tools), mention our new domain name, and mention our security advisory process.

Why OCaml-TLS matters

The last year has seen a slew of security flaws, which are even reaching the mainstream news. This history of flaws are often the result of implementation errors and stem from the underlying challenges of interpreting ambiguous specifications, the complexities of large APIs and code bases, and the use of unsafe programming practices. Re-engineering security-critical software allows the opportunity to use modern approaches to prevent these recurring issues. In a separate post, we cover some of the benefits of re-engineering TLS in OCaml.

TLS Unix Tools

To make it even easier to start benefiting from OCaml-TLS, we've also made a collection of TLS unix tools. These are designed to make it really easy to use a good portion of the stack without having to use Xen. For example, Unix tlstunnel is being used on If you have stunnel or stud in use somewhere, then replacing it with the tlstunnel binary is an easy way to try things out. Please do give this a go and send us feedback! ->

We've also switched our domain over to, which is a unikernel running the full stack. We've been discussing this transition for a while on our fortnightly calls and have actually been running this unikernel in parallel for a while. Setting things up this way has allowed us to stress test things in the wild and we've made big improvements to the networking stack as a result.

We now have end-to-end deployments for our secure-site unikernels, which is largely automated -- going from git push all the way to live site. You can get an idea of the workflows we have set up by looking over the following links:

Security disclosure process

Since we're incorporating more security features, it's important to consider the process of disclosing issues to us. Many bugs can be reported as usual on our issue tracker but if you think you've discovered a security vulnerability, the best way to inform us is described on a new page at

Get started!

As usual, MirageOS v2.5 and the its ever-growing collection of libraries is packaged with the OPAM package manager, so look over the installation instructions and run opam install mirage to get the command-line tool. To update from a previously installed version of MirageOS, simply use the normal workflow to upgrade your packages by using opam update -u (you should do this regularly to benefit from ongoing fixes). If you're looking for inspiration, you can check out the examples on mirage-skeleton or ask on the mailing list. Please do be aware that existing files using the conduit and http constructors might need to be updated -- we've made a page of backward incompatible changes to explain what you need to do.

We would love to hear your feedback on this release, either on our issue tracker or our mailing lists!


Why OCaml-TLS?

By Amir Chaudhry

TLS implementations have a history of security flaws, which are often the result of implementation errors. These security flaws stem from the underlying challenges of interpreting ambiguous specifications, the complexities of large APIs and code bases, and the use of unsafe programming practices.

Re-engineering security-critical software allows the opportunity to use modern approaches to prevent these recurring issues. Creating the TLS stack in OCaml offers a range of benefits, including:

Robust memory safety: Lack of memory safety was the largest single source of vulnerabilities in various TLS stacks throughout 2014, including Heartbleed (CVE-2014-0160). OCaml-TLS avoids this class of issues entirely due to OCaml's automatic memory management, safety guarantees and the use of a pure-functional programming style.

Improved certificate validation: Implementation errors in other stacks allowed validation to be skipped under certain conditions, leaving users exposed (e.g. CVE-2014-0092). In our TLS stack, we return errors explicitly as values and handle all possible variants. The OCaml toolchain and compile-time checks ensure that this has taken place.

Mitigation of state machine errors: Errors such as Apple's GoTo Fail (CVE-2014-1266) involved code being skipped and a default 'success' value being returned, even though signatures were never verified. Our approach encodes the state machine explicitly, while state transitions default to failure. The code structure also makes clear the need to consider preconditions.

Elimination of downgrade attacks: Legacy requirements forced other TLS stacks to incorporate weaker 'EXPORT' encryption ciphers. Despite the environment changing, this code still exists and leads to attacks such as FREAK (CVE-2015-0204) and Logjam (CVE-2015-4000). Our TLS server does not support weaker EXPORT cipher suites so was never vulnerable to such attacks. In addition our stack never supported SSLv3, which was known to be the cause of many vulnerabilities and is only now in the process of being deprecated (RFC: 7568).

Greatly reduced TCB: The size of the trusted computing base (TCB) of a system, measured in lines of code, is a widely accepted approximation of the size of its attack surface. Our secure Bitcoin Piñata, a unikernel built using our TLS stack, is less than 4% the size of an equivalent, traditional stack (102 kloc as opposed to 2560 kloc).

These are just some of the benefits of re-engineering critical software using modern techniques.


Smash the Bitcoin Pinata for fun and profit!

By Amir Chaudhry

Last summer we announced the beta release of a clean-slate implementation of TLS in pure OCaml, alongside a series of blog posts that described the libraries and the thinking behind them. It took two hackers six months — starting on the beach — to get the stack to that point and their demo server is still going strong. Since then, the team has continued working and recently presented at the 31st Chaos Communication Congress.

The authors are putting their stack to the test again and this time they've built a Bitcoin Piñata! Essentially, they've hidden a private key to a bitcoin address within a Unikernel running on Xen. If you're able to smash your way in, then you get to keep the spoils.

There's more context around this in my Piñata post and you can see the details on the site itself. Remember that the codebase is all open (as well as issues) so there's nothing to reverse engineer. Have fun!


MirageOS 2014 review: IPv6, TLS, Irmin, Jitsu and community growth

By Anil Madhavapeddy

This work funded in part by the EU FP7 User-Centric Networking project, Grant No. 611001.

An action-packed year has flown by for MirageOS, and it's time for a little recap of what's been happening and the plans for the new year. We announced MirageOS 1.0 just over a year ago, and 2014 also saw a major 2.0 summer release and the growth of a developer community that have been building support for IPv6, Transport Layer Security, on-demand spawning, profiling and much more. There have been 205 individual library releases, 25 presentations, and lots of online chatter through the year, so here follows a summary of our major activities recently.

Clean-Slate Transport Layer Security

David Kaloper and Hannes Mehnert started 2014 with getting interested in writing a safer and cleaner TLS stack in OCaml, and ended the year with a complete demonstration and talk last week in 31C3, the premier hacker conference! Their blog posts over the summer remain an excellent introduction to the new stack:

By summer, the stack was complete enough to connect to the majority of TLS 1.0+ sites on the Internet, and work progressed to integration with the remainder of the MirageOS libraries. By November, the Conduit network library had Unix support for both the OpenSSL/Lwt bindings and the pure OCaml stack, with the ability to dynamically select them. You can now deploy and test the pure OCaml TLS stack on a webserver simply by:

opam install lwt tls cohttp
export CONDUIT_TLS=native
cohttp-server-lwt -c <certfile> -p <port> <directory>

This will spin up an HTTPS server that serves the contents of <directory> to you over TLS. At the same time, we were also working on integrating the TLS stack into the Xen unikernel backend, so we could run completely standalone. This required some surgery:

  • The nocrypto crypto core is written in C, so we had to improve support for linking in external C libraries. Since the Xen unikernel is a single address-space custom kernel, we also need to be careful to compile it with the correct compilation flags or else risk subtle bugs. Thomas Leonard completely rearranged the MirageOS compilation pipeline to support separation compilation of C stubs, and we had the opportunity to remove lots of duplicated code within mirage-platform as a result of this work.
  • Meanwhile, the problem of gathering entropy in a virtual machine reared its head. We created a mirage-entropy device driver, and an active discussion ensued about how best to gather reliable randomness from Xen. Dave Scott built the best solution -- the xenentropyd that proxies entropy from dom0 to a unikernel VM.
  • David Kaloper also ported the nocrypto library to use the OCaml-Ctypes library, which increases the safety of the C bindings significantly. This is described in more detail in the "Modular foreign function bindings" blog post from the summer. This forms the basis for allowing Xen unikernels to communicate with C code, and integration with the MirageOS toolchain will continue to improve next year.

You can see Hannes and David present OCaml-TLS at CCC online. It's been a real pleasure watching their work develop in the last 12 months with such precision and attention to detail!

HTTP and JavaScript

Rudi Grinberg got sufficiently irked with the poor state of documentation for the CoHTTP library that he began gently contributing fixes towards the end of 2013, and rapidly became one of the maintainers. He also began improving the ecosystem around the web stack by building a HTTP routing layer, described in his blog posts:

Meanwhile, Andy Ray started developing HardCaml (a register transfer level hardware design system) in OCaml, and built the iocamljs interactive browser notebook. This uses js_of_ocaml to port the entire OCaml compilation toolstack to JavaScript, including ocamlfind, Lwt threading and dynamic loading support. The results are browsable online, and it is now easy to generate a JavaScript-driven interactive page for many MirageOS libraries.

An interesting side effect of Andy's patches were the addition of a JavaScript port to the CoHTTP library. For those not familiar with the innards, CoHTTP uses the OCaml module system to build a very portable HTTP implementation that can make mapped to different I/O models (Lwt or Async cooperative threading or POSIX blocking I/O), and to different operating systems (e.g. Unix or MirageOS). The JavaScript support mapped the high-level modules in CoHTTP to the XMLHTTPRequest native to JavaScript, allowing the same OCaml HTTP client code to run efficiently on Unix, Windows and now an IOCamlJS browser instance.

MirageOS uses a number of libraries developed by the Ocsigen team at IRILL in Paris, and so I was thrilled to deliver a talk there in December. Romain Calascibetta started integrating Ocsigen and MirageOS over the summer, and the inevitable plotting over beer in Paris lead Gabriel Radanne to kick off an effort to integrate the complete Ocsigen web stack into MirageOS. Head to ocsigen/ocsigenserver#54 if you're interested in seeing this happen in 2015! I also expect the JavaScript and MirageOS integration to continue to improve in 2015, thanks to large industrial users such as Facebook adopting js_of_ocaml in their open-source tools such as Hack and Flow.


We've wanted IPv6 support in MirageOS since its inception, and several people contributed to making this possible. At the start of the year, Hugo Heuzard and David Sheets got IPv6 parsing support into the ipaddr library (with me watching bemusedly at how insanely complex parsing is versus IPv4).

Meanwhile, Nicolas Ojeda Bar had been building OCaml networking libraries independently for some time, such as a IMAP client, Maildir handler, and a Bittorrent client. He became interested in the networking layer of MirageOS, and performed a comprehensive cleanup that resulted in a more modular stack that now supports both IPv4 and IPv6!

The addition of IPv6 support also forced us to consider how to simplify the configuration frontend to MirageOS unikernels that was originally written by Thomas Gazagnaire and described here by Mindy Preston. Nicolas has proposed a declarative extension to the configuration that allows applications to extend the mirage command-line more easily, thus unifying the "built-in" MirageOS compilation modes (such as choosing between Xen or Unix) and protocol-specific choices (such as configuring IPv4 and IPv6).

The new approach opens up the possibility of writing more user-friendly configuration frontends that can render them as a text- or web-based selectors, which is really important as more real-world uses of MirageOS are being created. It should be possible in 2015 to solve common problems such as web or DNS serving without having to write a single line of OCaml code.


One of the benefits touted by our CACM article on unikernels at the start of the year was the improved tooling from the static linking of an entire application stack with an operating system layer. Thomas Leonard joined the project this year after publishing a widely read blog series on his experiences from switching from Python to OCaml. Aside from leading (and upstreaming to Xen) the port of MirageOS to ARM, he also explored how to add profiling throughout the unikernel stack.

The support is now comprehensive and integrated into the MirageOS trees: the Lwt cooperative threading engine has hooks for thread switching, most of the core libraries register named events, traces are dumped into shared memory buffers in the CTF file format used by the Linux trace toolkit, and there are JavaScript and GTK+ GUI frontends that can parse them.

You can find the latest instructions on Tracing and Profiling on this website, and here are Thomas' original blog posts on the subject:


Thomas Gazagnaire spent most of the year furiously hacking away at the storage layer in Irmin, which is a clean-slate storage stack that uses a Git-like branching model as the basis for distributed unikernel storage. Irmin 0.9.0 was released in December with efficiency improvements and a sufficiently portable set of dependencies to make JavaScript compilation practical.

We also had the pleasure of Benjamin Farinier and Matthieu Journault join us as summer interns. Both of them did a great job improving the internals of Irmin, and Benjamin's work on Mergeable Persistent Datastructures will be presented at JFLA 2015.


Magnus Skjegstad returned to Cambridge and got interested in the rapid dynamic provisioning of unikernels. He built Jitsu, a DNS server that spawns unikernels in response to DNS requests and boots them in real-time with no perceptible lag to the end user. The longer term goal behind this is to enable a community cloud of ARM-based Cubieboard2 boards that serve user content without requiring centralised data centers, but with the ease-of-use of existing systems.

Building Jitsu and hitting our goal of extremely low latency management of unikernels required a huge amount of effort from across the MirageOS team.

  • Dave Scott and Jon Ludlam (two of the Xen maintainers at Citrix) improved the Xen xl toolstack to deserialise the VM startup chain to shave 100s of milliseconds off every operation.
  • Thomas Leonard drove the removal of our forked Xen MiniOS with a library version that is being fed upstream (including ARM support). This made the delta between Xen and MirageOS much smaller and therefore made reducing end-to-end latency tractable.
  • David Sheets built a test harness to boot unikernel services and measure their latency under very different conditions, including contrasting boot timer versus Docker containers. In many instances, we ended up booting faster than containers due to not touching disk at all with a standalone unikernel. Ian Leslie built us some custom power measurement hardware that came in handy to figure out how to drive down the energy cost of unikernels running on ARM boards.
  • Thomas Gazagnaire, Balraj Singh, Magnus Skjegstad built the synjitsu proxy server that intercepts and proxies TCP connections to mask the couple of 100 milliseconds during unikernel boot time, ensuring that no TCP connections ever require retransmission from the client.
  • Dave Scott and I built out the vchan shared memory transport that supports low-latency communiction between unikernels and/or Unix processes. This is rapidly heading into a Plan9-like model, with the additional twist of using Git instead of a flat filesystem hierarchy as its coordination basis.
  • Amir Chaudhry and Richard Mortier documented the Git-based (and eventually Irmin-based) workflow behind managing the unikernels themselves, so that they can easily be deployed to distance ARM devices simply by running git pull. You can read more about this in his From Jekyll to Unikernels post.

All of this work was hastily crammed into a USENIX NSDI 2015 paper that got submitted at 4am on a bright autumn morning. Here is the published paper, and we're planning a blog post describing how you can deploy this infrastructure for yourself.


All of the above work was only possible due to the vastly improved tooling and infrastructure around the project. Our community manager Amir Chaudhry led the minuted calls every two weeks that tied the efforts together, and we established some pioneer projects for newcomers to tackle.

The OPAM package manager continued to be the frontend for all MirageOS tools, with releases of libraries happening regularly. Because of the modular nature of MirageOS code, most of the libraries can also be used as normal Unix-based libraries, meaning that we aren't just limited to MirageOS users but can benefit from the entire OCaml community. The graph to the right shows the growth of the total package database since the project started to give you a sense of how much activity there is.

The major OPAM 1.2 also added a number of new features that made MirageOS code easier to develop, including a Git-based library pinning workflow that works superbly with GitHub, and easier Travis integration for continuous integration. Nik Sultana also improved the is-mirage-broken to give us a cron-driven prod if a library update caused an end-to-end failure in building the MirageOS website or other self-hosted infrastructure.

Our favourite random idiot, Mindy Preston, wrote up a superb blog series about her experiences in the spring of 2014 with moving her homepage to be hosted on MirageOS. This was followed up by Thomas Leonard, Phil Tomson, Ian Wilkinson, Toby Moore, and many others that we've tried to record in our link log. We really appreciate the hundreds of bug reports filed by users and folk trying out MirageOS; by taking the trouble to do this, you've helped us refine and polish the frontend. One challenge for 2015 that we could use help on is to pull together many of these distributed blogged instructions and merge them back into the main documentation (get in touch if interested!).

OCaml has come a long way in the last year in terms of tooling, and another task my research group OCaml Labs works on at Cambridge is the development of the OCaml Platform. I'll be blogging separately about our OCaml-specific activities in a few days, but all of this work has a direct impact on MirageOS itself since it lets us establish a local feedback loop between MirageOS and OCaml developers to rapidly iterate on large-scale development. The regular OCaml compiler hacking sessions organised by Jeremy Yallop and Leo White have been a great success this year, with a wide variety of people from academic (Cambridge, London universities and Microsoft Research) and industrial (Jane Street, Citrix and Facebook among others) and locally interested folk. One very important project that has had a lot of work put into it in 2014 (but isn't quite ready for a public release yet) is Assemblage, which will remove much of the boilerplate currently needed to build and release an OCaml library to OPAM.

We also had a great time working with open-source summer programs. Thanks to the Xen Foundation and GNOME for their support here, and we hope to do this again next summer! The roundup posts were:

Upcoming features

So what's coming up for our unikernels in 2015? Our focus heading into the new year is very much on improving the ease-of-use and deployability of MirageOS and fleshing out the feature set for the early adopters such as the XAPI project, Galois, and the Nymote personal data project. Here are some of the highlights:

  • Dust Clouds: The work on Jitsu is leading to the construction of what we term "dust clouds": on-demand scaling of unikernel services within milliseconds of requests coming in, terminated right beside the user on local ARM devices. The model supports existing clouds as well, and so we are improving support for cloud APIs such via Jyotsna Prakash's EC2 bindings, XenAPI, and (volunteers needed) OpenStack support. If you're interested in tracking this work, head over to the Nymote site for updates.

  • Portability: Beyond Xen, there are several efforts afoot to port MirageOS to bare metal targets. One promising effort is to use Rump Kernels as the boot infrastructure and MirageOS as the application stack. We hope to have a Raspberry Pi and other ARM targets fairly soon. Meanwhile at the end of the spectrum is mobile computing, which was part of the original multiscale vision for starting the project. The JavaScript, iOS and Android ports are all progressing (mainly thanks to community contributions around OCaml support for this space, such as Jeff Psellos' hard work on OCaml-IOS).

  • Protocol Development: There are a huge number of protocols being developed independently, and more are always welcome. Luke Dunstan is hacking on multicast DNS support, we have an IMAP client and server, Dominic Price has built a series of social network APIs for Facebook or Tumblr, and Masoud Koleini has been extending Haris Rotsos' work to achieve a line-rate and type-safe OpenFlow switch and controller based on the Frenetic project. Hannes is also developing Jackline, which uses his MirageOS to assemble a trustworthy communication client. Daniel Buenzli also continues to release a growing set of high-quality, modular libraries that we depend on throughout MirageOS.

  • Storage: All storage services from the unikernels will be Git-based (e.g. logging, command-and-control, key-value retrieval). Expect to see Xen toolstack extensions that make this support seamless, so a single Linux VM will be able to control a large army of unikernels via persistent data structures.

Want to get involved?

This is a really fun time to get involved with unikernels and the MirageOS project. The year of 2014 has seen lots of discussion about the potential of unikernels and we'll see some of the first big deployments involving them in 2015. For the ones among you who wish to learn more, then check out the pioneer projects, watch out for Amir's meeting notes and join the voice calls if you want a more interactive discussion, and engage on the mailing lists with any questions you might have.

For me personally, it's been a real privilege to spend the year working with and learning from the friendly, intelligent and diverse community that is springing up around the project. The progression from experiment to reality has been a lot of work, but the unikernel dream is finally coming together rath[er nicely thanks to everyone's hard work and enthusiasm. I'd also like to thank all of our funding bodies and the Linux Foundation and the Xen Project (especially Lars Kurth and Russell Pavlicek) for their support throughout the year that made all this work possible. Happy new year, everyone!


MirageOS v2.0: a recap of the new features

By Anil Madhavapeddy

This work funded in part by the EU FP7 User-Centric Networking project, Grant No. 611001.

The first release of MirageOS back in December 2013 introduced the prototype of the unikernel concept, which realised the promise of a safe, flexible mechanism to build highly optimized software stacks purpose-built for deployment in the public cloud (more background on this). Since then, we've been hard at work using and extending MirageOS for real projects and the community has been steadily growing.

We're thrilled to announce the release of MirageOS v2.0 today! Over the past few weeks the team has been hard at work blogging about all the new features in this latest release, coordinated by the tireless Amir Chaudhry:

All the libraries required for these new features are regularly released into the OPAM package manager, so just follow the installation instructions to give them a spin. A release this size probably introduces minor hiccups that may cause build failures, so we very much encourage bug reports on our issue tracker or questions to our mailing lists. Don't be shy: no question is too basic, and we'd love to hear of any weird and wacky uses you put this new release to! And finally, the lifeblood of MirageOS is about sharing and publishing libraries that add new functionality to the framework, so do get involved and open-source your own efforts.

Breaking news: Richard Mortier and I will be speaking at OSCON this week on Thursday morning about the new features in F150 in the Cloud Track. Come along if you are in rainy Portland at the moment!


Building an ARMy of Xen unikernels

By Thomas Leonard

Mirage has just gained the ability to compile unikernels for the Xen/arm32 platform, allowing Mirage guests to run under the Xen hypervisor on ARM devices such as the Cubieboard 2 and CubieTruck.


The ARMv7 architecture introduced the (optional) Virtualization Extensions, providing hardware support for running virtual machines on ARM devices, and Xen's ARM Hypervisor uses this to support hardware accelerated ARM guests.

Mini-OS is a tiny OS kernel designed specifically for running under Xen. It provides code to initialise the CPU, display messages on the console, allocate memory (malloc), and not much else. It is used as the low-level core of Mirage's Xen implementation.

Mirage v1 was built on an old version of Mini-OS which didn't support ARM. For Mirage v2, we have added ARM support to the current Mini-OS (completing Karim Allah Ahmed's initial ARM port) and made Mirage depend on it as an external library. This means that Mirage will automatically gain support for other architectures that get added later. We are currently working with the Xen developers to get our Mini-OS fork upstreamed.

In a similar way, we have replaced Mirage v1's bundled maths library with a dependency on the external OpenLibm, which we also extended with ARM support (this was just a case of fixing the build system; the code is from FreeBSD's libm, which already supported ARM).

Mirage v1 also bundled dietlibc to provide its standard C library. A nice side-effect of this work came when we were trying to separate out the dietlibc headers from the old Mini-OS headers in Mirage. These had rather grown together over time and the work was proving difficult, until we discovered that we no longer needed a libc at all, as almost everything that used it had been replaced with pure OCaml versions! The only exception was the printf code for formatting floating point numbers, which OCaml uses in its printf implementation. We replaced that by taking the small fmt_fp function from musl libc.

Here's the final diffstat of the changes to mirage-platform adding ARM support:

778 files changed, 1949 insertions(+), 59689 deletions(-)

Trying it out

You'll need an ARM device with the Virtualization Extensions. I've been testing using the Cubieboard 2 (and CubieTruck):


The first step is to install Xen. Running Xen on the Cubieboard2 documents the manual installation process, but you can now also use mirage/xen-arm-builder to build an SDcard image automatically. Copy the image to the SDcard, connect the network cable and power, and the board will boot Xen.

Once booted you can ssh to Dom0, the privileged Linux domain used to manage the system, install Mirage, and build your unikernel just as on x86. Currently, you need to select the Git versions of some components. The following commands will install the necessary versions if you're using the xen-arm-builder image:

$ opam init
$ opam install mirage-xen-minios
$ opam remote add mirage-dev git://
$ opam install mirage

Technical details

One of the pleasures of unikernels is that you can comprehend the whole system with relatively little effort, and those wishing to understand, debug or contribute to the ARM support may find the following technical sections interesting. However, you don't need to know the details of the ARM port to use it, as Mirage abstracts away the details of the underlying platform.

The boot process

An ARM Mirage unikernel uses the Linux zImage format, though it is not actually compressed. Xen will allocate some RAM for the image and load the kernel at the offset 0x8000 (32 KB).

Execution begins in arm32.S, with the r2 register pointing to a Flattened Device Tree (FDT) describing details of the virtual system. This assembler code performs a few basic boot tasks:

  1. Configuring the MMU, which maps virtual addresses to physical addresses (see next section).
  2. Turning on caching and branch prediction.
  3. Setting up the exception vector table (this says how to handle interrupts and deal with various faults, such as reading from an invalid address).
  4. Setting up the stack pointer and calling the C function arch_init.

arch_init makes some calls to the hypervisor to set up support for the console and interrupt controller, and then calls start_kernel.

start_kernel (in libxencaml) sets up a few more features (events, malloc, time-keeping and grant tables), then calls caml_startup.

caml_startup (in libocaml) initialises the garbage collector and calls caml_program, which is your application's

The address space

With the Virtualization Extensions, there are two stages to converting a virtual memory address (used by application code) to a physical address in RAM. The first stage is under the control of the guest VM, mapping the virtual address to what the guest believes is the physical address (this address is referred to as the Intermediate Physical Address or IPA). The second stage, under the control of Xen, maps the IPA to the real physical address. The tables holding these mappings are called translation tables.

Mirage's memory needs are simple: most of the RAM should be used for the garbage-collected OCaml heap, with a few pages used for interacting with Xen (these don't go on the OCaml heap because they must be page aligned and must not move around).

Xen does not commit to using a fixed address as the IPA of the RAM, but the C code needs to run from a known location. To solve this problem the assembler code in arm32.S detects where it is running from and sets up a virtual-to-physical mapping that will make it appear at the expected location, by adding a fixed offset to each virtual address. For example, on Xen/unstable, we configure the beginning of the virtual address space to look like this (on Xen 4.4, the physical addresses would start at 80000000 instead):

Virtual addressPhysical address (IPA)Purpose
40000040000000Stack (16 KB)
40400040004000Translation tables (16 KB)
40800040008000Kernel image

The physical address is always at a fixed offset from the virtual address and the addresses wrap around, so virtual address c0400000 maps back to physical address 0 (in this example).

The stack, which grows downwards, is placed at the start of RAM so that a stack overflow will trigger a fault rather than overwriting other data.

The 16 KB translation table is an array of 4-byte entries each mapping 1 MB of the virtual address space, so the 16 KB table is able to map the entire 32-bit address space (4 GB). Each entry can either give the physical section address directly (which is what we do) or point to a second-level table mapping individual 4 KB pages. By using only the top-level table we reduce possible delays due to TLB misses.

After the kernel code comes the data (constants and global variables), then the bss section (data that is initially zero, and therefore doesn't need to be stored in the kernel image), and finally the rest of the RAM, which is handed over to the malloc system.


The current version seems to be working well on Xen 4.4 (stable) and the 4.5 development version, but has only been lightly tested. If you have any problems or questions, or get it working on other devices, please let us know!


Using Irmin to add fault-tolerance to the Xenstore database

By Dave Scott

This is the second in a series of posts that introduces the Irmin distributed storage engine. You might like to begin with the introductory post.

Xenstore is a critical service found on all hosts running Xen. Xenstore is necessary to

  • configure all VM I/O devices such as disk controllers and network interface cards;
  • share performance statistics and OS version information; and
  • signal VMs during shutdown, suspend, resume, migrate etc.

Xenstore must be reliable: if it fails then the host is unmanageable and must be rebooted.

Xenstore must be secure: if it is compromised by a VM then that VM can access data belonging to other VMs.

The current version of Xenstore is already written in OCaml and documented in the paper OXenstored: an efficient hierarchical and transactional database using functional programming with reference cell comparisons presented at ICFP 2009. The existing code works very reliably, but there is always room for improvement for debuggability of such a complex system component. This is where Irmin, the storage layer of Mirage 2.0, can help.

But first, a quick Xenstore primer:

Xen and Xenstore in 30 seconds

The Xen hypervisor focuses on isolating VMs from each-other; the hypervisor provides a virtual CPU scheduler and a memory allocator but does not perform I/O on behalf of guest VMs. On a Xen host, privileged server VMs perform I/O on behalf of client VMs. The configuration for calculating which server VM services requests for which client VMs is stored in Xenstore, as key/value pairs.

The following diagram shows a Xen host with a single client and server VM, with a single virtual device in operation. Disk blocks and network packets flow via shared memory between Xen-aware drivers in the VMs, shown in the lower-half. The control-plane, shown in the upper-half, contains the metadata about the datapath: how the device should appear in the client VM; where the I/O should go in the server VM; where the shared memory control structures are etc.

Device configuration is stored in Xenstore as key=value pairs.

The Xenstore device attach protocol insists that all device keys are added through atomic transactions, i.e. partial updates are never visible to clients and transactions cannot interfere with each other. A Xenstore server must abort transactions whose operations were not successfully isolated from other transactions. After an abort, the client is expected to retry. Each key=value write is communicated to the server as a single request/response, so transactions comprising multiple writes are open for multiple round-trip times. This protocol is baked into guest VM kernels (including Linux, FreeBSD, Mirage, ...) and won't change anytime soon.

Xenstore is used heavily when lots of VMs are starting in parallel. Each VM typically has several devices, each of these devices is added in a parallel transaction and therefore many transactions are open at once. If the server aborts too many of these transactions, causing the clients to retry, the system will make little progress and may appear to live-lock. The challenge for a Xenstore implementation is to minimise the number of aborted transactions and retries, without compromising on the isolation guarantee.

Irmin Xenstore design goals

The design goals of the Irmin-based Mirage Xenstore server are:

  1. safely restart after a crash;
  2. make system debugging easy; and
  3. go really fast!

How does Irmin help achieve these goals?

Restarting after crashes

The Xenstore service is a reliable component and very rarely crashes. However, if a crash does occur, the impact is severe on currently running virtual machines. There is no protocol for a running VM to close its connection to a Xenstore and open a new one, so if Xenstore crashes then running VMs are simply left orphaned. VMs in this state are impossible to manage properly: there is no way to shut them down cleanly, to suspend/resume or migrate, or to configure any disk or network interfaces. If Xenstore crashes, the host must be rebooted shortly after.

Irmin helps make Xenstore recoverable after a crash, by providing a library that applications can use to persist and synchronise distributed data structures on disk and in memory. By using Irmin to persist all our state somewhere sensible and taking care to manage our I/O carefully, then the server process becomes stateless and can be restarted at will.

To make Xenstore use Irmin, the first task is to enumerate all the different kinds of state in the running process. This includes the obvious key-value pairs used for VM configuration as well as data currently hidden away in the OCaml heap: the addresses in memory of established communication rings, per-domain quotas, pending watch events and watch registrations etc etc. Once the state has been enumerated it must be mapped onto key-value pairs which can be stored in Irmin. Rather than using ad-hoc mappings everywhere, the Mirage Irmin server has persistent Maps, persistent Sets, persistent Queues and persistent reference cells.

Irmin applications are naturally written as functors, with the details of the persistence kept abstract. The following Irmin-inspired signature represents what Xenstore needs from Irmin:

module type VIEW = sig
  type t

  val create: unit -> t Lwt.t
  (** Create a fresh VIEW from the current state of the store.
      A VIEW tracks state queries and updates and acts like a branch
      which has an explicit [merge]. *)

  val read: t -> Protocol.Path.t -> 
    [ `Ok of Node.contents | `Enoent of Protocol.Path.t ] Lwt.t
  (** Read a single key *)

  val list: t -> Protocol.Path.t -> 
    [ `Ok of string list | `Enoent of Protocol.Path.t ] Lwt.t
  (** List all the children of a key *)

  val write: t -> Protocol.Path.t -> Node.contents -> 
    [ `Ok of unit ] Lwt.t
  (** Update a single key *)

  val mem: t -> Protocol.Path.t -> bool Lwt.t
  (** Check whether a key exists *)

  val rm: t -> Protocol.Path.t -> [ `Ok of unit ] Lwt.t
  (** Remove a key *)

  val merge: t -> string -> bool Lwt.t
  (** Merge this VIEW into the current state of the store *)

The main 'business logic' of Xenstore can then be functorised over this signature relatively easily. All we need is to instantiate the functor using Irmin to persist the data somewhere sensible. Eventually we will need two instantiations: one which runs as a userspace application and which writes to the filesystem; and a second which will run as a native Xen kernel (known as a xenstore stub domain) and which will write to a fixed memory region (like a ramdisk). The choice of which to use is left to the system administrator. Currently most (if not all) distribution packagers choose to run Xenstore in userspace. Administrators who wish to further secure their hosts are encouraged to run the kernelspace version to isolate Xenstore from other processes (where a VM offers more isolation than a container, which offers more isolation than a chroot). Note this choice is invisible to the guest VMs.

So far in the Irmin Xenstore integration only the userspace instantiation has been implemented. One of the most significant user-visible features is that all of the operations done through Irmin can be inspected using the standard git command line tool. The runes to configure Irmin to write git format data to the filesystem are as follows:

    let open Irmin_unix in
    let module Git = IrminGit.FS(struct
      let root = Some filename
      let bare = true
    end) in
    let module DB = Git.Make(IrminKey.SHA1)(IrminContents.String)(IrminTag.String) in
    DB.create () >>= fun db ->

where keys and values will be mapped into OCaml strings, and our VIEW.t is simply an Irmin DB.View.t. All that remains is to implement read, list, write, rm by

  1. mapping Xenstore Protocol.Path.t values onto Irmin keys; and
  2. mapping Xenstore Node.contents records onto Irmin values.

As it happens Xenstore and Irmin have similar notions of "paths" so the first mapping is easy. We currently use sexplib to map Node.contents values onto strings for Irmin.

The resulting Irmin glue module looks like:

    let module V = struct
      type t = DB.View.t
      let create = DB.View.create
      let write t path contents =
        DB.View.update t (value_of_filename path) (Sexp.to_string (Node.sexp_of_contents contents))
      (* omit read,list,write,rm for brevity *)
      let merge t origin =
        let origin = IrminOrigin.create "%s" origin in
        DB.View.merge_path ~origin db [] t >>= function
        | `Ok () -> return true
        | `Conflict msg ->
          info "Conflict while merging database view: %s" msg;
          return false
    end in

The write function simply calls through to Irmin's update function, while the merge function calls Irmin's merge_path. If Irmin cannot merge the transaction then our merge function will return false and this will be signalled to the client, which is expected to retry the high-level operation (e.g. hotplugging or unplugging a device).

Now all that remains is to carefully adjust the I/O code so that effects (reading and writing packets along the persistent connections) are interleaved properly with persisted state changes and voilà, we now have a xenstore which can recover after a restart.

Easy system debugging with Git

When something goes wrong on a Xen system it's standard procedure to

  1. take a snapshot of the current state of Xenstore; and
  2. examine the log files for signs of trouble.

Unfortunately by the time this is done, interesting Xenstore state has usually been deleted. Unfortunately the first task of the human operator is to evaluate by-hand the logged actions in reverse to figure out what the state actually was when the problem happened. Obviously this is tedious, error-prone and not always possible since the log statements are ad-hoc and don't always include the data you need to know.

In the new Irmin-powered Xenstore the history is preserved in a git-format repository, and can be explored using your favourite git viewing tool. Each store update has a compact one-line summary, a more verbose multi-line explanation and (of course) the full state change is available on demand.

For example you can view the history in a highly-summarised form with:

$ git log --pretty=oneline --abbrev-commit --graph
* 2578013 Closing connection -1 to domain 0
* d4728ba Domain 0: rm /bench/local/domain/0/backend/vbd/10 = ()
* 4b55c99 Domain 0: directory /bench/local/domain/0/backend = [ vbd ]
* a71a903 Domain 0: rm /bench/local/domain/10 = ()
* f267b31 Domain 0: rm /bench/vss/uuid-10 = ()
* 94df8ce Domain 0: rm /bench/vm/uuid-10 = ()
* 0abe6b0 Domain 0: directory /bench/vm/uuid-10/domains = [  ]
* 06ddd3b Domain 0: rm /bench/vm/uuid-10/domains/10 = ()
* 1be2633 Domain 0: read /bench/local/domain/10/vss = /bench/vss/uuid-10
* 237a8e4 Domain 0: read /bench/local/domain/10/vm = /bench/vm/uuid-10
* 49d70f6 Domain 0: directory /bench/local/domain/10/device = [  ]
*   ebf4935 Merge view to /
| * e9afd9f Domain 0: read /bench/local/domain/10 =
* | c4e0fa6 Domain 0: merging transaction 375

The summarised form shows both individual operations as well as isolated transactions which are represented as git branches. You can then 'zoom in' and show the exact state change with commands like:

$ git show bd44e03
commit bd44e0388696380cafd048eac49474f68d41bd3a
Author: 448 <>
Date:   Thu Jan 1 00:09:26 1970 +0000

    Domain 0: merging transaction 363

diff --git a/*0/bench.dir/local.dir/domain.dir/7.dir/control.dir/shutdown.value b/*0/bench.dir/local.dir/domain.dir/7.dir/control.dir/shutdown.value
new file mode 100644
index 0000000..aa38106
--- /dev/null
+++ b/*0/bench.dir/local.dir/domain.dir/7.dir/control.dir/shutdown.value
@@ -0,0 +1 @@
+((creator 0)(perms((owner 7)(other NONE)(acl())))(value halt))

Last but not least, you can git checkout to the exact time the problem occurred and examine the state of the store.

Going really fast

Xenstore is part of the control-plane of a Xen system and is most heavily stressed when lots of VMs are being started in parallel. Each VM has multiple devices and each device is added in a separate transaction. These transactions remain open for multiple client-server round-trips, as each individual operation is sent to Xenstore as a separate RPC. To provide isolation, each Xenstore transaction is represented by an Irmin VIEW.t which is persisted on disk as a git branch. When starting lots of VMs in parallel, lots of branches are created and must be merged back together. If a branch cannot be merged then an abort signal is sent to the client and it must retry.

Earlier versions of Xenstore had naive transaction merging algorithms which aborted many of these transactions, causing the clients to re-issue them.This led to a live-lock where clients were constantly reissuing the same transactions again and again.

Happily Irmin's default merging strategy is much better: by default Irmin records the results of every operation and replays the operations on merge (similar to git rebase). Irmin will only generate a Conflict and signal an abort if the client would now see different results to those it has already received (imagine reading a key twice within an isolated transaction and seeing two different values). In the case of parallel VM starts, the keys are disjoint by construction so all transactions are merged trivially; clients never receive abort signals; and therefore the system makes steady, predictable progress starting the VMs.

Trying it out

The Irmin Xenstore is under active development but you can try it by:

Install basic development tools along with the xen headers and xenstore tools (NB you don't actually have to run Xen):

  sudo apt-get install libxen-dev xenstore-utils opam build-essential m4

Initialise opam (if you haven't already). Make sure you have OCaml 4.01:

  opam init
  opam update
  opam switch 4.01.0

Install the OCaml build dependencies:

  opam install lwt irmin git sexplib cstruct uri sexplib cmdliner xen-evtchn shared-memory-ring io-page ounit

Clone the code and build it:

  git clone git://
  cd ocaml-xenstore-server

Run a server (as a regular user):

  ./main.native --database /tmp/db --enable-unix --path /tmp/xenstored

In a separate terminal, perform some operations:

  export XENSTORED_PATH=/tmp/xenstored
  xenstore-write -s /one/two/three 4 /five/six/seven 8
  xenstore-ls -s /

Next check out the git repo generated by Irmin:

  cd /tmp/db
  git log

Comments and/or contributions are welcome: join the Mirage email list and say hi!


Introducing Irmin: Git-like distributed, branchable storage

By Thomas Gazagnaire

This is the first post in a series which will describe Irmin, the new Git-like storage layer for Mirage OS 2.0. This post gives a high-level description on Irmin and its overall architecture, and later posts will detail how to use Irmin in real systems.

Irmin is a library to persist and synchronize distributed data structures both on-disk and in-memory. It enables a style of programming very similar to the Git workflow, where distributed nodes fork, fetch, merge and push data between each other. The general idea is that you want every active node to get a local (partial) copy of a global database and always be very explicit about how and when data is shared and migrated.

Irmin is not, strictly speaking, a full database engine. It is, as are all other components of Mirage OS, a collection of libraries designed to solve different flavours of the challenges raised by the CAP theorem. Each application can select the right combination of libraries to solve its particular distributed problem. More precisely, Irmin consists of a core of well-defined low-level data structures that specify how data should be persisted and be shared across nodes. It defines algorithms for efficient synchronization of those distributed low-level constructs. It also builds a collection of higher-level data structures, like persistent mergeable queues, that can be used by developers without having to know precisely how Irmin works underneath.

Since it's a part of Mirage OS, Irmin does not make strong assumptions about the OS environment that it runs in. This makes the system very portable, and the details below hold for in-memory databases as well as for slower persistent serialization such as SSDs, hard drives, web browser local storage, or even the Git file format.

Persistent Data Structures

Persistent data structures are well known and used pervasively in many different areas. The programming language community has investigated the concepts widely (and this is not limited to functional programming), and in the meantime, the systems community experimented with various persistent strategies such as copy-on-write filesystems. In most of these systems, the main concern is how to optimize the space complexity by maximizing the sharing of immutable sub-structures.

The Irmin design ideas share roots with previous works on persistent data structures, as it provides an efficient way to fork data structures, but it also explores new strategies and mechanisms to be able to efficiently merge back these forked structures. This offers programming constructs very similar to the Git workflow.

Irmin focuses on two main aspects:

  • Semantics: what properties the resulting merged objects should verify.

  • Complexity: how to design efficient merge and synchronization primitives, taking advantage of the immutable nature of the underlying objects.

Although it is pervasively used, data persistence has a very broad and fuzzy meaning. In this blog post, I will refer to data persistence as a way for:

  • a single process to lazily populate a process memory on startup. You need this when you want the process to be able to resume while holding part of its previous state if it crashes

  • concurrent processes to share references between objects living in a global pool of data. Sharing references, as opposed to sharing values, reduces memory copies and allow different processes to concurrently update a shared store.

In both cases, you need a global pool of data (the Irmin block store) and a way to name values in that pool (the Irmin tag store).

The Block Store: a Virtual Heap

Even high-level data structures need to be allocated in memory, and it is the purpose of the runtime to map such high-level constructs into low-level memory graph blocks. One of the strengths of OCaml is the very simple and deterministic mapping from high-level data structures to low-level block representations (the heap): see for instance, the excellent series of blog posts on OCaml internals by Richard W. Jones, or Chapter 20: Memory Representation of Values in Real World OCaml.

An Irmin block store can be seen as a virtual OCaml heap that uses a more abstract way of connecting heap blocks. Instead of using the concrete physical memory addresses of blocks, Irmin uses the hash of the block contents as an address. As for any content-addressable storage, this gives Irmin block stores a lot of nice properties and greatly simplifies the way distributed stores can be synchronized.

Persistent data structures are immutable, and once a block is created in the block store, its contents will never change again. Updating an immutable data structure means returning a completely new structure, while trying to share common sub-parts to avoid the cost of making new allocations as much as possible. For instance, modifying a value in a persistent tree means creating a chain of new blocks, from the root of the tree to the modified leaf. For convenience, Irmin only considers acyclic block graphs -- it is difficult in a non-lazy pure language to generate complex cyclic values with reasonable space usage.

Conceptually, an Irmin block store has the following signature:

type t
(** The type for Irmin block store. *)

type key
(** The type for Irmin pointers *)

type value = ...
(** The type for Irmin blocks *)

val read: t -> key -> value option
(** [read t k] is the block stored at the location [k] of the
store. It is [None] if no block is available at that location. *)

val add: t -> key -> value -> t
(** [add t k v] is the *new* store storing the block [v] at the
location [k]. *)

Persistent data structures are very efficient to store in memory and on disk as you do not need write barriers, and updates can be written sequentially instead of requiring random access into the data structure.

The Tag Store: Controlled Mutability and Concurrency

So far, we have only discussed purely functional data structures, where updating a structure means returning a pointer to a new structure in the heap that shares most of its contents with the previous one. This style of programming is appealing when implementing complex protocols as it leads to better compositional properties.

Irmin Stores

However, this makes sharing information between processes much more difficult, as you need a way to "inject" the state of one structure into another process's memory. In order to do so, Irmin borrows the concept of branches from Git by relating every operation to a branch name, and modifying the tip of the branch if it has side-effects. The Irmin tag store is the only mutable part of the whole system and is responsible for mapping some global (branch) names to blocks in the block store. These tag names can then be used to pass block references between different processes.

A block store and a tag store can be combined to build a higher-level store (the Irmin store) with fine concurrency control and atomicity guarantees. As mutation happens only in the tag store, we can ensure that as long a given tag is not updated, no change made in the block store will be visible by anyone. This also gives a nice story for concurrency: as in Git, creating a concurrent view of the store is the straightforward operation of creating a new tag that denotes a new branch. All concurrent operations can then happen on different branches:

type t
(** The type for Irmin store. *)

type tag
(** Mutable tags *)

type key = ...
(** The type for user-defined keys (for instance a list of strings) *)

type value = ...
(** The type for user-defined values *)

val read: t -> ?branch:tag -> key -> value option
(** [read t ?branch k] reads the contents of the key [k] in the branch
[branch] of the store [t]. If no branch is specified, then use the
["HEAD"] one. *)

val update: t -> ?branch:tag -> key -> value -> unit
(** [update t ?branch k v] *updates* the branch [branch] of the store
[t] the association of the key [key] to the value [value]. *)

Interactions between concurrent processes are completely explicit and need to happen via synchronization points and merge events (more on this below). It is also possible to emulate the behaviour of transactions by recording the sequence of operations (read and update) on a given branch -- that sequence is used before a merge to check that all the operations are valid (i.e. that all reads in the transaction still return the same result on the current tip of the store) and it can be discarded after the merge takes place.

Merging Data Structures

To merge two data structures in a consistent way, one has to compute the sequence of operations which leads, from an initial common state, to two diverging states (the ones that you want to merge). Once these two sequences of operations have been found, they must be combined (if possible) in a sensible way and then applied again back on the initial state, in order to get the new merged state. This mechanism sounds nice, but in practice it has two major drawbacks:

  • It does not specify how we find the initial state from two diverging states -- this is generally not possible (think of diverging counters); and
  • It means we need to compute the sequence of update operations that leads from one state to an other. This is easier than finding the common initial state between two branches, but is still generally not very efficient.

In Irmin, we solve these problems using two mechanisms.

First of all, an interesting observation is that that we can model the sequence of store tips as a purely functional data-structure. We model the partial order of tips as a directed acyclic graph where nodes are the tips, and there is an edge between two tips if either (i) one is the result of applying a sequence of updates to the other, or (ii) one is the result of a merge operation between the other and some other tips. Practically speaking, that means that every tip should contains the list of its predecessors as well as the actual data it associated to. As it is purely functional, we can (and we do) store that graph in an Irmin block store.

Finding a common ancestor

Having a persistent and immutable history is good for various obvious reasons, such as access to a forensics if an error occurs or snapshot and rollback features for free. But another less obvious useful property is that we can now find the greatest common ancestors of two data structures without an expensive global search.

The second mechanism is that we require the data structures used in Irmin to be equipped with a well-defined 3-way merge operation, which takes two diverging states, the corresponding initial state (computed using the previous mechanism) and that return either a new state or a conflict (similar to the EAGAIN exception that you get when you try to commit a conflicting transaction in more traditional transactional databases). Having access to the common ancestors makes a great difference when designing new merge functions, as usually no modification is required to the data-structure itself. In contrast, the conventional approach is more invasive as it requires the data structure to carry more information about the operation history (for instance conflict-free replicated datatypes, which relies on unbounded vector clocks).

We have thus been designing interesting data structure equipped with a 3-way merge, such as counters, queues and ropes.

This is what the implementation of distributed and mergeable counters looks like:

type t = int
(** distributed counters are just normal integers! *)

let merge ~old t1 t2 = old + (t1-old) + (t2-old)
(** Merging counters means:
   - computing the increments of the two states [t1] and [t2]
     relatively to the initial state [old]; and
   - and add these two increments to [old]. *)

Next steps, how to git at your data

From a design perspective, having access to the history makes it easier to design complex data structures with good compositional properties to use in unikernels. Moreover, as we made few assumptions on how the substrate of the low-level constructs need to be implemented, the Irmin engine can be be ported to many exotic backends such as JavaScript or anywhere else that Mirage OS runs: this is just a matter of implementing a rather trivial signature.

From a developer perspective, this means that the full history of operations is available to inspect, and that the history model is very similar to the Git workflow that is increasingly familiar. So similar, in fact, that we've developed a bidirectional mapping between Irmin data structures and the Git format to permit the git command-line to interact with.

The next post in our series explains what Dave Scott has been doing with the new version of the Xenstore database that powers every Xen host, where the entire database is stored in a prefix-tree Irmin data-structure and exposed as a Git repository which is live-updated! Here's a sneak preview...


Fitting the modular MirageOS TCP/IP stack together

By Mindy Preston

A critical part of any unikernel is its network stack -- it's difficult to think of a project that needs a cloud platform or runs on a set-top box with no network communications.

Mirage provides a number of module types that abstract interfaces at different layers of the network stack, allowing unikernels to customise their own stack based on their deployment needs. Depending on the abstractions your unikernel uses, you can fulfill these abstract interfaces with implementations ranging from the venerable and much-imitated Unix sockets API to a clean-slate Mirage TCP/IP stack written from the ground up in pure OCaml!

A Mirage unikernel will not use all these interfaces, but will pick those that are appropriate for the particular application at hand. If your unikernel just needs a standard TCP/IP stack, the STACKV4 abstraction will be sufficient. However, if you want more control over the implementation of the different layers in the stack or you don't need TCP support, you might construct your stack by hand using just the NETWORK, ETHIF, IPV4 and UDPV4 interfaces.

How a Stack Looks to a Mirage Application

Mirage provides a high-level interface to a TCP/IP network stack through the module type STACKV4. (Currently this can be included with open V1_LWT, but soon open V2_LWT will also bring this module type into scope as well when Mirage 2.0 is released.)

(** Single network stack *)                                                     
module type STACKV4 = STACKV4                                                   
  with type 'a io = 'a Lwt.t                                                    
   and type ('a,'b,'c) config = ('a,'b,'c) stackv4_config                       
   and type ipv4addr = Ipaddr.V4.t                                              
   and type buffer = Cstruct.t 

STACKV4 has useful high-level functions, a subset of which are reproduced below:

    val listen_udpv4 : t -> port:int -> UDPV4.callback -> unit
    val listen_tcpv4 : t -> port:int -> TCPV4.callback -> unit
    val listen : t -> unit io

as well as submodules that include functions for data transmission:

    module UDPV4 :
        type callback =
            src:ipv4addr -> dst:ipv4addr -> src_port:int -> buffer -> unit io
        val input :
          listeners:(dst_port:int -> callback option) -> t -> ipv4input
        val write :
          ?source_port:int ->
          dest_ip:ipv4addr -> dest_port:int -> t -> buffer -> unit io
    module TCPV4 :
        type flow
        type callback = flow -> unit io
        val read : flow -> [ `Eof | `Error of error | `Ok of buffer ] io
        val write : flow -> buffer -> unit io
        val close : flow -> unit io
        val create_connection :
          t -> ipv4addr * int -> [ `Error of error | `Ok of flow ] io
        val input : t -> listeners:(int -> callback option) -> ipv4input

These should look rather familiar if you've used the Unix sockets API before, with one notable difference: the stack accepts functional callbacks to react to events such as a new connection request. This permits callers of the library to define the precise datastructures that are used to store intermediate state (such as active connections). This becomes important when building very scalable systems that have to deal with lots of concurrent connections efficiently.

Configuring a Stack

The STACKV4 signature shown so far is just a module signature, and you need to find a concrete module that satisfies that signature. The known implementations of a module can be found in the mirage CLI frontend, which provids the configuration API for unikernels.
There are currently two implementations for STACKV4: direct and socket.

module STACKV4_direct: CONFIGURABLE with                                        
  type t = console impl * network impl * [`DHCP | `IPV4 of ipv4_config]         
module STACKV4_socket: CONFIGURABLE with                                        
  type t = console impl * Ipaddr.V4.t list  

The socket implementations rely on an underlying OS kernel to provide the transport, network, and data link layers, and therefore can't be used for a Xen guest VM deployment. Currently, the only way to use socket is by configuring your Mirage project for Unix with mirage configure --unix. This is the mode you will most often use when developing high-level application logic that doesn't need to delve into the innards of the network stack (e.g. a REST website).

The direct implementations use the mirage-tcpip implementations of the transport, network, and data link layers. When you use this stack, all the network traffic from the Ethernet level up will be handled in pure OCaml. This means that the direct stack will work with either a Xen guest VM (provided there's a valid network configuration for the unikernel's running environment of course), or a Unix program if there's a valid tuntap interface. direct this works with both mirage configure --xen and mirage configure --unix as long as there is a corresponding available device when the unikernel is run.

There are a few Mirage functions that provide IPv4 (and UDP/TCP) stack implementations (of type stackv4 impl), usable from your application code. The stackv4 impl is generated in by some logic set when the program is mirage configure'd - often by matching an environment variable. This means it's easy to flip between different stack implementations when developing an application just be recompiling the application. The below allows the developer to build socket code with NET=socket make and direct code with NET=direct make.

let main = foreign "Services.Main" (console @-> stackv4 @-> job)

let net =
  try match Sys.getenv "NET" with
    | "direct" -> `Direct
    | "socket" -> `Socket
    | _        -> `Direct
  with Not_found -> `Direct

let dhcp =
  try match Sys.getenv "ADDR" with
    | "dhcp"   -> `Dhcp
    | "static" -> `Static
    | _ -> `Dhcp
  with Not_found -> `Dhcp

let stack console =
  match net, dhcp with
  | `Direct, `Dhcp   -> direct_stackv4_with_dhcp console tap0
  | `Direct, `Static -> direct_stackv4_with_default_ipv4 console tap0
  | `Socket, _       -> socket_stackv4 console [Ipaddr.V4.any]

let () =
  register "services" [
    main $ default_console $ stack default_console

Moreover, it's possible to configure multiple stacks individually for use in the same program, and to register multiple modules from the same This means functions can be written such that they're aware of the network stack they ought to be using, and no other - a far cry from developing network code over most socket interfaces, where it can be quite difficult to separate concerns nicely.

let client = foreign "Unikernel.Client" (console @-> stackv4 @-> job)
let server = foreign "Unikernel.Server" (console @-> stackv4 @-> job) 

let client_netif = (netif "0")
let server_netif = (netif "1") 

let client_stack = direct_stackv4_with_dhcp default_console client_netif
let server_stack = direct_stackv4_with_dhcp default_console server_netif

let () = 
  register "unikernel" [
    main $ default_console $ client_stack;
    server $ default_console $ server_stack 

Acting on Stacks

Most network applications will either want to listen for incoming connections and respond to that traffic with information, or to connect to some remote host, execute a query, and receive information. STACKV4 offers simple ways to define functions implementing either of these patterns.

Establishing and Communicating Across Connections

STACKV4 offers listen_tcpv4 and listen_udpv4 functions for establishing listeners on specific ports. Both take a stack impl, a named port, and a callback function.

For UDP listeners, which are datagram-based rather than connection-based, callback is a function of the source IP, destination IP, source port, and the Cstruct.t that contains the payload data. Applications that wish to respond to incoming UDP packets with their own UDP responses (e.g., DNS servers) can use this information to construct reply packets and send them with UDPV4.write from within the callback function.

For TCP listeners, callback is a function of TCPV4.flow -> unit Lwt.t. STACKV4.TCPV4 offers read, write, and close on flows for application writers to build higher-level protocols on top of.

TCPV4 also offers create_connection, which allows client application code to establish TCP connections with remote servers. In success cases, create_connection returns a TCPV4.flow, which can be acted on just as the data in a callback above. There's also a polymorphic variant for error conditions, such as an unreachable remote server.

A Simple Example

Some very simple examples of user-level TCP code are included in mirage-tcpip/examples. is identical to the first configuration example above, and will build a direct stack by default.

Imagine a very simple application - one which simply repeats any data back to the sender, until the sender gets bored and wanders off (RFC 862, for the curious).

open Lwt
open V1_LWT

module Main (C: V1_LWT.CONSOLE) (S: V1_LWT.STACKV4) = struct
  let report_and_close c flow message =
    C.log c message;
    S.TCPV4.close flow

  let rec echo c flow = flow >>= fun result -> (
      match result with  
        | `Eof -> report_and_close c flow "Echo connection closure initiated."
        | `Error e -> 
          let message = 
          match e with 
            | `Timeout -> "Echo connection timed out; closing.\n"
            | `Refused -> "Echo connection refused; closing.\n"
            | `Unknown s -> (Printf.sprintf "Echo connection error: %s\n" s)
          report_and_close c flow message
        | `Ok buf ->
            S.TCPV4.write flow buf >>= fun () -> echo c flow

  let start c s = 
    S.listen_tcpv4 s ~port:7 (echo c);
    S.listen s


All the application programmer needs to do is define functionality in relation to flow for sending and receiving data, establish this function as a callback with listen_tcpv4, and start a listening thread with listen.

More Complex Uses

An OCaml HTTP server, Cohttp, is currently powering this very blog. A simple static webserver using Cohttp is included in mirage-skeleton.

The OCaml-TLS demonstration server announced here just a few days ago is also running atop Cohttp - source is available on Github.

The future

Mirage's TCP/IP stack is under active development! Some low-level details are still stubbed out, and we're working on implementing some of the trickier corners of TCP, as well as doing automated testing on the stack. We welcome testing tools, bug reports, bug fixes, and new protocol implementations!


Vchan: Low-latency inter-VM communication channels

By Jon Ludlam

Today's post is an update to Vincent Bernardoff's introducing vchan blog post, updated to use the modern build scheme for Mirage.

Unless you are familiar with Xen's source code, there is little chance that you've ever heard of the vchan library or protocol. Documentation about it is very scarce: a description can be found on vchan's public header file, that I quote here for convenience:

Originally borrowed from the Qubes OS Project, this code (i.e. libvchan) has been substantially rewritten [...] This is a library for inter-domain communication. A standard Xen ring buffer is used, with a datagram-based interface built on top. The grant reference and event channels are shared in XenStore under a user-specified path.

This protocol uses shared memory for inter-domain communication, i.e. between two VMs residing in the same Xen host, and uses Xen's mechanisms -- more specifically, ring buffers and event channels -- in order to achieve its aims. The term datagram-based interface simply means that the interface resembles UDP, although there is support for stream based communication (like TCP) as well.

The vchan protocol is an important feature in MirageOS 2.0 since it forms the foundational communication mechanism for building distributed clusters of unikernels that cooperate to solve problems that are beyond the power of a single node. Instead of forcing communication between nodes via a conventional wire protocol like TCP, it permits highly efficient low-overhead communication to nodes that are colocated on the same Xen host machine.

Before diving into vchan, I thought I'd also take the opportunity to describe the Ubuntu-Trusty environment for developing and running Xen unikernels.

Installing Xen on Ubuntu

Ubuntu 14.04 has good support for running Xen 4.4, the most recent release (at time of writing). For running VMs it's a good idea to install Ubuntu on an LVM volume rather than directly on a partition, which allows the use of LVs as the virtual disks for your VMs. On my system I have a 40 Gig partition for '/', an 8 Gig swap partition and the rest is free for my VMs:

$ sudo lvs
   LV     VG      Attr      LSize  Pool Origin Data%  Move Log Copy%  Convert
   root   st28-vg -wi-ao--- 37.25g
   swap_1 st28-vg -wi-ao---  7.99g

In this particular walkthough I won't be using disks, but later posts will. Install Xen via the meta-package. This brings in all you will need to run VMs:

$ sudo apt-get install xen-system-amd64

It used to be necessary to reorder the grub entries to make sure Xen was started by default, but this is no longer necessary. Once the machine has rebooted, you should be able to verify you're running virtualized by invoking 'xl':

$ sudo xl list
Name                                        ID   Mem VCPUs      State   Time(s)
Domain-0                                     0  7958     6     r-----       9.7

My machine has 8 Gigs of memory, and this list shows that it's all being used by my dom0, so I'll need to either balloon down dom0 or reboot with a lower maximum memory. Ballooning is the most straightfoward:

$ sudo xenstore-write /local/domain/0/memory/target 4096000
$ sudo xl list
Name                                        ID   Mem VCPUs      State   Time(s)
Domain-0                                     0  4000     6     r-----      12.2

This is handy for quick testing, but is discouraged by the Xen folks. So alternatively, change the xen command line by editing /etc/default/grub and add the line:


Once again, update-grub and reboot.


Now lets get Mirage up and running. Install ocaml, opam and set up the opam environment:

$ sudo apt-get install ocaml opam ocaml-native-compilers camlp4-extra
$ opam init
$ eval `opam config env`

Don't forget the ocaml-native-compilers, as without this we can't compile the unikernels. Now we are almost ready to install Mirage; we need two more dependencies, and then we're good to go.

$ sudo apt-get install m4 libxen-dev
$ opam install mirage mirage-xen mirage-unix vchan

Where m4 is for ocamlfind, and libxen-dev is required to compile the unix variants of the xen-evtchn and xen-gnt libraries. Without these installing vchan will complain that there is no xen-evtchn.lwt library installed.

This second line installs the various Mirage and vchan libraries, but doesn't build the demo unikernel and Unix CLI. To get them, clone the ocaml-vchan repository:

$ git clone

The demo unikernel is a very straightforward capitalizing echo server. The main function simply consists of

let (>>=) = Lwt.bind

let (>>|=) m f = m >>= function
| `Ok x -> f x
| `Eof -> (Failure "End of file")
| `Error (`Not_connected state) -> (Failure (Printf.sprintf "Not in a connected state: %s"
      (Sexplib.Sexp.to_string (Node.V.sexp_of_state state))))

let rec echo vch = vch >>|= fun input_line ->
  let line = String.uppercase (Cstruct.to_string input_line) in
  let buf = Cstruct.create (String.length line) in
  Cstruct.blit_from_string line 0 buf 0 (String.length line);
  Node.V.write vch buf >>|= fun () ->
  echo vch

where we've defined an error-handling monadic bind (>>|=) which is then used to sequence the read and write operations.

Building the CLI is done simply via make.

$ make
$ ls -l node_cli.native
lrwxrwxrwx 1 jludlam jludlam 52 Jul 14 14:56 node_cli.native -> /home/jludlam/ocaml-vchan/_build/cli/node_cli.native

Building the unikernel is done via the mirage tool:

$ cd test
$ mirage configure --xen
$ make depend
$ make
$ ls -l mir-echo.xen echo.xl
-rw-rw-r-- 1 jludlam jludlam     596 Jul 14 14:58 echo.xl
-rwxrwxr-x 1 jludlam jludlam 3803982 Jul 14 14:59 mir-echo.xen

This make both the unikernel binary (the mir-echo.xen file) and a convenient xl script to run it. To run, we use the xl tool, passing '-c' to connect directly to the console so we can see what's going on:

$ sudo xl create -c echo.xl
Parsing config from echo.xl
kernel.c: Mirage OS!
kernel.c:   start_info: 0x11cd000(VA)
kernel.c:     nr_pages: 0x10000
kernel.c:   shared_inf: 0xdf2f6000(MA)
kernel.c:      pt_base: 0x11d0000(VA)
kernel.c: nr_pt_frames: 0xd
kernel.c:     mfn_list: 0x114d000(VA)
kernel.c:    mod_start: 0x0(VA)
kernel.c:      mod_len: 0
kernel.c:        flags: 0x0
kernel.c:     cmd_line:
x86_setup.c:   stack:      0x144f40-0x944f40
mm.c: MM: Init
x86_mm.c:       _text: 0x0(VA)
x86_mm.c:      _etext: 0xb8eec(VA)
x86_mm.c:    _erodata: 0xde000(VA)
x86_mm.c:      _edata: 0x1336f0(VA)
x86_mm.c: stack start: 0x144f40(VA)
x86_mm.c:        _end: 0x114d000(VA)
x86_mm.c:   start_pfn: 11e0
x86_mm.c:     max_pfn: 10000
x86_mm.c: Mapping memory range 0x1400000 - 0x10000000
x86_mm.c: setting 0x0-0xde000 readonly
x86_mm.c: skipped 0x1000
mm.c: MM: Initialise page allocator for 0x1256000 -> 0x10000000
mm.c: MM: done
x86_mm.c: Pages to allocate for p2m map: 2
x86_mm.c: Used 2 pages for map
x86_mm.c: Demand map pfns at 10001000-2010001000.
Initialising timer interface
Initializing Server domid=0 xs_path=data/vchan
gnttab_stubs.c: gnttab_table mapped at 0x10001000
Server: right_order = 13, left_order = 13
allocate_buffer_locations: gntref = 9
allocate_buffer_locations: gntref = 10
allocate_buffer_locations: gntref = 11
allocate_buffer_locations: gntref = 12
Writing config into the XenStore
Shared page is:

00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0d 00 0d 00 02 01 01 00 09 00 00 00 0a 00 00 00
0b 00 00 00 0c 00 00 00
Initialization done!

Vchan is domain-to-domain communication, and relies on Xen's grant tables to share the memory. The entries in the grant tables have domain-level access control, so we need to know the domain ID of the client and server in order to set up the communications. The test unikernel server is hard-coded to talk to domain 0, so we only need to know the domain ID of our echo server. In another terminal,

$ sudo xl list
Name                                        ID   Mem VCPUs      State   Time(s)
Domain-0                                     0  4095     6     r-----    1602.9
echo                                         2   256     1     -b----       0.0

In this case, the domain ID is 2, so we invoke the CLI as follows:

$ sudo ./node_cli.native 2
Client initializing: Received gntref = 8, evtchn = 4
Mapped the ring shared page:

00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0d 00 0d 00 02 01 01 00 09 00 00 00 0a 00 00 00
0b 00 00 00 0c 00 00 00
Correctly bound evtchn number 71

We're now connected via vchan to the Mirage domain. The test server is simply a capitalisation service:

hello from dom0

Ctrl-C to get out of the CLI, and destroy the domain with an xl destroy:

$ sudo xl destroy test

vchan is a very low-level communication mechanism, and so our next post on this topic will address how to use it in combination with a name resolver to intelligently map connection requests to use vchan if available, and otherwise fall back to normal TCP or TCP+TLS.


Modular foreign function bindings

By Jeremy Yallop

One of the most frequent questions about MirageOS from developers is "do I really need to write all my code in OCaml"? There are, of course, very good reasons to build the core system in pure OCaml: the module system permits reusing algorithmic abstractions at scale, and OCaml's static type checking makes it possible to enforce lightweight invariants across interfaces. However, it's ultimately necessary to support interfacing to existing code, and this blog post will describe what we're doing to make this possible this without sacrificing the security benefits afforded by unikernels.

A MirageOS application works by abstracting the logic of the application from the details of platform that it is compiled for. The mirage CLI tool parses a configuration file that represents the desired hardware target, which can be a Unix binary or a specialized Xen guest OS. Our foreign function interface design elaborates on these design principles by separating the description of the C foreign functions from how we link to that code. For instance, a Unix unikernel could use the normal to connect to a shared library, while in Xen we would need to interface to that C library through some other mechanism (for instance, a separate VM could be spawned to run the untrusted OpenSSL code). If you're curious about how this works, this blog post is for you!

Introducing ctypes

ocaml-ctypes ("ctypes" for short) is a library for gluing together OCaml code and C code without writing any C. This post introduces the ctypes library with a couple of simple examples, and outlines how OCaml's module system makes it possible to write high-level bindings to C that are independent of any particular linking mechanism.

Hello, C

Binding a C function using ctypes involves two steps.

  • First, construct an OCaml value that represents the type of the function
  • Second, use the type representation and the function name to resolve and bind the function

For example, here's a binding to C's puts function, which prints a string to standard output and returns the number of characters written:

let puts = foreign "puts" (string @-> returning int)

After the call to foreign the bound function is available to OCaml immediately. Here's a call to puts from the interactive top level:

# puts "Hello, world";;
Hello, world
- : int = 13


Now that we've had a taste of ctypes, let's look at a more realistic example: a program that defines bindings to the expat XML parsing library, then uses them to display the structure of an XML document.

We'll start by describing the types used by expat. Since ctypes represents C types as OCaml values, each of the types we need becomes a value binding in our OCaml program. The parser object involves an incomplete (abstract) struct definition and a typedef for a pointer to a struct:

struct xml_ParserStruct;
typedef xml_ParserStruct *xml_Parser;

In ctypes these become calls to the structure and ptr functions:

let parser_struct : [`XML_ParserStruct] structure typ = structure "xml_ParserStruct"
let xml_Parser = ptr parser_struct

Next, we'll use the type representations to bind some functions. The XML_ParserCreate and XML_ParserFree functions construct and destroy parser objects. As with puts, each function binding involves a simple call to foreign:

let parser_create = foreign "XML_ParserCreate"
  (ptr void @-> returning xml_Parser)
let parser_free = foreign "XML_ParserFree"
  (xml_Parser @-> returning void)

Expat operates primarily through callbacks: when start and end elements are encountered the parser invokes user-registered functions, passing the tag names and attributes (along with a piece of user data):

typedef void (*start_handler)(void *, char *, char **);
typedef void (*end_handler)(void *, char *);

In ctypes function pointer types are built using the funptr function:

let start_handler =
  funptr (ptr void @-> string @-> ptr string @-> returning void)
let end_handler =
  funptr (ptr void @-> string @-> returning void)

We can use the start_handler and end_handler type representations to bind XML_SetElementHandler, the callback-registration function:

let set_element_handler = foreign "XML_SetElementHandler"
  (xml_Parser @-> start_handler @-> end_handler @-> returning void)

The type that OCaml infers for set_element_handler reveals that the function accepts regular OCaml functions as arguments, since the argument types are normal OCaml function types:

val set_element_handler :
  [ `XML_ParserStruct ] structure ptr ->
  (unit ptr -> string -> string ptr -> unit) ->
  (unit ptr -> string -> unit) -> unit

There's one remaining function to bind, then we're ready to use the library. The XML_Parse function performs the actual parsing, invoking the callbacks when tags are encountered:

let parse = foreign "XML_Parse"
  (xml_Parser @-> string @-> int @-> int @-> returning int)

As before, all the functions that we've bound are available for use immediately. We'll start by using them to define a more idiomatic OCaml entry point to the library. The parse_string function accepts the start and end callbacks as labelled arguments, along with a string to parse:

let parse_string ~start_handler ~end_handler s =
  let p = parser_create null in
  let () = set_element_handler p start_handler end_handler in
  let _ = parse p s (String.length s) 1 in
  parser_free p

Using parse_string we can write a program that prints out the names of each element in an XML document, indented according to nesting depth:

let depth = ref 0

let start_handler _ name _ =
  Printf.printf "%*s%s\n" (!depth * 3) "" name;
  incr depth

let end_handler _ _ =
  decr depth

let () =
  parse_string ~start_handler ~end_handler (In_channel.input_all stdin)

The full source of the program is available on github.

Here's the program in action:

$ ocamlfind opt -thread -package core,ctypes.foreign \
   -linkpkg -cclib -lexpat -o expat_example
$ wget -q -O /dev/stdout \
  | ./expat_example

Since this is just a high-level overview we've passed over a number of details. The interested reader can find a more comprehensive introduction to using ctypes in Chapter 19: Foreign Function Interface of Real World OCaml.

Dynamic vs static

Up to this point we've been using a single function, foreign, to make C functions available to OCaml. Although foreign is simple to use, there's quite a lot going on behind the scenes. The two arguments to foreign are used to dynamically construct an OCaml function value that wraps the C function: the name is used to resolve the code for the C function, and the type representation is used to construct a call frame appropriate to the C types invovled and to the underlying platform.

The dynamic nature of foreign that makes it convenient for interactive use, also makes it unsuitable for some environments. There are three main drawbacks:

  • Binding functions dynamically involves a certain loss of safety: since C libraries typically don't maintain information about the types of the functions they contain, there's no way to check whether the type representation passed to foreign matches the actual type of the C function.

  • Dynamically constructing calls introduces a certain interpretative overhead. In mitigation, this overhead is much less than might be supposed, since much of the work can be done when the function is bound rather than when the call is made, and foreign has been used to bind C functions in performance-sensitive applications without problems.

  • The implementation of foreign uses a low-level library, libffi, to deal with calling conventions across platforms. While libffi is mature and widely supported, it's not appropriate for use in every environment. For example, introducing such a (relatively) large and complex library into Mirage would compromise many of the benefits of writing the rest of the system in OCaml.

Happily, there's a solution at hand. As the introduction hints, foreign is one of a number of binding strategies, and OCaml's module system makes it easy to defer the choice of which strategy to use when writing the actual code. Placing the expat bindings in a functor (parameterised module) makes it possible to abstract over the linking strategy:

module Bindings(F : FOREIGN) =
  let parser_create = F.foreign "XML_ParserCreate"
    (ptr void @-> returning xml_Parser)
  let parser_free = F.foreign "XML_ParserFree"
    (xml_Parser @-> returning void)
  let set_element_handler = F.foreign "XML_SetElementHandler"
    (xml_Parser @-> start_handler @-> end_handler @-> returning void)
  let parse = F.foreign "XML_Parse"
    (xml_Parser @-> string @-> int @-> int @-> returning int)

The Bindings module accepts a single parameter of type FOREIGN, which encodes the binding strategy to use. Instantiating Bindings with a module containing the foreign function used above recovers the dynamically-constructed bindings that we've been using so far. However, there are now other possibilities available. In particular, we can instantiate Bindings with code generators that output code to expose the bound functions to OCaml. The actual instantiation is hidden behind a couple of convenient functions, write_c and write_ml, which accept Bindings as a parameter and write to a formatter:

Cstubs.write_c formatter ~prefix:"expat" ~bindings:(module Bindings)
Cstubs.write_ml formatter ~prefix:"expat" ~bindings:(module Bindings)

Generating code in this way eliminates the concerns associated with constructing calls dynamically:

  • The C compiler checks the types of the generated calls against the C headers (the API), so the safety concerns associated with linking directly against the C library binaries (the ABI) don't apply.

  • There's no interpretative overhead, since the generated code is (statically) compiled.

  • The dependency on libffi disappears altogether.

How easy is it in practice to switch between dynamic and static binding strategies? It turns out that it's quite straightforward, even for code that was originally written without parameterisation. Bindings written using early releases of ctypes used the dynamic strategy exclusively, since dynamic binding was then the only option available. The commit logs for projects that switched over to static generation and linking (e.g. ocaml-lz4 and async-ssl) when it became available show that moving to the new approach involved only straightforward and localised changes.

Local vs remote

Generating code is safer than constructing calls dynamically, since it allows the C compiler to check the types of function calls against declarations. However, there are some safety problems that even C's type checking doesn't detect. For instance, the following call is type correct (given suitable definitions of p and q), but is likely to misbehave at run time:

memcpy(p, q, SIZE_MAX)

In contrast, code written purely in OCaml detects and prevents attempts to write beyond the bounds of allocated objects:

# StringLabels.blit ~src ~dst ~src_pos:0 ~dst_pos:0 ~len:max_int;;
Exception: Invalid_argument "String.blit".

It seems a shame to weaken OCaml's safety guarantees by linking in C code that can potentially write to any region of memory, but what is the alternative?

One possibility is to use privilege separation to separate trusted OCaml code from untrusted C functions. The modular design of ctypes means that privilege separation can be treated as one more linking strategy: we can run C code in an entirely separate process (or for Mirage/Xen, in a separate virtual machine), and instantiate Bindings with a strategy that forwards calls to the process using standard inter-process communication. The remote calling strategy is not supported in the current release of ctypes, but it's scheduled for a future version. As with the switch from dynamic to static bindings, we anticipate that updating existing bindings to use cross-process calls will be straightforward.

This introductory post should give you a sense of the power of the unikernel approach in Mirage. By turning the FFI into just another library (for the C interface description) and protocol (for the linkage model), we can use code generation to map application logic onto the privilege model most suitable for the target hardware platform. This starts with Unix processes, continues onto Xen paravirtualization, and could even extend into CHERI fine-grained compartmentalization.

Further examples

Although ctypes is a fairly new library, it's already in use in a number of projects across a variety of domains: graphics, multimedia, compression, cryptography, security, geospatial data, communication, and many others. Further resources (documentation, forums, etc.) are available via the home page.


OCaml-TLS: the protocol implementation and mitigations to known attacks

By David Kaloper, Hannes Mehnert

This is the fifth in a series of posts that introduce new libraries for a pure OCaml implementation of TLS. You might like to begin with the introduction.

ocaml-tls is the new, clean-slate implementation of TLS in OCaml that we've been working on for the past six months. In this post we try to document some of its internal design, the reasons for the decisions we made, and the current security status of that work. Try our live interactive demonstration server which visualises TLS sessions.

The OCaml-TLS architecture

The OCaml ecosystem has several distinct ways of interacting with the outside world (and the network in particular): straightforward unix interfaces and the asynchronous programming libraries lwt and async. One of the early considerations was not to restrict ourselves to any of those -- we wanted to support them all.

There were also two distinct basic "platforms" we wanted to target from the outset: the case of a simple executable, and the case of Mirage unikernels.

So one of the first questions we faced was deciding how to represent interactions with the network in a portable way. This can be done by systematically abstracting out the API boundary which gives access to network operations, but we had a third thing in mind as well: we wanted to exploit the functional nature of OCaml to its fullest extent!

Our various prior experiences with Haskell and Idris convinced us to adopt what is called "purely functional" technique. We believe it to be an approach which first forces the programmer to give principled answers to all the difficult design questions (errors and global data-flow) in advance, and then leads to far cleaner and composable code later on. A purely functional system has all the data paths made completely explicit in the form of function arguments and results. There are no unaccounted-for interactions between components mediated by shared state, and all the activity of the parts of the system is exposed through types since, after all, it's only about computing values from values.

For these reasons, the library is split into two parts: the directory /lib (and the corresponding findlib package tls) contains the core TLS logic, and /mirage and /lwt (packaged as tls.mirage and tls.lwt respectively) contain front-ends that tie the core to Mirage and Lwt_unix.


The core library is purely functional. A TLS session is represented by the abstract type Tls.Engine.state, and various functions consume this session type together with raw bytes (Cstruct.t -- which is by itself mutable, but ocaml-tls eschews this) and produce new session values and resulting buffers.

The central entry point is handle_tls, which transforms an input state and a buffer to an output state, a (possibly empty) buffer to send to the communication partner, and an optional buffer of data intended to be received by the application:

type state

type ret = [
  | `Ok of [ `Ok of state | `Eof | `Alert of alert ] *
      [ `Response of Cstruct.t ] * [ `Data of Cstruct.t option ]
  | `Fail of alert * [ `Response of Cstruct.t ]

val handle_tls : state -> Cstruct.t -> ret

As the signature shows, errors are signalled through the ret type, which is a polymorphic variant. This reflects the actual internal structure: all the errors are represented as values, and operations are composed using an error monad.

Other entry points share the same basic behaviour: they transform the prior state and input bytes into the later state and output bytes.

Here's a rough outline of what happens in handle_tls:

  • TLS packets consist of a header, which contains the protocol version, length, and content type, and the payload of the given content type. Once inside our main handler, we separate the buffer into TLS records, and process each individually. We first check that the version number is correct, then decrypt, and verify the mac.

  • Decrypted data is then dispatched to one of four sub-protocol handlers (Handshake, Change Cipher Spec, Alert and Application Data). Each handler can return a new handshake state, outgoing data, application data, the new decryption state or an error (with the outgoing data being an interleaved list of buffers and new encryption states).

  • The outgoing buffers and the encryption states are traversed to produce the final output to be sent to the communication partner, and the final encryption, decryption and handshake states are combined into a new overall state which is returned to the caller.

Handshake is (by far) the most complex TLS sub-protocol, with an elaborate state machine. Our client and server encode this state as a "flat" sum type, with exactly one incoming message allowed per state. The handlers first parse the handshake packet (which fails in case of malformed or unknown data) and then dispatch it to the handling function. The handshake state is carried around and a fresh one is returned from the handler in case it needs updates. It consists of a protocol version, the handshake state, configuration, renegotiation data, and possibly a handshake fragment.

Logic of both handshake handlers is very localised, and does not mutate any global data structures.

Core API

OCaml permits the implementation a module to be exported via a more abstract signature that hides the internal representation details. Our public API for the core library consists of the Tls.Engine and Tls.Config modules.

Tls.Engine contains the basic reactive function handle_tls, mentioned above, which processes incoming data and optionally produces a response, together with several operations that allow one to initiate message transfer like send_application_data (which processes application-level messages for sending), send_close_notify (for sending the ending message) and reneg (which initiates full TLS renegotiation).

The module also contains the only two ways to obtain the initial state:

val client : Config.client -> (state * Cstruct.t)
val server : Config.server -> state

That is, one needs a configuration value to create it. The Cstruct.t that client emits is the initial Client Hello since in TLS, the client starts the session.

Tls.Config synthesizes configurations, separately for client and server endpoints, through the functions client_exn and server_exn. They take a number of parameters that define a TLS session, check them for consistency, and return the sanitized config value which can be used to create a state and, thus, a session. If the check fails, they raise an exception.

The parameters include the pair of a certificate and its private key for the server, and an X509.Authenticator.t for the client, both produced by our ocaml-x509 library and described in a previous article.

This design reflects our attempts to make the API as close to "fire and forget" as we could, given the complexity of TLS: we wanted the library to be relatively straightforward to use, have a minimal API footprint and, above all, fail very early and very loudly when misconfigured.

Effectful front-ends

Clearly, reading and writing network data does change the state of the world. Having a pure value describing the state of a TLS session is not really useful once we write something onto the network; it is certainly not the case that we can use more than one distinct state to process further data, as only one value is in sync with the other endpoint at any given time.

Therefore we wrap the core types into stateful structures loosely inspired by sockets and provide IO operations on those. The structures of mirage and lwt front-ends mirror one another.

In both cases, the structure is pull-based in the sense that no processing is done until the client requires a read, as opposed to a callback-driven design where the client registers a callback and the library starts spinning in a listening loop and invoking it as soon as there is data to be processed. We do this because in an asynchronous context, it is easy to create a callback-driven interface from a demand-driven one, but the opposite is possible only with unbounded buffering of incoming data.

One exception to demand-driven design is the initial session creation: the library will only yield the connection after the first handshake is over, ensuring the invariant that it is impossible to interact with a connection if it hasn't already been fully established.


The Mirage interface matches the FLOW signature (with additional TLS-specific operations). We provide a functor that needs to be applied to an underlying TCP module, to obtain a TLS transport on top. For example:

module Server (Stack: STACKV4) (Entropy: ENTROPY) (KV: KV_RO) =

  module TLS  = Tls_mirage.Make (Stack.TCPV4) (Entropy)
  module X509 = Tls_mirage.X509 (KV) (Clock)

  let accept conf flow =
    TLS.server_of_tcp_flow conf flow >>= function
    | `Ok tls -> tls >>= function
      | `Ok buf ->
        TLS.write tls buf >>= fun () -> TLS.close buf

  let start stack e kv =
    TLS.attach_entropy e >>= fun () ->
    lwt authenticator = X509.authenticator kv `Default in
    let conf          = Tls.Config.server_exn ~authenticator () in
    Stack.listen_tcpv4 stack 4433 (accept conf) ;
    Stack.listen stack



The lwt interface has two layers. Tls_lwt.Unix is loosely based on read/write operations from Lwt_unix and provides in-place update of buffers. read, for example, takes a Cstruct.t to write into and returns the number of bytes read. The surrounding module, Tls_lwt, provides a simpler, Lwt_io-compatible API built on top:

let main host port =
  Tls_lwt.rng_init () >>= fun () ->
  lwt authenticator = X509_lwt.authenticator (`Ca_dir nss_trusted_ca_dir) in
  lwt (ic, oc)      = Tls_lwt.connect ~authenticator (host, port) in
  let req = String.concat "\r\n" [
    "GET / HTTP/1.1" ; "Host: " ^ host ; "Connection: close" ; "" ; ""
  ] in
  Lwt_io.(write oc req >>= fun () -> read ic >>= print)

We have further plans to provide wrappers for `Async` and plain `Unix` in a similar vein.

Attacks on TLS

TLS the most widely deployed security protocol on the Internet and, at over 15 years, is also showing its age. As such, a flaw is a valuable commodity due to the commercially sensitive nature of data that is encrypted with TLS. Various vulnerabilities on different layers of TLS have been found - heartbleed and others are implementation specific, advancements in cryptanalysis such as collisions of MD5 lead to vulnerabilities, and even others are due to incorrect usage of TLS (truncation attack or BREACH). Finally, some weaknesses are in the protocol itself. Extensive overviews of attacks on TLS are available.

We look at protocol level attacks of TLS and how ocaml-tls implements mitigations against these. TLS 1.2 RFC provides an overview of attacks and mitigations, and we track our progress in covering them. This is slightly out of date as the RFC is roughly six years old and in the meantime more attacks have been published, such as the renegotiation flaw.

As already mentioned, we track all our mitigated and open security issues on our GitHub issue tracker.

Due to the choice of using OCaml, a memory managed programming language, we obstruct entire bug classes, namely temporal and spatial memory safety.

Cryptanalysis and improvement of computational power weaken some ciphers, such as RC4 and 3DES (see issue 8 and issue 10). If we phase these two ciphers out, there wouldn't be any matching ciphersuite left to communicate with some compliant TLS-1.0 implementations, such as Windows XP, that do not support AES.

Timing attacks

When the timing characteristics between the common case and the error case are different, this might potentially leak confidential information. Timing is a very prominent side-channel and there are a huge variety of timing attacks on different layers, which are observable by different attackers. Small differences in timing behaviour might initially be exploitable only by a local attacker, but advancements to the attack (e.g. increasing the number of tests) might allow a remote attacker to filter the noise and exploit the different timing behaviour.

Timing of cryptographic primitives

We already mentioned cache timing attacks on our AES implementation, and that we use blinding techniques to mitigate RSA timing attacks.

By using a memory managed programming language, we open the attack vector of garbage collector (GC) timing attacks (also mentioned in our nocrypto introduction).

Furthermore, research has been done on virtual machine side channels (l3, cross vm and cache timing), which we will need to study and mitigate appropriately.

For the time being we suggest to not use the stack on a multi-tenant shared host or on a shared host which malicious users might have access to.


In 1998, Daniel Bleichenbacher discovered a timing flaw in the PKCS1 encoding of the premaster secret: the TLS server failed faster when the padding was wrong than when the decryption failed. Using this timing, an attacker can run an adaptive chosen ciphertext attack and find out the plain text of a PKCS1 encrypted message. In TLS, when RSA is used as the key exchange method, this leads to discovery of the premaster secret, which is used to derive the keys for the current session.

The mitigation is to have both padding and decryption failures use the exact same amount of time, thus there should not be any data-dependent branches or different memory access patterns in the code. We implemented this mitigation in Handshake_server.

Padding oracle and CBC timing

Vaudenay discovered a vulnerability involving block ciphers: if an attacker can distinguish between bad mac and bad padding, recovery of the plaintext is possible (within an adaptive chosen ciphertext attack). Another approach using the same issue is to use timing information instead of separate error messages. Further details are described here.

The countermeasure, which we implement here, is to continue with the mac computation even though the padding is incorrect. Furthermore, we send the same alert (bad_record_mac) independent of whether the padding is malformed or the mac is incorrect.

Lucky 13

An advancement of the CBC timing attack was discovered in 2013, named Lucky 13. Due to the fact that the mac is computed over the plaintext without padding, there is a slight (but measurable) difference in timing between computing the mac of the plaintext and computing the fake mac of the ciphertext. This leaks information. We do not have proper mitigation against Lucky 13 in place yet. You can find further discussion in issue 7 and pull request 49.

Renegotiation not authenticated

In 2009, Marsh Ray published a vulnerability of the TLS protocol which lets an attacker prepend arbitrary data to a session due to unauthenticated renegotiation. The attack exploits the fact that a renegotiation of ciphers and key material is possible within a session, and this renegotiated handshake is not authenticated by the previous handshake. A man in the middle can initiate a session with a server, send some data, and hand over the session to a client. Neither the client nor the server can detect the man in the middle.

A fix for this issue is the secure renegotiation extension, which embeds authenticated data of the previous handshake into the client and server hello messages. Now, if a man in the middle initiates a renegotiation, the server will not complete it due to missing authentication data (the client believes this is the first handshake).

We implement and require the secure renegotiation extension by default, but it is possible to configure ocaml-tls to not require it -- to be able to communicate with servers and clients which do not support this extension.

Implementation of the mitigation is on the server side in ensure_reneg and on the client side in validate_reneg. The data required for the secure renegotiation is stored in `handshake_state` while sending and receiving Finished messages. You can find further discussion in issue 3.

TLS 1.0 and known-plaintext (BEAST)

TLS 1.0 reuses the last ciphertext block as IV in CBC mode. If an attacker has a (partially) known plaintext, she can find the remaining plaintext. This is known as the BEAST attack and there is a long discussion about mitigations. Our mitigation is to prepend each TLS-1.0 application data fragment with an empty fragment to randomize the IV. We do this exactly here. There is further discussion in issue 2.

Our mitigation is slightly different from the 1/n-1 splitting proposed here: we split every application data frame into a 0 byte and n byte frame, whereas they split into a 1 byte and a n-1 byte frame.

Researchers have exploited this vulnerability in 2011, although it was known since 2006. TLS versions 1.1 and 1.2 use an explicit IV, instead of reusing the last cipher block on the wire.

Compression and information leakage (CRIME)

When using compression on a chosen-plaintext, encrypting this can leak information, known as CRIME. BREACH furthermore exploits application layer compression, such as HTTP compression. We mitigate CRIME by not providing any TLS compression support, while we cannot do anything to mitigate BREACH.

Traffic analysis

Due to limited amount of padding data, the actual size of transmitted data can be recovered. The mitigation is to implement length hiding policies. This is tracked as issue 162.

Version rollback

SSL-2.0 is insecure, a man in the middle can downgrade the version to SSL-2.0. The mitigation we implement is that we do not support SSL-2.0, and thus cannot be downgraded. Also, we check that the version of the client hello matches the first two bytes in the premaster secret here. You can find further discussion in issue 5.

Triple handshake

A vulnerability including session resumption and renegotiation was discovered by the miTLS team, named triple handshake. Mitigations include disallowing renegotiation, disallowing modification of the certificate during renegotiation, or a hello extension. Since we do not support session resumption yet, we have not yet implemented any of the mentioned mitigations. There is further discussion in issue 9.

Alert attack

A fragment of an alert can be sent by a man in the middle during the initial handshake. If the fragment is not cleared once the handshake is finished, the authentication of alerts is broken. This was discovered in 2012; our mitigation is to discard fragmented alerts.


Within six months, two hackers managed to develop a clean-slate TLS stack, together with required crypto primitives, ASN.1, and X.509 handling, in a high-level pure language. We interoperate with widely deployed TLS stacks, as shown by our demo server. The code size is nearly two orders of magnitude smaller than OpenSSL, the most widely used open source library (written in C, which a lot of programming languages wrap instead of providing their own TLS implementation). Our code base seems to be robust -- the demo server successfully finished over 22500 sessions in less than a week, with only 11 failing traces.

There is a huge need for high quality TLS implementations, because several TLS implementations suffered this year from severe security problems, such as heartbleed, goto fail, session id, Bleichenbacher, change cipher suite and GCM DoS. The main cause is implementation complexity due to lack of abstraction, and memory safety issues.

We still need to address some security issues, and improve our performance. We invite people to do rigorous code audits (both manual and automated) and try testing our code in their services.

Please be aware that this release is a beta and is missing external code audits. It is not yet intended for use in any security critical applications.


Since this is the final post in our series, we would like to thank all people who reported issues so far: Anil Madhavapeddy, Török Edwin, Daniel Bünzli, Andreas Bogk, Gregor Kopf, Graham Steel, Jerome Vouillon, Amir Chaudhry, Ashish Agarwal. Additionally, we want to thank the miTLS team (especially Cedric and Karthikeyan) for fruitful discussions, as well as the OCaml Labs and Mirage teams. And thanks to Peter Sewell and Richard Mortier for funding within the REMS, UCN, and Horizon projects. The software was started in Aftas beach house in Mirleft, Morocco.

Aftas Beach

Posts in this TLS series:


OCaml-TLS: ASN.1 and notation embedding

By David Kaloper

This is the fourth in a series of posts that introduce new libraries for a pure OCaml implementation of TLS. You might like to begin with the introduction.

asn1-combinators is a library that allows one to express ASN.1 grammars directly in OCaml, manipulate them as first-class entities, combine them with one of several ASN encoding rules and use the result to parse or serialize values.

It is the parsing and serialization backend for our X.509 certificate library, which in turn provides certificate handling for ocaml-tls. We wrote about the X.509 certificate handling yesterday.

What is ASN.1, really?

ASN.1 (Abstract Syntax Notation, version one) is a way to describe on-the-wire representation of messages. It is split into two components: a way to describe the content of a message, i.e. a notation for its abstract syntax, and a series of standard encoding rules that define the exact byte representations of those syntaxes. It is defined in ITU-T standards X.680-X.683 and X.690-X.695.

The notation itself contains primitive grammar elements, such as BIT STRING or GeneralizedTime, and constructs that allow for creation of compound grammars from other grammars, like SEQUENCE. The notation is probably best introduced through a real-world example:

-- Simple name bindings
UniqueIdentifier ::= BIT STRING

-- Products
Validity ::= SEQUENCE {
  notBefore Time,
  notAfter  Time

-- Sums
Time ::= CHOICE {
  utcTime     UTCTime,
  generalTime GeneralizedTime

(Example from RFC 5280, the RFC that describes X.509 certificates which heavily rely on ASN.)

The first definition shows that we can introduce an alias for any existing ASN grammar fragment, in this case the primitive BIT STRING. The second and third definitions are, at least morally, a product and a sum.

At their very core, ASN grammars look roughly like algebraic data types, with a range of pre-defined primitive grammar fragments like BIT STRING, INTEGER, NULL, BOOLEAN or even GeneralizedTime, and a number of combining constructs that can be understood as denoting sums and products.

Definitions such as the above are arranged into named modules. The standard even provides for some abstractive capabilities: initially just a macro facility, and later a form of parameterized interfaces.

To facilitate actual message transfer, a grammar needs to be coupled with an encoding. By far the most relevant ones are Basic Encoding Rules (BER) and Distinguished Encoding Rules (DER), although other encodings exist.

BER and DER are tag-length-value (TLV) encodings, meaning that every value is encoded as a triplet containing a tag that gives the interpretation of its contents, a length field, and the actual contents which can in turn contain other TLV triplets.

Let's drop the time from the example above, as time encoding is a little involved, and assume a simpler version for a moment:

Pair ::= SEQUENCE {
  car Value,
  cdr Value

Value ::= CHOICE {
  v_str UTF8String,
  v_int INTEGER

Then two possible BER encodings of a Pair ("foo", 42) are:

  30         - SEQUENCE            30         - SEQUENCE
  08         - length              0c         - length
  [ 0c       - UTF8String          [ 2c       - UTF8String, compound
    03       - length                07       - length
    [ 66     - 'f'                   [ 0c     - UTF8String
      6f     - 'o'                     01     - length
      6f ]   - 'o'                     [ 66 ] - 'f'
    02       - INTEGER                 0c     - UTF8String
    01       - length                  02     - length
    [ 2a ] ] - 42                      [ 6f   - 'o'
                                         6f ] - 'o'
                                     02       - INTEGER
                                     01       - length
                                     [ 2a ] ] - 42

The left one is also the only valid DER encoding of this value: BER allows certain freedoms in encoding, while DER is just a BER subset without those freedoms. The property of DER that any value has exactly one encoding is useful, for example, when trying to digitally sign a value.

If this piqued your curiosity about ASN, you might want to take a detour and check out this excellent writeup.

A bit of history

The description above paints a picture of a technology a little like Google's Protocol Buffers or Apache Thrift: a way to declaratively specify the structure of a set of values and derive parsers and serializers, with the addition of multiple concrete representations.

But the devil is in the detail. For instance, the examples above intentionally gloss over the fact that often concrete tag values leak into the grammar specifications for various disambiguation reasons. And ASN has more than 10 different string types, most of which use long-obsolete character encodings. Not to mention that the full standard is close to 200 pages of relatively dense language and quite difficult to follow. In general, ASN seems to have too many features for the relatively simple task it is solving, and its specification has evolved over decades, apparently trying to address various other semi-related problems, such as providing a general Interface Description Language.

Which is to say, ASN is probably not what you are looking for. So why implement it?

Developed in the context of the telecom industry around 30 years ago, modified several times after that and apparently suffering from a lack of a coherent goal, by the early 90s ASN was still probably the only universal, machine- and architecture-independent external data representation.

So it came easily to hand around the time RSA Security started publishing its series of PKCS standards, aimed at the standardization of cryptographic material exchange. RSA keys and digital signatures are often exchanged ASN-encoded.

At roughly the same time, ITU-T started publishing the X.500 series of standards which aimed to provide a comprehensive directory service. Much of this work ended up as LDAP, but one little bit stands out in particular: the X.509 PKI certificate.

So a few years later, when Netscape tried to build an authenticated and confidential layer to tunnel HTTP through, they based it on -- amongst other things -- X.509 certificates. Their work went through several revisions as SSL and was finally standardized as TLS. Modern TLS still requires X.509.

Thus, even though TLS uses ASN only for encoding certificates (and the odd PKCS1 signature), every implementation needs to know how to deal with ASN. In fact, many other general cryptographic libraries also need to deal with ASN, as various PKCS standards mandate ASN as the encoding for exchange of cryptographic material.

The grammar of the grammar

As its name implies, ASN was meant to be used with a specialized compiler. ASN is really a standard for writing down abstract syntaxes, and ASN compilers provided with the target encoding will generate code in your programming language of choice that, when invoked, parses to or serializes from ASN.

As long as your programming language of choice is C, C++, Java or C#, obviously -- there doesn't seem to be one freely available that targets OCaml. In any case, generating code for such a high-level language feels wrong somehow. In its effort to be language-neutral, ASN needs to deal with things like modules, abstraction and composition. At this point, most functional programmers reading this are screaming: "I already have a language that can deal with modules, abstraction and composition perfectly well!"

So we're left with implementing ASN in OCaml.

One strategy is to provide utility functions for parsing elements of ASN and simply invoke them in the appropriate order, as imposed by the target grammar. This amounts to hand-writing the parser and is what TLS libraries in C typically do.

As of release 1.3.7, PolarSSL includes ~7,500 lines of rather beautifully written C, that implement a specialized parser for dealing with X.509. OpenSSL's libcrypto contains ~50,000 lines of C in its 'asn1', 'x509' and 'x509v3' directories, and primarily deals with X.509 specifically as required by TLS.

In both cases, low-level control flow is intertwined with the parsing logic and, above the ASN parsing level, the code that deals with interpreting the ASN structure is not particularly concise. It is certainly a far cry from the (relatively) simple grammar description ASN itself provides.

Since in BER every value fully describes itself, another strategy is to parse the input stream without reference to the grammar. This produces a value that belongs to the general type of all ASN-encoded trees, after which we need to process the structure according to the grammar. This is similar to a common treatment of JSON or XML, where one decouples parsing of bytes from the higher-level concerns about the actual structure contained therein. The problem here is that either the downstream client of such a parser needs to constantly re-check whether the parts of the structure it's interacting with are really formed according to the grammar (probably leading to a tedium of pattern-matches), or we have to turn around and solve the parsing problem again, mapping the uni-typed contents of a message to the actual, statically known structure we require the message to have.

Surely we can do better?

LAMBDA: The Ultimate Declarative

Again, ASN is a language with a number of built-in primitives, a few combining constructs, (recursive) name-binding and a module system. Our target language is a language with a perfectly good module system and it can certainly express combining constructs. It includes an abstraction mechanism arguably far simpler and easier to use than those of ASN, namely, functions. And the OCaml compilers can already parse OCaml sources. So why not just reuse this machinery?

The idea is familiar. Creating embedded languages for highly declarative descriptions within narrowly defined problem spaces is the staple of functional programming. In particular, combinatory parsing has been known, studied and used for decades.

However, we also have to diverge from traditional parser combinators in two major ways. Firstly, a single grammar expression needs to be able to generate different concrete parsers, corresponding to different ASN encodings. More importantly, we desire our grammar descriptions to act bidirectionally, producing both parsers and complementary deserializers.

The second point severely restricts the signatures we can support. The usual monadic parsers are off the table because the expression such as:

( (pa : a t) >>= fun (a : a) ->
  (pb : b t) >>= fun (b : b) ->
  return (b, b, a) ) : (b * b * a) t

... "hides" parts of the parser inside the closures, especially the method of mapping the parsed values into the output values, and can not be run "in reverse" [1].

We have a similar problem with applicative functors:

( (fun a b -> (b, b, a))
  <$> (pa : a t)
  <*> (pb : b t) ) : (b * b * a) t

(Given the usual <$> : ('a -> 'b) -> 'a t -> 'b t and <*> : ('a -> 'b) t -> 'a t -> 'b t.) Although the elements of ASN syntax are now exposed, the process of going from intermediate parsing results to the result of the whole is still not accessible.

Fortunately, due to the regular structure of ASN, we don't really need the full expressive power of monadic parsing. The only occurrence of sequential parsing is within SEQUENCE and related constructs, and we don't need look-ahead. All we need to do is provide a few specialized combinators to handle those cases -- combinators the likes of which would be derived in a more typical setting.

So if we imagine we had a few values, like:

val gen_time : gen_time t
val utc_time : utc_time t
val choice   : 'a t -> 'b t -> ('a, 'b) choice t
val sequence : 'a t -> 'b t -> ('a * 'b) t

Assuming appropriate OCaml types gen_time and utc_time that reflect their ASN counterparts, and a simple sum type choice, we could express the Validity grammar above using:

type time = (gen_time, utc_time) choice
let time     : time t          = choice gen_time utc_time
let validity : (time * time) t = sequence time time

In fact, ASN maps quite well to algebraic data types. Its SEQUENCE corresponds to n-ary products and CHOICE to sums. ASN SET is a lot like SEQUENCE, except the elements can come in any order; and SEQUENCE_OF and SET_OF are just lifting an 'a-grammar into an 'a list-grammar.

A small wrinkle is that SEQUENCE allows for more contextual information on its components (so does CHOICE in reality, but we ignore that): elements can carry labels (which are not used for parsing) and can be marked as optional. So instead of working directly on the grammars, our sequence must work on their annotated versions. A second wrinkle is the arity of the sequence combinator.

Thus we introduce the type of annotated grammars, 'a element, which corresponds to one ,-delimited syntactic element in ASN's own SEQUENCE grammar, and the type 'a sequence, which describes the entire contents ({ ... }) of a SEQUENCE definition:

val required : 'a t -> 'a element
val optional : 'a t -> 'a option element
val ( -@ )   : 'a element -> 'b element -> ('a * 'b) sequence
val ( @ )    : 'a element -> 'a sequence -> ('a * 'b) sequence
val sequence : 'a sequence -> 'a t

The following are then equivalent:

Triple ::= SEQUENCE {
let triple : (int * (bool * bool option)) t =
  sequence (
      required int
    @ required bool
   -@ optional bool

We can also re-introduce functions, but in a controlled manner:

val map : ('a -> 'b) -> ('b -> 'a) -> 'a t -> 'b t

Keeping in line with the general theme of bidirectionality, we require functions to come in pairs. The deceptively called map could also be called iso, and comes with a nice property: if the two functions are truly inverses, the serialization process is fully reversible, and so is parsing, under single-representation encodings (DER)!

ASTs of ASNs

To go that last mile, we should probably also implement what we discussed.

Traditional parser combinators look a little like this:

type 'a p = string -> 'a * string

let bool : bool p = fun str -> (s.[0] <> "\000", tail_of_string str)

Usually, the values inhabiting the parser type are the actual parsing functions, and their composition directly produces larger parsing functions. We would probably need to represent them with 'a p * 'a s, pairs of a parser and its inverse, but the same general idea applies.

Nevertheless, we don't want to do this. The grammars need to support more than one concrete parser/serializer, and composing what is common between them and extracting out what is not would probably turn into a tangled mess. That is one reason. The other is that if we encode the grammar purely as (non-function) value, we can traverse it for various other purposes.

So we turn from what is sometimes called "shallow embedding" to "deep embedding" and try to represent the grammar purely as an algebraic data type.

Let's try to encode the parser for bools, boolean : bool t:

type 'a t =
  | Bool

let boolean : bool t = Bool

Unfortunately our constructor is fully polymorphic, of type 'a. 'a t. We can constrain it for the users, but once we traverse it there is nothing left to prove its intended association with booleans!

Fortunately, starting with the release of OCaml 4.00.0, OCaml joined the ranks of languages equipped with what is probably the supreme tool of deep embedding, GADTs. Using them, we can do things like:

type _ t =
  | Bool   : bool t
  | Pair   : ('a t * 'b t) -> ('a * 'b) t
  | Choice : ('a t * 'b t) -> ('a, 'b) choice t

In fact, this is very close to how the library is actually implemented.

There is only one thing left to worry about: ASN definitions can be recursive. We might try something like:

let rec list = choice null (pair int list)

But this won't work. Being just trees of applications, our definitions never contain statically constructive parts -- this expression could never terminate in a strict language.

We can get around that by wrapping grammars in Lazy.t (or just closures), but this would be too awkward to use. Like many other similar libraries, we need to provide a fixpoint combinator:

val fix : ('a t -> 'a t) -> 'a t

And get to write:

let list = fix @@ fun list -> choice null (pair int list)

This introduces a small problem. So far we simply reused binding inherited from OCaml without ever worrying about identifiers and references, but with a fixpoint, the grammar encodings need to be able to somehow express a cycle.

Borrowing an idea from higher-order abstract syntax, we can represent the entire fixpoint node using exactly the function provided to define it, re-using OCaml's own binding and identifier resolution:

type _ t =
  | Fix : ('a t -> 'a t) -> 'a t

This treatment completely sidesteps the problems with variables. We need no binding environments or De Brujin indices, and need not care about the desired scoping semantics. A little trade-off is that with this simple encoding it becomes more difficult to track cycles (when traversing the AST, if we keep applying a Fix node to itself while descending into it, it looks like an infinite tree), but with a little opportunistic caching it all plays out well [2].

The parser and serializer proper then emerge as interpreters for this little language of typed trees, traversing them with an input string, and parsing it in a fully type-safe manner.

How does it play out?

The entire ASN library comes down to ~1,700 lines of OCaml, with around ~1,100 more in tests, giving a mostly-complete treatment of BER and DER.

Its main use so far is in the context of the X.509 library (discussed yesterday). It allowed the grammar of certificates and RSA keys, together with a number of transformations from the raw types to more pleasant, externally facing ones, to be written in ~900 lines of OCaml. And the code looks a lot like the actual standards the grammars were taken from -- the fragment from the beginning of this article becomes:

let unique_identifier = bit_string_cs

let time =
  map (function `C1 t -> t | `C2 t -> t) (fun t -> `C2 t)
      (choice2 utc_time generalized_time)

let validity =
    (required ~label:"not before" time)
    (required ~label:"not after"  time)

We added ~label to 'a element-forming injections, and have:

val choice2 : 'a t -> 'b t -> [ `C1 of 'a | `C2 of 'b ] t

To get a sense of how the resulting system eases the translation of standardized ASN grammars into working code, it is particularly instructive to compare these two definitions.

Reversibility was a major simplifying factor during development. Since the grammars are traversable, it is easy to generate their random inhabitants, encode them, parse the result and verify the reversibility still holds. This can't help convince us the parsing/serializing pair is actually correct with respect to ASN, but it gives a simple tool to generate large amounts of test cases and convince us that that pair is equivalent. A number of hand-written cases then check the conformance to the actual ASN.

As for security, there were two concerns we were aware of. There is a history of catastrophic buffer overruns in some ASN.1 implementations, but -- assuming our compiler and runtime system are correct -- we are immune to these as we are subject to bounds-checking. And there are some documented problems with security of X.509 certificate verification due to overflows of numbers in ASN OID types, which we explicitly guard against.

You can check our security status on our issue tracker.


  1. In fact, the problem with embedding functions in combinator languages, and the fact that in a functional language it is not possible to extract information from a function other than by applying it, was discussed more than a decade ago. Such discussions led to the development of Arrows, amongst other things.

  2. Actually, a version of the library used the more proper encoding to be able to inject results of reducing referred-to parts of the AST into the referring sites directly, roughly like Fix : ('r -> ('a, 'r) t) -> ('a, 'r) t. This approach was abandoned because terms need to be polymorphic in 'r, and this becomes impossible to hide from the user of the library, creating unwelcome noise.

Posts in this TLS series:


OCaml-TLS: Adventures in X.509 certificate parsing and validation

By Hannes Mehnert

This is the third in a series of posts that introduce new libraries for a pure OCaml implementation of TLS. You might like to begin with the introduction.

The problem of authentication

The authenticity of the remote server needs to be verified while establishing a secure connection to it, or else an attacker (MITM) between the client and the server can eavesdrop on the transmitted data. To the best of our knowledge, authentication cannot be done solely in-band, but needs external infrastructure. The most common methods used in practice rely on public key encryption.

Web of trust (used by OpenPGP) is a decentralised public key infrastructure. It relies on out-of-band verification of public keys and transitivity of trust. If Bob signed Alice's public key, and Charlie trusts Bob (and signed his public key), then Charlie can trust that Alice's public key is hers.

Public key infrastructure (used by TLS) relies on trust anchors which are communicated out-of-band (e.g. distributed with the client software). In order to authenticate a server, a chain of trust between a trust anchor and the server certificate (public key) is established. Only those clients which have the trust anchor deployed can verify the authenticity of the server.

X.509 public key infrastructure

X.509 is an ITU standard for a public key infrastructure, developed in 1988. Amongst other things, it specifies the format of certificates, their attributes, revocation lists, and a path validation algorithm. X.509 certificates are encoded using abstract syntax notation one (ASN.1).

A certificate contains a public key, a subject (server name), a validity period, a purpose (i.e. key usage), an issuer, and possibly other extensions. All components mentioned in the certificate are signed by an issuer.

A certificate authority (CA) receives a certificate signing request from a server operator. It verifies that this signing request is legitimate (e.g. requested server name is owned by the server operator) and signs the request. The CA certificate must be trusted by all potential clients. A CA can also issue intermediate CA certificates, which are allowed to sign certificates.

When a server certificate or intermediate CA certificate is compromised, the CA publishes this certificate in its certificate revocation list (CRL), which each client should poll periodically.

The following certificates are exchanged before a TLS session:

  • CA -> Client: CA certificate, installed as trust anchor on the client
  • Server -> CA: certificate request, to be signed by the CA
  • CA -> Server: signed server certificate

During the TLS handshake the server sends the certificate chain to the client. When a client wants to verify a certificate, it has to verify the signatures of the entire chain, and find a trust anchor which signed the outermost certificate. Further constraints, such as the maximum chain length and the validity period, are checked as well. Finally, the server name in the server certificate is checked to match the expected identity. For an example, you can see the sequence diagram of the TLS handshake your browser makes when you visit our demonstration server.

Example code for verification

OpenSSL implements RFC5280 path validation, but there is no implementation to validate the identity of a certificate. This has to be implemented by each client, which is rather complex (e.g. in libfetch it spans over more than 300 lines). A client of the ocaml-x509 library (such as our http-client) has to write only two lines of code:

lwt authenticator = X509_lwt.authenticator (`Ca_dir ca_cert_dir) in
lwt (ic, oc) =
    (Tls.Config.client_exn ~authenticator ())
    (host, port)

The authenticator uses the default directory where trust anchors are stored ('ca_cert_dir'), and this authenticator is passed to the 'connect_ext' function. This initiates the TLS handshake, and passes the trust anchors and the hostname to the TLS library.

During the client handshake when the certificate chain is received by the server, the given authenticator and hostname are used to authenticate the certificate chain (in 'validate_chain'):

 X509.Authenticator.authenticate ?host:server_name authenticator stack
 | `Fail SelfSigned         -> fail Packet.UNKNOWN_CA
 | `Fail NoTrustAnchor      -> fail Packet.UNKNOWN_CA
 | `Fail CertificateExpired -> fail Packet.CERTIFICATE_EXPIRED
 | `Fail _                  -> fail Packet.BAD_CERTIFICATE
 | `Ok                      -> return server_cert

Internally, ocaml-x509 extracts the hostname list from a certificate in 'cert_hostnames', and the wildcard or strict matcher compares it to the input. In total, this is less than 50 lines of pure OCaml code.

Problems in X.509 verification

Several weaknesses in the verification of X.509 certificates have been discovered, ranging from cryptographic attacks due to collisions in hash algorithms (practical) over misinterpretation of the name in the certificate (a C string is terminated by a null byte), and treating X.509 version 1 certificates always as a trust anchor in GnuTLS.

An empirical study of software that does certificate verification showed that badly designed APIs are the root cause of vulnerabilities in this area. They tested various implementations by using a list of certificates, which did not form a chain, and would not authenticate due to being self-signed, or carrying a different server name.

Another recent empirical study (Frankencert) generated random certificates and validated these with various stacks. They found lots of small issues in nearly all certificate verification stacks.

Our implementation mitigates against some of the known attacks: we require a complete valid chain, check the extensions of a certificate, and implement hostname checking as specified in RFC6125. We have a test suite with over 3200 tests and multiple CAs. We do not yet discard certificates which use MD5 as hash algorithm. Our TLS stack requires certificates to have at least 1024 bit RSA keys.

X.509 library internals

The x509 library uses asn-combinators to parse X.509 certificates and the nocrypto library for signature verification (which we wrote about previously). At the moment we do not yet expose certificate builders from the library, but focus on certificate parsing and certificate authentication.

The x509 module provides modules which parse PEM-encoded (pem) certificates (Cert) and private keys (Pk), and an authenticator module (Authenticators).

So far we have two authenticators implemented:

  • 'chain_of_trust', which implements the basic path validation algorithm from RFC5280 (section 6) and the hostname validation from RFC6125. To construct such an authenticator, a timestamp and a list of trust anchors is needed.
  • 'null', which always returns success.

The method 'authenticate', to be called when a certificate stack should be verified, receives an authenticator, a hostname and the certificate stack. It returns either Ok or Fail.

Our certificate type is very similar to the described structure in the RFC:

type tBSCertificate = {
  version    : [ `V1 | `V2 | `V3 ] ;
  serial     : Z.t ;
  signature  : Algorithm.t ;
  issuer     : Name.dn ;
  validity   : Time.t * Time.t ;
  subject    : Name.dn ;
  pk_info    : PK.t ;
  issuer_id  : Cstruct.t option ;
  subject_id : Cstruct.t option ;
  extensions : (bool * Extension.t) list

type certificate = {
  tbs_cert       : tBSCertificate ;
  signature_algo : Algorithm.t ;
  signature_val  : Cstruct.t

The certificate itself wraps the to be signed part ('tBSCertificate'), the used signature algorithm, and the actual signature. It consists of a version, serial number, issuer, validity, subject, public key information, optional issuer and subject identifiers, and a list of extensions -- only version 3 certificates may have extensions.

The 'certificate' module implements the actual authentication of certificates, and provides some useful getters such as 'cert_type', 'cert_usage', and 'cert_extended_usage'. The main entry for authentication is 'verify_chain_of_trust', which checks correct signatures of the chain, extensions and validity of each certificate, and the hostname of the server certificate.

The grammar of X.509 certificates is developed in the 'asn_grammars' module, and the object identifiers are gathered in the 'registry' module.

Implementation of certificate verification

We provide the function 'valid_cas', which takes a timestamp and a list of certificate authorities. Each certificate authority is checked to be valid, self-signed, correctly signed, and having proper X.509 v3 extensions. As mentioned above, version 1 and version 2 certificates do not contain extensions. For a version 3 certificate, 'validate_ca_extensions' is called: The basic constraints extensions must be present, and its value must be true. Also, key usage must be present and the certificate must be allowed to sign certificates. Finally, we reject the certificate if there is any extension marked critical, apart from the two mentioned above.

When we have a list of validated CA certificates, we can use these to verify the chain of trust, which gets a hostname, a timestamp, a list of trust anchors and a certificate chain as input. It first checks that the server certificate is valid, the validity of the intermediate certificates, and that the chain is complete (the pathlen constraint is not validated) and rooted in a trust anchor. A server certificate is valid if the validity period matches the current timestamp, the given hostname matches its subject alternative name extension or common name (might be wildcard or strict matching, RFC6125), and it does not have a basic constraints extension which value is true.

Current status of ocaml-x509

We currently support only RSA certificates. We do not check revocation lists or use the online certificate status protocol (OCSP). Our implementation does not handle name constraints and policies. However, if any of these extensions is marked critical, we refuse to validate the chain. To keep our main authentication free of side-effects, it currently uses the timestamp when the authenticator was created rather than when it is used (this isn't a problem if lifetime of the OCaml-TLS process is comparatively short, as in the worst case the lifetime of the certificates can be extended by the lifetime of the process).

We invite people to read through the certificate verification and the ASN.1 parsing. We welcome discussion on the mirage-devel mailing list and bug reports on the GitHub issue tracker.

Posts in this TLS series:


OCaml-TLS: building the nocrypto library core

By David Kaloper

This is the second in a series of posts that introduce new libraries for a pure OCaml implementation of TLS. You might like to begin with the introduction.

What is nocrypto?

nocrypto is the small cryptographic library behind the ocaml-tls project. It is built to be straightforward to use, adhere to functional programming principles and able to run in a Xen-based unikernel. Its major use-case is ocaml-tls, which we announced yesterday, but we do intend to provide sufficient features for it to be more widely applicable.

"Wait, you mean you wrote your own crypto library?"

"Never write your own crypto"

Everybody seems to recognize that cryptography is horribly difficult. Building cryptography, it is all too easy to fall off the deep end and end up needing to make decisions only a few, select specialists can make. Worse, any mistake is difficult to uncover but completely compromises the security of the system. Or in Bruce Schneier's words:

Building a secure cryptographic system is easy to do badly, and very difficult to do well. Unfortunately, most people can't tell the difference. In other areas of computer science, functionality serves to differentiate the good from the bad: a good compression algorithm will work better than a bad one; a bad compression program will look worse in feature-comparison charts. Cryptography is different. Just because an encryption program works doesn't mean it is secure.

Obviously, it would be far wiser not to attempt to do this and instead reuse good, proven work done by others. And with the wealth of free cryptographic libraries around, one gets to take their pick.

So to begin with, we turned to cryptokit, the more-or-less standard cryptographic library in the OCaml world. It has a decent coverage of the basics: some stream ciphers (ARC4), some block ciphers (AES, 3DES and Blowfish) the core hashes (MD5, SHA, the SHA2 family and RIPEMD) and the public-key primitives (Diffie-Hellman and RSA). It is also designed with composability in mind, exposing various elements as stream-transforming objects that can be combined on top of one another.

Unfortunately, its API was a little difficult to use. Suppose you have a secret key, an IV and want to use AES-128 in CBC mode to encrypt a bit of data. You do it like this:

let key = "abcd1234abcd1234"
and iv  = "1234abcd1234abcd"
and msg = "fire the missile"

let aes     = new Cryptokit.Block.aes_encrypt key
let aes_cbc = new Cryptokit.Block.cbc_encrypt ~iv aes

let cip =
  let size =
    int_of_float (ceil (float String.(length msg) /. 16.) *. 16.) in
  String.create size

let () = aes_cbc#transform msg 0 cip 0

At this point, cip contains our secret message. This being CBC, both msg and the string the output will be written into (cip) need to have a size that is a multiple of the underlying block size. If they do not, bad things will happen -- silently.

There is also the curious case of hashing-object states:

let md5 = Cryptokit.Hash.md5 ()

let s1 = Cryptokit.hash_string md5 "bacon"
let s2 = Cryptokit.hash_string md5 "bacon"
let s3 = Cryptokit.hash_string md5 "bacon"

  s1 = "x\019%\142\248\198\1822\221\232\204\128\246\189\166/"
  s2 = "'\\F\017\234\172\196\024\142\255\161\145o\142\128\197"
  s3 = "'\\F\017\234\172\196\024\142\255\161\145o\142\128\197"

The error here is to try and carry a single instantiated hashing object around, while trying to get hashes of distinct strings. But with the convergence after the second step, the semantics of the hashing object still remains unclear to us.

One can fairly easily overcome the API style mismatches by making a few specialized wrappers, of course, except for two major problems:

  • Cryptokit is pervasively stateful. While this is almost certainly a result of performance considerations combined with its goals of ease of compositionality, it directly clashes with the fundamental design property of the TLS library we wanted to use it in: our ocaml-tls library is stateless. We need to be able to represent the state the encryption engine is in as a value.

  • Cryptokit operates on strings. As a primary target of ocaml-tls was Mirage, and Mirage uses separate, non-managed regions of memory to store network data in, we need to be able to handle foreign-allocated storage. This means Bigarray (as exposed by Cstruct), and it seems just plain wrong to negate all the careful zero-copy architecture of the stack below by copying everything into and out of strings.

There are further problems. For example, Cryptokit makes no attempts to combat well-known timing vulnerabilities. It has no support for elliptic curves. And it depends on the system-provided random number generator, which does not exist when running in the context of a unikernel.

At this point, with the de facto choice off the table, it's probably worth thinking about writing OCaml bindings to a rock-solid cryptographic library written in C.

NaCl is a modern, well-regarded crypto implementation, created by a group of pretty famous and equally well-regarded cryptographers, and was the first choice. Or at least its more approachable and packageable fork was, which already had OCaml bindings. Unfortunately, NaCl provides a narrow selection of implementations of various cryptographic primitives, the ones its authors thought were best-of-breed (for example, the only symmetric ciphers it implements are (X-)Salsa and AES in CTR mode). And they are probably right (in some aspects they are certainly right), but NaCl is best used for implementations of newly-designed security protocols. It is simply too opinionated to support an old, standardized behemoth like TLS.

Then there is crypto, the library OpenSSL is built on top of. It is quite famous and provides optimized implementations of a wide range of cryptographic algorithms. It also contains upwards of 200,000 lines of C and a very large API footprint, and it's unclear whether it would be possible to run it in the unikernel context. Recently, the parent project it is embedded in has become highly suspect, with one high-profile vulnerability piling on top of another and at least two forks so far attempting to clean the code base. It just didn't feel like a healthy code base to build a new project on.

There are other free cryptographic libraries in C one could try to bind, but at a certain point we faced the question: is the work required to become intimately familiar with the nuances and the API of an existing code base, and create bindings for it in OCaml, really that much smaller than writing one from scratch? When using a full library one commits to its security decisions and starts depending on its authors' time to keep it up to date -- maybe this effort is better spent in writing one in the first place.

Tantalizingly, the length of the single OCaml source file in Cryptokit is 2260 lines.

Maybe if we made zero decisions ourselves, informed all our work by published literature and research, and wrote the bare minimum of code needed, it might not even be dead-wrong to do it ourselves?

And that is the basic design principle. Do nothing fancy. Do only documented things. Don't write too much code. Keep up to date with security research. Open up and ask people.

The anatomy of a simple crypto library

nocrypto uses bits of C, similarly to other cryptographic libraries written in high-level languages.

This was actually less of a performance concern, and more of a security one: for the low-level primitives which are tricky to implement and for which known, compact and widely used code already exists, the implementation is probably better reused. The major pitfall we hoped to avoid that way are side-channel attacks.

We use public domain (or BSD licenced) C sources for the simple cores of AES, 3DES, MD5, SHA and SHA2. The impact of errors in this code is constrained: they contain no recursion, and they perform no allocation, simply filling in caller-supplied fixed-size buffer by appropriate bytes.

The block implementations in C have a simple API that requires us to provide the input and output buffers and a key, writing the single encrypted (or decrypted) block of data into the buffer. Like this:

void rijndaelEncrypt(const unsigned long *rk, int nrounds,
  const unsigned char plaintext[16], unsigned char ciphertext[16]);

void rijndaelDecrypt(const unsigned long *rk, int nrounds,
  const unsigned char ciphertext[16], unsigned char plaintext[16]);

The hashes can initialize a provided buffer to serve as an empty accumulator, hash a single chunk of data into that buffer and convert its contents into a digest, which is written into a provided fixed buffer.

In other words, all the memory management happens exclusively in OCaml and all the buffers passed into the C layer are tracked by the garbage collector (GC).

Symmetric ciphers

So far, the only provided ciphers are AES, 3DES and ARC4, with ARC4 implemented purely in OCaml (and provided only for TLS compatibility and for testing).

AES and 3DES are based on core C code, on top of which we built some standard modes of operation in OCaml. At the moment we support ECB, CBC and CTR. There is also a nascent GCM implementation which is, at the time of writing, known not to be optimal and possibly prone to timing attacks, and which we are still working on.

The exposed API strives to be simple and value-oriented. Each mode of each cipher is packaged up as a module with a similar signature, with a pair of functions for encryption and decryption. Each of those essentially takes a key and a byte buffer and yields the resulting byte buffer, minimising hassle.

This is how you encrypt a message:

open Nocrypto.Block

let key = AES.CBC.of_secret Cstruct.(of_string "abcd1234abcd1234")
and iv  = Cstruct.of_string "1234abcd1234abcd"
and msg = Cstruct.of_string "fire the missile"

let { AES.CBC.message ; iv } = AES.CBC.encrypt ~key ~iv msg

The hashes implemented are just MD5, SHA and the SHA2 family. Mirroring the block ciphers, they are based on C cores, with the HMAC construction provided in OCaml. The API is similarly simple: each hash is a separate module with the same signature, providing a function that takes a byte buffer to its digest, together with several stateful operations for incremental computation of digests.

Of special note is that our current set of C sources will probably soon be replaced. AES uses code that is vulnerable to a timing attack, stemming from the fact that substitution tables are loaded into the CPU cache as-needed. The code does not take advantage of the AES-NI instructions present in modern CPUs that allow AES to be hardware-assisted. SHA and SHA2 cores turned out to be (comparatively) ill-performing, and static analysis already uncovered some potential memory issues, so we are looking for better implementations.

Public-key cryptography

Bignum arithmetic is provided by the excellent zarith library, which in turn uses GMP. This might create some portability problems later on, but as GMP is widely used and well rounded code base which also includes some of the needed auxiliary number-theoretical functions (its slightly extended Miller-Rabin probabilistic primality test and the fast next-prime-scanning function), it seemed like a much saner choice than redoing it from scratch.

The RSA module provides the basics: raw encryption and decryption, PKCS1-padded versions of the same operations, and PKCS1 signing and signature verification. It can generate RSA keys, which it does simply by finding two large primes, in line with Rivest's own recommendation.

Notably, RSA implements the standard blinding technique which can mitigate some side-channel attacks, such as timing or acoustic cryptanalysis. It seems to foil even stronger, cache eviction based attacks, but as of now, we are not yet completely sure.

The Diffie-Hellman module is also relatively basic. We implement some widely recommended checks on the incoming public key to mitigate some possible MITM attacks, the module can generate strong DH groups (using safe primes) with guaranteed large prime-order subgroup, and we provide a catalogue of published DH groups ready for use.


Random number generation used to be a chronically overlooked part of cryptographic libraries, so much so that nowadays one of the first questions about a crypto library is, indeed, "Where does it get randomness from?"

It's an important question. A cryptographic system needs unpredictability in many places, and violating this causes catastrophic failures.

nocrypto contains its own implementation of Fortuna. Like Yarrow, Fortuna uses a strong block cipher in CTR mode (AES in our case) to produce the pseudo-random stream, a technique that is considered as unbreakable as the underlying cipher.

The stream is both self-rekeyed, and rekeyed with the entropy gathered into its accumulator pool. Unlike the earlier designs, however, Fortuna is built without entropy estimators, which usually help the PRNG decide when to actually convert the contents of an entropy pool into the new internal state. Instead, Fortuna uses a design where the pools are fed round-robin, but activated with an exponential backoff. There is recent research showing this design is essentially sound: after a state compromise, Fortuna wastes no more than a constant factor of incoming entropy -- whatever the amount of entropy is -- before coming back to an unpredictable state. The resulting design is both simple, and robust in terms of its usage of environmental entropy.

The above paper also suggests a slight improvement to the accumulator regime, yielding a factor-of-2 improvement in entropy usage over the original. We still haven't implemented this, but certainly intend to.

A PRNG needs to be fed with some actual entropy to be able to produce unpredictable streams. The library itself contains no provisions for doing this and its PRNG needs to be fed by the user before any output can be produced. We are working with the Mirage team on exposing environmental entropy sources and connecting them to our implementation of Fortuna.

Above & beyond

nocrypto is still very small, providing the bare minimum cryptographic services to support TLS and related X.509 certificate operations. One of the goals is to flesh it out a bit, adding some more widely deployed algorithms, in hopes of making it more broadly usable.

There are several specific problems with the library at this stage:

C code - As mentioned, we are seeking to replace some of the C code we use. The hash cores are underperforming by about a factor of 2 compared to some other implementations. AES implementation is on one hand vulnerable to a timing attack and, on the other hand, we'd like to make use of hardware acceleration for this workhorse primitive -- without it we lose about an order of magnitude of performance.

Several options were explored, ranging from looking into the murky waters of OpenSSL and trying to exploit their heavily optimized primitives, to bringing AES-NI into OCaml and redoing AES in OCaml. At this point, it is not clear which path we'll take.

ECC - Looking further, the library still lacks support for elliptic curve cryptography and we have several options for solving this. Since it is used by TLS, ECC is probably the missing feature we will concentrate on first.

Entropy on Xen - The entropy gathering on Xen is incomplete. The current prototype uses current time as the random seed and the effort to expose noisier sources like interrupt timings and the RNG from dom0's kernel is still ongoing. Dave Scott, for example, has submitted patches to upstream Xen to make it easier to establish low-bandwidth channels to supplies guest VMs with strong entropy from a privileged domain that has access to physical devices and hence high-quality entropy sources.

GC timing attacks? - There is the question of GC and timing attacks: whether doing cryptography in a high-level language opens up a completely new surface for timing attacks, given that GC runs are very visible in the timing profile. The basic approach is to leave the core routines which we know are potentially timing-sensitive (like AES) and for which we don't have explicit timing mitigations (like RSA) to C, and invoke them atomically from the perspective of the GC. So far, it's an open question whether the constructions built on top of them expose further side-channels.

Still, we believe that the whole package is a pleasant library to work with. Its simplicity contributes to the comparative simplicity of the entire TLS library, and we are actively seeking input on areas that need further improvement. Although we are obviously biased, we believe it is the best cryptographic base library available for this project, and it might be equally suited for your next project too!

We are striving to be open about the current security status of our code. You are free to check out our issue tracker and invited to contribute comments, ideas, and especially audits and code.

Posts in this TLS series:


Introducing transport layer security (TLS) in pure OCaml

By Hannes Mehnert, David Kaloper

We announce a beta release of ocaml-tls, a clean-slate implementation of Transport Layer Security (TLS) in OCaml.

What is TLS?

Transport Layer Security (TLS) is probably the most widely deployed security protocol on the Internet. It provides communication privacy to prevent eavesdropping, tampering, and message forgery. Furthermore, it optionally provides authentication of the involved endpoints. TLS is commonly deployed for securing web services (HTTPS), emails, virtual private networks, and wireless networks.

TLS uses asymmetric cryptography to exchange a symmetric key, and optionally authenticate (using X.509) either or both endpoints. It provides algorithmic agility, which means that the key exchange method, symmetric encryption algorithm, and hash algorithm are negotiated.

TLS in OCaml

Our implementation ocaml-tls is already able to interoperate with existing TLS implementations, and supports several important TLS extensions such as server name indication (RFC4366, enabling virtual hosting) and secure renegotiation (RFC5746).

Our demonstration server runs ocaml-tls and renders exchanged TLS messages in nearly real time by receiving a trace of the TLS session setup. If you encounter any problems, please give us feedback.

ocaml-tls and all dependent libraries are available via OPAM (opam install tls). The source is available under a BSD license. We are primarily working towards completeness of protocol features, such as client authentication, session resumption, elliptic curve and GCM cipher suites, and have not yet optimised for performance.

ocaml-tls depends on the following independent libraries: ocaml-nocrypto implements the cryptographic primitives, ocaml-asn1-combinators provides ASN.1 parsers/unparsers, and ocaml-x509 implements the X509 grammar and certificate validation (RFC5280). ocaml-tls implements TLS (1.0, 1.1 and 1.2; RFC2246, RFC4346, RFC5246).

We invite the community to audit and run our code, and we are particularly interested in discussion of our APIs. Please use the mirage-devel mailing list for discussions.

Please be aware that this release is a beta and is missing external code audits. It is not yet intended for use in any security critical applications.

In our issue tracker we transparently document known attacks against TLS and our mitigations (checked and unchecked). We have not yet implemented mitigations against either the Lucky13 timing attack or traffic analysis (e.g. length-hiding padding).

Trusted code base

Designed to run on Mirage, the trusted code base of ocaml-tls is small. It includes the libraries already mentioned, `ocaml-tls`, `ocaml-asn-combinators`, `ocaml-x509`, and `ocaml-nocrypto` (which uses C implementations of block ciphers and hash algorithms). For arbitrary precision integers needed in asymmetric cryptography, we rely on `zarith`, which wraps `libgmp`. As underlying byte array structure we use `cstruct` (which uses OCaml Bigarray as storage).

We should also mention the OCaml runtime, the OCaml compiler, the operating system on which the source is compiled and the binary is executed, as well as the underlying hardware. Two effectful frontends for the pure TLS core are implemented, dealing with side-effects such as reading and writing from the network: Lwt_unix and Mirage, so applications can run directly as a Xen unikernel.

Why a new TLS implementation?

Update: Thanks to Frama-C guys for pointing out that CVE-2014-1266 and CVE-2014-0224 are not memory safety issues, but logic errors. This article previously stated otherwise.

There are only a few TLS implementations publicly available and most programming languages bind to OpenSSL, an open source implementation written in C. There are valid reasons to interface with an existing TLS library, rather than developing one from scratch, including protocol complexity and compatibility with different TLS versions and implementations. But from our perspective the disadvantage of most existing libraries is that they are written in C, leading to:

  • Memory safety issues, as recently observed by Heartbleed and GnuTLS session identifier memory corruption (CVE-2014-3466) bugs;
  • Control flow complexity (Apple's goto fail, CVE-2014-1266);
  • And difficulty in encoding state machines (OpenSSL change cipher suite attack, CVE-2014-0224).

Our main reasons for ocaml-tls are that OCaml is a modern functional language, which allows concise and declarative descriptions of the complex protocol logic and provides type safety and memory safety to help guard against programming errors. Its functional nature is extensively employed in our code: the core of the protocol is written in purely functional style, without any side effects.

Subsequent blog posts over the coming days will examine in more detail the design and implementation of the four libraries, as well as the security trade-offs and some TLS attacks and our mitigations against them. For now though, we invite you to try out our demonstration server running our stack over HTTPS. We're particularly interested in feedback on our issue tracker about clients that fail to connect, and any queries from anyone reviewing the source code of the constituent libraries.

Posts in this TLS series:


MirageOS 1.2 released and the 2.0 runup begins

By Anil Madhavapeddy

Summer is in full swing here in MirageOS HQ with torrential rainstorms, searing sunshine, and our OSCON 2014 talk rapidly approaching in just a few weeks. We've been steadily releasing point releases since the first release back in December, and today's MirageOS 1.2.0 is the last of the 1.x series. The main improvements are usability-oriented:

  • The Mirage frontend tool now generates a Makefile with a make depend target, instead of directly invoking OPAM as part of mirage configure. This greatly improves usability on slow platforms such as ARM, since the output of OPAM as it builds can be inspected more easily. Users will now need to run make depend to ensure they have the latest package set before building their unikernel.

  • Improve formatting of the mirage output, including pretty colours! This makes it easier to distinguish complex unikernel configurations that have lots of deployment options. The generated files are built more verbosely by default to facilitate debugging, and with debug symbols and backtraces enabled by default.

  • Added several device module types, including ENTROPY for random noise, FLOW for stream-oriented connections, and exposed the IPV4 device in the STACKV4 TCP/IP stack type.

  • Significant bugfixes in supporting libraries such as the TCP/IP stack (primarily thanks to Mindy Preston fuzz testing and finding some good zingers). There are too many library releases to list individually here, but you can browse the changelog for more details.

 Towards MirageOS 2.0

We've also been working hard on the MirageOS 2.x series, which introduces a number of new features and usability improvements that emerged from actually using the tools in practical projects. Since there have been so many new contributors recently, Amir Chaudhry is coordinating a series of blog posts in the runup to OSCON that explains the new work in depth. Once the release rush has subsided, we'll be working on integrating these posts into our documentation properly.

The new 2.0 features include the Irmin branch-consistent distributed storage library, the pure OCaml TLS stack, Xen/ARM support and the Conduit I/O subsystem for mapping names to connections. Also included in the blog series are some sample usecases on how these tie together for real applications (as a teaser, here's a video of Xen VMs booting using Irmin thanks to Dave Scott and Thomas Gazagnaire!)

Upcoming talks and tutorials

Richard Mortier and myself will be gallivanting around the world to deliver a few talks this summer:

  • The week of OSCON on July 20th-24th. Please get in touch via the conference website or a direct e-mail, or attend our talk on Thursday morning. There's a Real World OCaml book signing on Tuesday morning for the super keen as well.
  • The ECOOP summer school in beautiful Uppsala in Sweden on Weds 30th July.
  • I'll be presenting the Irmin and Xen integration at Xen Project Developer Summit in Chicago on Aug 18th (as part of LinuxCon North America). Mort and Mindy (no jokes please) will be joining the community panel about GSoC/OPW participation.

As always, if there are any particular topics you would like to see more on, then please comment on the tracking issue or get in touch directly. There will be a lot of releases coming out in the next few weeks (including a beta of the new version of OPAM, so bug reports are very much appreciated for those things that slip past Travis CI!


Welcome to the summer MirageOS hackers

By Anil Madhavapeddy

Following our participation in the Google Summer of Code program, we've now finalised selections. We've also got a number of other visitors joining us to hack on Mirage over the summer time, so here are introductions!

  • SSL support: Hannes Mehnert and David Kaloper have been working hard on a safe OCaml TLS implementation. They're going to hack on integrating it all into working under Xen so we can make HTTPS requests (and our Twitter bot will finally be able to tweet!). Both are also interested in formal verification of the result, and several loooong conversations with Peter Sewell will magically transform into machine specifications by summer's end, I'm reliably informed.
  • Cloud APIs: Jyotsna Prakash will spend her summer break as part of Google Summer of Code working on improving cloud provider APIs in OCaml (modelled from her notes on how the GitHub bindings are built). This will let the mirage command-line tool have much more natural integration with remote cloud providers for executing the unikernels straight from a command-line. If you see Jyotsna wandering around aimlessly muttering darkly about HTTP, JSON and REST, then the project is going well.
  • Network Stack fuzzing: Mindy Preston joins us for the summer after her Hacker School stay, courtesy of the OPW program. She's been delving into the network stack running on EC2 and figuring out how to debug issues when the unikernel is running a cloud far, far away (see the post series here: 1, 2, 3, 4).
  • Visualization: Daniel Buenzli returns to Cambridge this summer to continue his work on extremely succinctly named graphics libaries. His Vz, Vg and Gg libaries build a set of primitives for 2D graphics programming. Since the libraries compile to JavaScript, we're planning to use this as the basis for visualization of Mirage applications via a built-in webserver.
  • Modular implicits: Frederic Bour, author of the popular Merlin IDE tool is also in Cambridge this summer working on adding modular implicits to the core OCaml language. Taking inspiration from Modular Type-classes and Scala's implicits, modular implcits allow functions to take implicit module arguments which will be filled-in by the compiler by searching the environment for a module with the appropriate type. This enables ad-hoc polymorphism in a very similar way to Haskell's type classes.
  • Irmin storage algorithms: Benjamin Farinier (from ENS Lyon) and Matthieu Journault (from ENS Cachan) will work on datastructures for the Irmin storage system that the next version of Mirage will use. They'll be grabbing copies of the Okasaki classic text and porting some of them into a branch-consistent form.

Of course, work continues apace by the rest of the team as usual, with a steady stream of releases that are building up to some exciting new features. We'll be blogging about ARM support, PVHVM, Irmin storage and SSL integration just as soon as they're pushed into the stable branches. As always, get in touch via the IRC channel (#mirage on or the mailing lists with questions.


MirageOS is in Google Summer of Code 2014

By Anil Madhavapeddy

MirageOS will part of the Google Summer of Code 2014 program, thanks to the Xen Project's participation! It's been a few years since I've mentored for GSoc, but I still have fond memories of some great projects in the past (such as the legendary Quake testing we were forced to do for hours on end). I've already received a number of queries about this year's program from potential students, so here's a few things to note to become a successful applicant.

  • Students still need to apply and be accepted. Your chances of being selected are much higher if you demonstrate some participation and code contributions (even minor) before submitting an application. Thus, even if you don't have a copy of Xen around, roll up your sleeves and head over to the installation instructions.

  • Contributions do not have to be just code. They can be documentation, help with packaging, wiki posts about a particular use, or test cases to improve code coverage.

  • It's unlikely that we'll get students who are very familiar with both OCaml and Xen (if you are, definitely get in touch with us!). You should therefore look over the project ideas as a set of guidelines and not firm suggestions. If you have a particular thing you'd like to do with Mirage (for example, work on the JavaScript backend, an IPython interface or a particular protocol implementation such as XMPP, then that's fine. Just get in touch with us on the mailing lists or directly via e-mail, and we can work through them.

  • Under some circumstances, we can provide resources such as a login to a Xen machine, or delegated credits on a cloud provider. Don't let that stop you from applying for a project idea. In general though, it's best to only depend on your own computer resources if practical to do so.


MirageOS 1.1.0: the eat-your-own-dogfood release

By Anil Madhavapeddy

We've just released MirageOS 1.1.0 into OPAM. Once the live site updates, you should be able to run opam update -u and get the latest version. This release is the "eat our own dogfood" release; as I mentioned earlier in January, a number of the MirageOS developers have decided to shift our own personal homepages onto MirageOS. There's nothing better than using our own tools to find all the little annoyances and shortcomings, and so MirageOS 1.1.0 contains some significant usability and structural improvements for building unikernels.

Functional combinators to build device drivers

MirageOS separates the application logic from the concrete backend in use by writing the application as an OCaml functor that is parameterized over module types that represent the device driver signature. All of the module types used in MirageOS can be browsed in one source file.

In MirageOS 1.1.0, Thomas Gazagnaire implemented a a combinator library that makes it easy to separate the definition of application logic from the details of the device drivers that actually execute the code (be it a Unix binary or a dedicated Xen kernel). It lets us write code of this form (taken from mirage-skeleton/block):

let () =
  let main = foreign "Unikernel.Block_test" (console @-> block @-> job) in
  let img = block_of_file "disk.img" in
  register "block_test" [main $ default_console $ img]

In this configuration fragment, our unikernel is defined as a functor over a console and a block device by using console @-> block @-> job. We then define a concrete version of this job by applying the functor (using the $ combinator) to a default console and a file-backed disk image.

The combinator approach lets us express complex assemblies of device driver graphs by writing normal OCaml code, and the mirage command line tool parses this at build-time and generates a file that has all the functors applied to the right device drivers. Any mismatches in module signatures will result in a build error, thus helping to spot nonsensical combinations (such as using a Unix network socket in a Xen unikernel).

This new feature is walked through in the tutorial, which now walks you through several skeleton examples to explain all the different deployment scenarios. It's also followed by the website tutorial that explains how this website works, and how our Travis autodeployment throws the result onto the public Internet.

Who will win the race to get our website up and running first? Sadly for Anil, Mort is currently in the lead with an all-singing, all-dancing shiny new website. Will he finish in the lead though? Stay tuned!

Less magic in the build

Something that's more behind-the-scenes, but important for easier development, is a simplication in how we build libraries. In MirageOS 1.0, we had several packages that couldn't be simultaneously installed, as they had to be compiled in just the right order to ensure dependencies.

With MirageOS 1.1.0, this is all a thing of the past. All the libraries can be installed fully in parallel, including the network stack. The 1.1.0 TCP/IP stack is now built in the style of the venerable FoxNet network stack, and is parameterized across its network dependencies. This means that once can quickly assemble a custom network stack from modular components, such as this little fragment below from mirage-skeleton/ethifv4/:

module Main (C: CONSOLE) (N: NETWORK) = struct

  module E = Ethif.Make(N)
  module I = Ipv4.Make(E)
  module U = Udpv4.Make(I)
  module T = Tcpv4.Flow.Make(I)(OS.Time)(Clock)(Random)
  module D = Dhcp_clientv4.Make(C)(OS.Time)(Random)(E)(I)(U)

This functor stack starts with a NETWORK (i.e. Ethernet) device, and then applies functors until it ends up with a UDPv4, TCPv4 and DHCPv4 client. See the full file to see how the rest of the logic works, but this serves to illustrate how MirageOS makes it possible to build custom network stacks out of modular components. The functors also make it easier to embed the network stack in non-MirageOS applications, and the tcpip OPAM package installs pre-applied Unix versions for your toplevel convenience.

To show just how powerful the functor approach is, the same stack can also be mapped onto a version that uses kernel sockets simply by abstracting the lower-level components into an equivalent that uses the Unix kernel to provide the same functionality. We explain how to swap between these variants in the tutorials.

Lots of library releases

While doing the 1.1.0 release in January, we've also released quite a few libraries into OPAM. Here are some of the highlights.

Low-level libraries:

  • mstruct is a streaming layer for handling lists of memory buffers with a simpler read/write interface.
  • nbd is an implementation of the Network Block Device protocol for block drivers.

Networking and web libraries:

  • ipaddr now has IPv6 parsing support thanks to Hugo Heuzard and David Sheets. This is probably the hardest bit of adding IPv6 support to our network stack!
  • cowabloga is slowly emerging as a library to handle the details of rendering Zurb Foundation websites. It's still in active development, but being used for a few of our personal websites as well as this website.
  • cohttp has had several releases thanks to external contributions, particular from Rudy Grinberg who added s-expression support and several other improvements.
  • uri features performance improvements and the elimination of Scanf (considered rather slow by OCaml standards).
  • cow continues its impossible push to make coding HTML and CSS a pleasant experience, with better support for Markdown now.
  • The github bindings are now also in use as part of an experiment to make upstream OCaml development easier for newcomers, thanks to Gabriel Scherer.

Dave Scott led the splitting up of several low-level Xen libraries as part of the build simplication. These now compile on both Xen (using the direct hypercall interface) and Unix (using the dom0 /dev devices) where possible.

  • xen-evtchn for the event notification mechanism. There are a couple of wiki posts that explain how event channels and suspend/resume work in MirageOS/Xen guests.
  • xen-gnt for the grant table mechanism that controls inter-process memory.
  • The io-page library no longer needs Unix and Xen variants, as the interface has been standardized to work in both.

All of Dave's hacking on Xen device drivers is showcased in this xen-disk wiki post that explains how you can synthesize your own virtual disk backends using MirageOS. Xen uses a split device model, and now MirageOS lets us build backend device drivers that service VMs as well as the frontends!

Last, but not least, Thomas Gazagnaire has been building a brand new storage system for MirageOS guests that uses git-style branches under the hood to help coordinate clusters of unikernels. We'll talk about how this works in a future update, but there are some cool libraries and prototypes available on OPAM for the curious.

  • lazy-trie is a lazy version of the Trie data structure, useful for exposing Git graphs.
  • git is a now-fairly complete implementation of the Git protocol in pure OCaml, which can interoperate with normal Git servers via the ogit command-line tool that it installs.
  • irmin is the main library that abstracts Git DAGs into an OCaml programming API. The homepage has instructions on how to play with the command-line frontend to experiment with the database.
  • git2fat converts a Git checkout into a FAT block image, useful when bundling up unikernels.

We'd also like to thank several conference organizers for giving us the opportunity to demonstrate MirageOS. The talk video from QCon SF is now live, and we also had a great time at FOSDEM recently (summarized by Amir here). So lots of activities, and no doubt little bugs lurking in places (particularly around installation). As always, please do let us know of any problem by reporting bugs, or feel free to contact us via our e-mail lists or IRC. Next stop: our unikernel homepages!


Presenting Decks

By Richard Mortier

A few months ago, partly as a stunt, mostly because we could, Anil and I put together a presentation for OSCON'13 about Mirage in Mirage. That is, as a self-hosting Mirage web application that served up slides using RevealJS. It was a bit of a hack, but it was cool (we thought!) and it worked. Several more presentations were written and given this way, at venues ranging from the XenSummit 2013 to ACM FOCI 2013 to the Cambridge Computer Lab's MSc in Advanced Computer Science.

With the release of Mirage 1.0, CoHTTP, Cowabloga and the new Zurb Foundation based website, it was time to refresh them and as a little seasonal gift, give them a shiny new index with some actual CSS styling. So here they are, a set of presentations that have been given by various members of the Mirage team over the last 6 months or so. They cover a range of topics, from general introductions to the Xen roadmap to more detailed technical background. And, of course, as Mirage is under constant rapid development, some of the older content may already be outdated. But the code for the site itself serves as another example of a simple -- somewhat simpler than the Mirage website in fact -- Mirage web application.


MirageOS 1.0.3 released; tutorial on building this website available

By Anil Madhavapeddy

We've had a lot of people trying out MirageOS since the 1.0 release last week, and so we've been steadily cutting point releases and new libraries to OPAM as they're done. The most common build error by far has been people using outdated OPAM packages. Do make sure that you have at least OPAM 1.1 installed, and that you've run opam update -u to get the latest package lists from the package repository.

MirageOS 1.0.3 improves Xen configuration generation, cleans up HTTP support, and adds support for FAT filesystems. Here are some of the libraries we've released this week to go along with it:

  • mirage-www (update): the live website now runs on the 1.0 tools. Explanation of how to build it in various configurations is available here.
  • alcotest (new): a lightweight and colourful test framework built over oUnit. The interface is simpler to facilitate writing tests quickly, and it formats test results nicely.
  • mirage-block-xen.1.0.0 (new): is the stable release of the Xen Blkfront driver for block devices. The library supports both frontend and backend operation, but only the frontend is plumbed through to Mirage for now (although the backend can be manually configured).
  • mirage-block-unix.1.2.0 (update): fixed some concurrency bugs and added support for buffered I/O to improve performance.
  • fat-filesystem.0.10.0 (update): copies with more sector sizes, uses buffered I/O on Unix, and adds a KV_RO key/value interface as well as a more complicated filesystem one.
  • mirage-fs-unix.1.0.0 (update): implements the KV_RO signature as a passthrough to a Unix filesystem. This is convenient during development to avoid recompile cycles while changing data.
  • mirage-xen.1.0.0 (update): removed several distracting-but-harmless linker warnings about code bloat.
  • cohttp.0.9.14 (update): supports Server-Side Events via better channel flushing, has a complete set of HTTP codes autogenerated from and exposes a platform-independent Cohttp_lwt module.
  • cow.0.8.1 (update): switch to the Omd library for Markdown parsing, which is significantly more compatible with other parsers.
  • ezjsonm.0.2.0 (new): a combinator library to parse, select and manipulate JSON structures.
  • ezxmlm.1.0.0 (new): a combinator library to parse, select and transform XML tags and attributes.
  • mirage-http-xen and mirage-http-unix provide the HTTP drivers on top of Cohttp for MirageOS. Although they are very similar at the moment, they will diverge as the Unix variant gains options to use kernel sockets instead of only the network stack.

We're making great progress on moving our personal homepages over to MirageOS. The first two introductory wiki posts are also now available:

  • Building a hello world example takes you through the basic steps to build a Unix and Xen binary.
  • Building the MirageOS website lets you build this website with several variants, demonstrating the Unix passthrough filesystem, the OCaml FAT filesystem library, and how to attach a network stack to your application.

As always, please feel free to report any issues via the bug tracker and ask questions on the mailing list.


MirageOS 1.0: not just a hallucination!

By Anil Madhavapeddy

First: read the overview and technical background behind the project.

When we started hacking on MirageOS back in 2009, it started off looking like a conventional OS, except written in OCaml. The monolithic repository contained all the libraries and boot code, and exposed a big OS module for applications to use. We used this to do several fun tutorials at conferences such as ICFP/CUFP and get early feedback.

As development continued though, we started to understand what it is we were building: a "library operating system". As the number of libraries grew, putting everything into one repository just wasn't scaling, and it made it hard to work with third-party code. We spent some time developing tools to make Mirage fit into the broader OCaml ecosystem.

Three key things have emerged from this effort:

  • OPAM, a source-based package manager for OCaml. It supports multiple simultaneous compiler installations, flexible package constraints, and a Git-friendly development workflow. Since releasing 1.0 in March 2013 and 1.1 in October, the community has leapt in to contribute over 1800 packages in this short time. All of the Mirage libraries are now tracked using it, including the Xen libraries.
  • The build system for embedded programming (such as the Xen target) is a difficult one to get right. After several experiments, Mirage provides a single command-line tool that combines configuration directives (also written in OCaml) with OPAM to make building Xen unikernels as easy as Unix binaries.
  • All of the Mirage-compatible libraries satisfy a set of module type signatures in a single file. This is where Mirage lives up to its name: we've gone from the early monolithic repository to a single, standalone interface file that describes the interfaces. Of course, we also have libraries to go along with this signature, and they all live in the MirageOS GitHub organization.

With these components, I'm excited to announce that MirageOS 1.0 is finally ready to see the light of day! Since it consists of so many libraries, we've decided not to have a "big bang" release where we dump fifty complex libraries on the open-source community. Instead, we're going to spend the month of December writing a series of blog posts that explain how the core components work, leading up to several use cases:

  • The development team have all decided to shift our personal homepages to be Mirage kernels running on Xen as a little Christmas present to ourselves, so we'll work through that step-by-step how to build a dedicated unikernel and maintain and deploy it (spoiler: see this repo). This will culminate in a webservice that our colleagues at Horizon have been building using Android apps and an HTTP backend.
  • The XenServer crew at Citrix are using Mirage to build custom middlebox VMs such as block device caches.
  • For teaching purposes, the Cambridge Computer Lab team want a JavaScript backend, so we'll explain how to port Mirage to this target (which is rather different from either Unix or Xen, and serves to illustrate the portability of our approach).

How to get involved

Bear with us while we update all the documentation and start the blog posts off today (the final libraries for the 1.0 release are all being merged into OPAM while I write this, and the usually excellent Travis continuous integration system is down due to a bug on their side). I'll edit this post to contain links to the future posts as they happen.

Since we're now also a proud Xen and Linux Foundation incubator project, our mailing list is shifting to, and we very much welcome comments and feedback on our efforts over there. The #mirage channel on Libera Chat IRC is also growing increasingly popular, as is simply reporting issues on the main Mirage GitHub repository.

Several people have also commented that they want to learn OCaml properly to start using Mirage. I've just co-published an O'Reilly book called Real World OCaml that's available for free online and also as hardcopy/ebook. Our Cambridge colleague John Whittington has also written an excellent introductory text, and you can generally find more resources online. Feel free to ask beginner OCaml questions on our mailing lists and we'll help as best we can!


Introducing vchan

By Vincent Bernardoff

Editor: Note that some of the toolchain details of this blog post are now out-of-date with Mirage 1.1, so we will update this shortly.

Unless you are familiar with Xen's source code, there is little chance that you've ever heard of the vchan library or protocol. Documentation about it is very scarce: a description can be found on vchan's public header file, that I quote here for convenience:

Originally borrowed from the Qubes OS Project, this code (i.e. libvchan) has been substantially rewritten [...] This is a library for inter-domain communication. A standard Xen ring buffer is used, with a datagram-based interface built on top. The grant reference and event channels are shared in XenStore under a user-specified path.

This protocol uses shared memory for inter-domain communication, i.e. between two VMs residing in the same Xen host, and uses Xen's mechanisms -- more specifically, ring buffers and event channels -- in order to achieve its aims. Datagram-based interface simply means that the interface resembles UDP, although there is support for stream based communication (like TCP) as well.

Over the last two months or so, I worked on a pure OCaml implementation of this library, meaning that Mirage-based unikernels can now take full advantage of vchan to communicate with neighboring VMs! If your endpoint -- a Linux VM or another unikernel -- is on the same host, it is much faster and more efficient to use vchan rather than the network stack (although unfortunately, it is currently incompatible with existing programs written against the socket library under UNIX or the Flow module of Mirage, although this will improve). It also provides a higher level of security compared to network sockets as messages will never leave the host's shared memory.

Building the vchan echo domain

Provided that you have a Xen-enabled machine, do the following from dom0:

    opam install mirari mirage-xen mirage vchan

This will install the library and its dependencies. mirari is necessary to build the echo unikernel:

    git clone git://
    cd test
    mirari configure --xen --no-install
    mirari build --xen
    sudo mirari run --xen

This will boot a vchan echo domain for dom0, with connection parameters stored in xenstore at /local/domain/<domid>/data/vchan, where <domid> is the domain id of the vchan echo domain. The echo domain is simply an unikernel hosting a vchan server accepting connections from dom0, and echo'ing everything that is sent to it.

The command xl list will give you the domain id of the echo server.

Building the vchan CLI from Xen's sources

You can try it using a vchan client that can be found in Xen's sources at tools/libvchan: Just type make in this directory. It will compile the executable vchan-node2 that you can use to connect to our freshly created echo domain:

    ./vchan-node2 client <domid>/local/domain/<domid>/data/vchan

If everything goes well, what you type in there will be echoed.

You can obtain the full API documentation for ocaml-vchan by doing a cd ocaml-vchan && make doc. If you are doing network programming under UNIX, vchan's interface will not surprise you. If you are already using vchan for a C project, you will see that the OCaml API is nearly identical to what you are used to.

Please let us know if you use or plan to use this library in any way! If you need tremedous speed or more security, this might fit your needs.


MirageOS travels to OSCON'13: a trip report

By Richard Mortier

Now that Mirage OS is rapidly converging on a Developer Preview Release 1, we took it for a first public outing at OSCON'13, the O'Reilly Open Source Conference. OSCON is in its 15th year now, and is a meeting place for developers, business people and investors. It was a great opportunity to show MirageOS off to some of the movers and shakers in the OSS world.

Partly because MirageOS is about synthesising extremely specialised guest kernels from high-level code, and partly because both Anil and I are constitutionally incapable of taking the easy way out, we self-hosted the slide deck on Mirage: after some last-minute hacking -- on content not Mirage I should add! -- we built a self-contained unikernel of the talk.

This was what you might call a "full stack" presentation: the custom unikernel (flawlessly!) ran a type-safe network device driver, OCaml TCP/IP stack supporting an OCaml HTTP framework that served slides rendered using reveal.js. The slide deck, including the turbo-boosted screencast of the slide deck compilation, is hosted as another MirageOS virtual machine at We hope to add more slide decks there soon, including resurrecting the tutorial! The source code for all this is in the mirage-decks GitHub repo.

The Talk

The talk went down pretty well -- given we were in a graveyard slot on Friday after many people had left, attendance was fairly high (around 30-40), and the feedback scores have been positive (averaging 4.7/5) with comments including "excellent content and well done" and "one of the most excited projects I heard about" (though we are suspicious that just refers to Anil's usual high-energy presentation style...).

Probably the most interesting chat after the talk was with the Rust authors at Mozilla (@pcwalton and @brson) about combining the Mirage unikernel techniques with the Rust runtime. But perhaps the most surprising feedback was when Anil and I were stopped in the street while walking back from some well-earned sushi, by a cyclist who loudly declared that he'd really enjoyed the talk and thought it was a really exciting project -- never done something that achieved public acclaim from the streets before :)

Book Signing and

Anil also took some time to sit in a book signing for his forthcoming Real World OCaml O'Reilly book. This is really important to making OCaml easier to learn, especially given that all the Mirage libraries are using it. Most of the dev team (and especially thanks to Heidi Howard who bravely worked through really early alpha revisions) have been giving us feedback as the book is written, using the online commenting system.

The booth was also huge, and we spent quite a while plotting the forthcoming Mirage/Xen/ARM backend. We're pretty much just waiting for the Cubieboard2 kernel patches to be upstreamed (keep an eye here) so that we can boot Xen/ARM VMs on tiny ARM devices. There's a full report about this on the blog post about OSCon.

Galois and HalVM

We also stopped by the Galois to chat with Adam Wick, who is the leader of the HalVM project at Galois. This is a similar project to Mirage, but, since it's written in Haskell, has more of a focus on elegant compositional semantics rather than the more brutal performance and predictability that Mirage currently has at its lower levels.

The future of all this ultimately lies in making it easier for these multi-lingual unikernels to be managed and for all of them to communicate more easily, so we chatted about code sharing and common protocols (such as vchan) to help interoperability. Expect to see more of this once our respective implementations get more stable.

All-in-all OSCON'13 was a fun event and definitely one that we look forward returning to with a more mature version of MirageOS, to build on the momentum begun this year! Portland was an amazing host city too, but what happens in Portland, stays in Portland...


Creating Xen block devices with MirageOS

By Dave Scott

MirageOS is a unikernel or "library operating system" that allows us to build applications which can be compiled to very diverse environments: the same code can be linked to run as a regular Unix app, relinked to run as a FreeBSD kernel module, and even linked into a self-contained kernel which can run on the Xen hypervisor.

Mirage has access to an extensive suite of pure OCaml libraries, covering everything from Xen block and network virtual device drivers, a TCP/IP stack, OpenFlow learning switches and controllers, to SSH and HTTP server implementations.

I normally use Mirage to deploy applications as kernels on top of a XenServer hypervisor. I start by first using the Mirage libraries within a normal Unix userspace application -- where I have access to excellent debugging tools -- and then finally link my app as a high-performance Xen kernel for production.

However Mirage is great for more than simply building Xen kernels. In this post I'll describe how I've been using Mirage to create experimental virtual disk devices for existing Xen VMs (which may themselves be Linux, *BSD, Windows or even Mirage kernels). The Mirage libraries let me easily experiment with different backend file formats and protocols, all while writing only type-safe OCaml code thats runs in userspace in a normal Linux domain 0.

Disk devices under Xen

The protocols used by Xen disk and network devices are designed to permit fast and efficient software implementations, avoiding the inefficiencies inherent in emulating physical hardware in software. The protocols are based on two primitives:

  • shared memory pages: used for sharing both data and metadata
  • event channels: similar to interrupts, these allow one side to signal the other

In the disk block protocol, the protocol starts with the client ("frontend" in Xen jargon) sharing a page with the server ("backend"). This single page will contain the request/response metadata, arranged as a circular buffer or "ring". The client ("frontend") can then start sharing pages containing disk blocks with the backend and pushing request structures to the ring, updating shared pointers as it goes. The client will give the server end a kick via an event channel signal and then both ends start running simultaneously. There are no locks in the protocol so updates to the shared metadata must be handled carefully, using write memory barriers to ensure consistency.

Xen disk devices in MirageOS

Like everything else in Mirage, Xen disk devices are implemented as libraries. The ocamlfind library called "xenctrl" provides support for manipulating blocks of raw memory pages, "granting" access to them to other domains and signalling event channels. There are two implementations of "xenctrl": one that invokes Xen "hypercalls" directly and one which uses the Xen userspace library libxc. Both implementations satisfy a common signature, so it's easy to write code which will work in both userspace and kernelspace.

The ocamlfind library shared-memory-ring provides functions to create and manipulate request/response rings in shared memory as used by the disk and network protocols. This library is a mix of 99.9% OCaml and 0.1% asm, where the asm is only needed to invoke memory barrier operations to ensure that metadata writes issued by one CPU core appear in the same order when viewed from another CPU core.

Finally the ocamlfind library xenblock provides functions to hotplug and hotunplug disk devices, together with an implementation of the disk block protocol itself.

Making custom virtual disk servers with MirageOS

Let's experiment with making our own virtual disk server based on the Mirage example program, xen-disk.

First, install Xen, OCaml and OPAM. Second initialise your system:

  opam init
  eval `opam config env`

At the time of writing, not all the libraries were released as upstream OPAM packages, so it was necessary to add some extra repositories. This should not be necessary after the Mirage developer preview at OSCON 2013.

  opam remote add mirage-dev git://
  opam remote add xapi-dev git://

Install the unmodified xen-disk package, this will ensure all the build dependencies are installed:

  opam install xen-disk

When this completes it will have installed a command-line tool called xen-disk. If you start a VM using your Xen toolstack of choice ("xl create ..." or "xe vm-install ..." or "virsh create ...") then you should be able to run:

  xen-disk connect <vmname>

which will hotplug a fresh block device into the VM "<vmname>" using the "discard" backend, which returns "success" to all read and write requests, but actually throws all data away. Obviously this backend should only be used for basic testing!

Assuming that worked ok, clone and build the source for xen-disk yourself:

  git clone git://
  cd xen-disk

Making a custom virtual disk implementation

The xen-disk program has a set of simple built-in virtual disk implementations. Each one satisifies a simple signature, contained in src/storage.mli:

type configuration = {
  filename: string;      (** path where the data will be stored *)
  format: string option; (** format of physical data *)
(** Information needed to "open" a disk *)

module type S = sig
  (** A concrete mechanism to access and update a virtual disk. *)

  type t
  (** An open virtual disk *)

  val open_disk: configuration -> t option Lwt.t
  (** Given a configuration, attempt to open a virtual disk *)

  val size: t -> int64
  (** [size t] is the size of the virtual disk in bytes. The actual
      number of bytes stored on media may be different. *)

  val read: t -> Cstruct.t -> int64 -> int -> unit Lwt.t
  (** [read t buf offset_sectors len_sectors] copies [len_sectors]
      sectors beginning at sector [offset_sectors] from [t] into [buf] *)

  val write: t -> Cstruct.t -> int64 -> int -> unit Lwt.t
  (** [write t buf offset_sectors len_sectors] copies [len_sectors]
      sectors from [buf] into [t] beginning at sector [offset_sectors]. *)

Let's make a virtual disk implementation which uses an existing disk image file as a "gold image", but uses copy-on-write so that no writes persist. This is a common configuration in Virtual Desktop Infrastructure deployments and is generally handy when you want to test a change quickly, and revert it cleanly afterwards.

A useful Unix technique for file I/O is to "memory map" an existing file: this associates the file contents with a range of virtual memory addresses so that reading and writing within this address range will actually read or write the file contents. The "mmap" C function has a number of flags, which can be used to request "copy on write" behaviour. Reading the OCaml manual Bigarray.map_file it says:

If shared is true, all modifications performed on the array are reflected in the file. This requires that fd be opened with write permissions. If shared is false, modifications performed on the array are done in memory only, using copy-on-write of the modified pages; the underlying file is not affected.

So we should be able to make a virtual disk implementation which memory maps the image file and achieves copy-on-write by setting "shared" to false. For extra safety we can also open the file read-only.

Luckily there is already an "mmap" implementation in xen-disk; all we need to do is tweak it slightly. Note that the xen-disk program uses a co-operative threading library called lwt which replaces functions from the OCaml standard library which might block with non-blocking variants. In particular lwt uses Lwt_bytes.map_file as a wrapper for the Bigarray.Array1.map_file function. In the "open-disk" function we simply need to set "shared" to "false" to achieve the behaviour we want i.e.

  let open_disk configuration =
    let fd = Unix.openfile configuration.filename [ Unix.O_RDONLY ] 0o0 in
    let stats = Unix.LargeFile.fstat fd in
    let mmap = Lwt_bytes.map_file ~fd ~shared:false () in
    Unix.close fd;
    return (Some (stats.Unix.LargeFile.st_size, Cstruct.of_bigarray mmap))

The read and write functions can be left as they are:

  let read (_, mmap) buf offset_sectors len_sectors =
    let offset_sectors = Int64.to_int offset_sectors in
    let len_bytes = len_sectors * sector_size in
    let offset_bytes = offset_sectors * sector_size in
    Cstruct.blit mmap offset_bytes buf 0 len_bytes;
    return ()

  let write (_, mmap) buf offset_sectors len_sectors =
    let offset_sectors = Int64.to_int offset_sectors in
    let offset_bytes = offset_sectors * sector_size in
    let len_bytes = len_sectors * sector_size in
    Cstruct.blit buf 0 mmap offset_bytes len_bytes;
    return () 

Now if we rebuild and run something like:

  dd if=/dev/zero of=disk.raw bs=1M seek=1024 count=1
  losetup /dev/loop0 disk.raw
  mkfs.ext3 /dev/loop0
  losetup -d /dev/loop0

  dist/build/xen-disk/xen-disk connect <myvm> --path disk.raw

Inside the VM we should be able to do some basic speed testing:

  # dd if=/dev/xvdb of=/dev/null bs=1M iflag=direct count=100
  100+0 records in
  100+0 records out
  104857600 bytes (105 MB) copied, 0.125296 s, 837 MB/s

Plus we should be able to mount the filesystem inside the VM, make changes and then disconnect (send SIGINT to xen-disk by hitting Control+C on your terminal) without disturbing the underlying disk contents.

So what else can we do?

Thanks to Mirage it's now really easy to experiment with custom storage types for your existing VMs. If you have a cunning scheme where you want to hash block contents, and use the hashes as keys in some distributed datastructure -- go ahead, it's all easy to do. If you have ideas for improving the low-level block access protocol then Mirage makes those experiments very easy too.

If you come up with a cool example with Mirage, then send us a pull request or send us an email to the Mirage mailing list -- we'd love to hear about it!


The road to a developer preview at OSCON 2013

By Anil Madhavapeddy

There's been a crazy stream of activity since the start of the year, but the most important news is that we have a release target for an integrated developer preview of the Mirage stack: a talk at O'Reilly OSCon in July! Do turn up there and find Dave Scott and Anil Madhavapeddy showing off interactive demonstrations.

Meanwhile, another significant announcement has been that Xen is joining the Linux Foundation as a collaborative project. This is great news for Mirage: as a library operating system, we can operate just as easily under other hypervisors, and even on bare-metal devices such as the Raspberry Pi. We're very much looking forward to getting the Xen-based developer release done, and interacting with the wider Linux community (and FreeBSD, for that matter, thanks to Gabor Pali's kFreeBSD backend).

Here's some other significant news from the past few months:

  • OPAM 1.0 was released, giving Mirage a solid package manager for handling the many libraries required to glue an application together. Vincent Bernardoff joined the team at Citrix and has been building a Mirage build-frontend called Mirari to hide much of the system complexity from a user who isn't too familiar with either Xen or OCaml.

  • A new group called the OCaml Labs has started up in the Cambridge Computer Laboratory, and is working on improving the OCaml toolchain and platform. This gives Mirage a big boost, as we can re-use several of the documentation, build and test improvements in our own releases. You can read up on the group's activities via the monthly updates, or browse through the various projects. One of the more important projects is the OCamlot continuous build infrastructure, which will also be testing Mirage kernels as one of the supported backends.

  • As we head into release mode, we've started weekly meetings to coordinate all the activities. We're keeping notes as we go along, so you should be able to skim the notes and mailing list archives to get a feel for the overall activities. Anil is maintaining a release checklist for the summer developer preview.

  • Anil (along with Yaron Minsky and Jason Hickey) is finishing up an O'Reilly book on Real World OCaml, which will be a useful guide to using OCaml for systems and network programming. If you'd like to review an early copy, please get in touch. The final book is anticipated to be released towards the end of the year, with a Rough Cut at the end of the summer.

  • The core system was described in an ASPLOS 2013 paper, which should help you understand the background behind library operating systems. Some of the Mirage libraries are also currently being integrated into the next-generation Windsor release of the Xen Cloud Platform, which means that several of the libraries will be used in production and hence move beyond research-quality code.

In the next few months, the installation notes and getting started guides will all be revamped to match the reality of the new tooling, so expect some flux there. If you want to take an early try of Mirage beforehand, don't forget to hop on the #mirage IRC channel on and ping us with questions directly. We will also be migrating some of the project infrastructure to be fully self-hosted on Mirage and Xen, and placing some of the services onto the new infrastructure.


Breaking up is easy to do (with OPAM)

By Anil Madhavapeddy

When we first started developing Mirage in 2009, we were rewriting huge chunks of operating system and runtime code in OCaml. This ranged from low-level device drivers to higher-level networking protocols such as TCP/IP or HTTP. The changes weren't just straight rewrites of C code either, but also involved experimenting with interfaces such as iteratees and lightweight threading to take advantage of OCaml's static type system. To make all of this easy to work with, we decided to lump everything into a single Git repository that would bootstrap the entire system with a single make invocation.

Nowadays though, Mirage is self-hosting, the interfaces are settling down, the number of libraries are growing every day, and portions of it are being used in the Xen Cloud Platform. So for the first developer release, we wanted to split up the monolithic repository into more manageable chunks, but still make it as easy as possible for the average OCaml developer to try out Mirage.

Thanks to much hard work from Thomas and his colleagues at OCamlPro, we now have OPAM: a fully-fledged package manager for Mirage! OPAM is a source-based package manager that supports a growing number of community OCaml libraries. More importantly for Mirage, it can also switch between multiple compiler installations, and so support cross-compiled runtimes and modified standard libraries.

OPAM includes compiler variants for Mirage-friendly environments for Xen and the UNIX tuntap backends. The installation instructions now give you instructions on how to use OPAM, and the old monolithic repository is considered deprecated. We're still working on full documentation for the first beta release, but all the repositories are on the Mirage organisation on Github, with some of the important ones being:

  • mirage-platform has the core runtime for Xen and UNIX, implemented as the OS module.
  • mirage-net has the TCP/IP networking stack.
  • ocaml-cstruct has the camlp4 extension to manipulate memory like C structs, but with type-safe accessors in OCaml.
  • ocaml-xenstore has a portable implementation of the Xenstore protocol to communicate with the Xen management stack from a VM (or even act as a server in a stub domain).
  • ocaml-dns is a pure OCaml implementation of the DNS protocol, including a server and stub resolver.
  • ocaml-re is a pure OCaml version of several regular expression engines, including Perl compatibility.
  • ocaml-uri handles parsing the surprisingly complex URI strings.
  • ocaml-cohttp is a portable HTTP parser, with backends for Mirage, Lwt and Core/Async. This is a good example of how to factor out OS-specific concerns using the OCaml type system (and I plan to blog more about this soon).
  • ocaml-cow is a set of syntax extensions for JSON, CSS, XML and XHTML, which are explained here, and used by this site.
  • ocaml-dyntype uses camlp4 to generate dynamic types and values from OCaml type declarations.
  • ocaml-orm auto-generates SQL scheme from OCaml types via Dyntype, and currently supports SQLite.
  • ocaml-openflow implements an OCaml switch and controller for the Openflow protocol.

There are quite a few more that are still being hacked for release by the team, but we're getting there very fast now. We also have the Mirage ports of SSH to integrate before the first release this year, and Haris has got some interesting DNSSEC code! If you want to get involved, join the mailing list or IRC channel!


Building a "Xenstore stub domain" with MirageOS

By Dave Scott

[ *Due to continuing development, some of the details in this blog post are now out-of-date. It is archived here.* ]

On all hosts running Xen, there is a critical service called xenstore. Xenstore is used to allow untrusted user VMs to communicate with trusted system VMs, so that

  • virtual disk and network connections can be established
  • performance statistics and OS version information can be shared
  • VMs can be remotely power-cycled, suspended, resumed, snapshotted and migrated.

If the xenstore service fails then at best the host cannot be controlled (i.e. no VM start or shutdown) and at worst VM isolation is compromised since an untrusted VM will be able to gain unauthorised access to disks or networks. This blog post examines how to disaggregate xenstore from the monolithic domain 0, and run it as an independent stub domain.

Recently in the Xen community, Daniel De Graaf and Alex Zeffertt have added support for xenstore stub domains where the xenstore service is run directly as an OS kernel in its own isolated VM. In the world of Xen, a running VM is a "domain" and a "stub" implies a single-purpose OS image rather than a general-purpose machine. Previously if something bad happened in "domain 0" (the privileged general-purpose OS where xenstore traditionally runs) such as an out-of-memory event or a performance problem, then the critical xenstore process might become unusable or fail altogether. Instead if xenstore is run as a "stub domain" then it is immune to such problems in domain 0. In fact, it will even allow us to reboot domain 0 in future (along with all other privileged domains) without incurring any VM downtime during the reset!

The new code in xen-unstable.hg lays the necessary groundwork (Xen and domain 0 kernel changes) and ports the original C xenstored to run as a stub domain.

Meanwhile, thanks to Vincent Hanquez and Thomas Gazagnaire, we also have an OCaml implementation of xenstore which, as well as the offering memory-safety, also supports a high-performance transaction engine, necessary for surviving a stressful "VM bootstorm" event on a large server in the cloud. Vincent and Thomas' code is Linux/POSIX only.

Ideally we would have the best of both worlds:

  • a fast, memory-safe xenstored written in OCaml,
  • running directly as a Xen stub domain i.e. as a specialised kernel image without Linux or POSIX

We can now do both, using Mirage! If you're saying, "that sounds great! How do I do that?" then read on...

Step 1: remove dependency on POSIX/Linux

If you read through the existing OCaml xenstored code, it becomes obvious that the main uses of POSIX APIs are for communication with clients, both Unix sockets and for a special Xen inter-domain shared memory interface. It was a fairly painless process to extract the required socket-like IO signature and turn the bulk of the server into a functor. The IO signature ended up looking approximately like:

    type t
    val read: t -> string -> int -> int -> int Lwt.t
    val write: t -> string -> int -> int -> unit Lwt.t
    val destroy: t -> unit Lwt.t

For now the dependency on Lwt is explicit but in future I'll probably make it more abstract so we can use Core Async too.

Step 2: add a Mirage Xen IO implementation

In a stub-domain all communication with other domains is via shared memory pages and "event channels". Mirage already contains extensive support for using these primitives, and uses them to create fast network and block virtual device drivers. To extend the code to cover the Xenstore stub domain case, only a few tweaks were needed to add the "server" side of a xenstore ring communication, in addition to the "client" side which was already present.

In Xen, domains share memory by a system of explicit "grants", where a client (called "frontend") tells the hypervisor to allow a server (called "backend") access to specific memory pages. Mirage already had code to create such grants, all that was missing was a few simple functions to receive grants from other domains.

These changes are all in the current mirage-platform tree.

Step 3: add a Mirage Xen "main" module and Makefile

The Mirage "main" module necessary for a stub domain looks pretty similar to the normal Unix userspace case except that it:

  • arranges to log messages via the VM console (rather than a file or the network, since a disk or network device cannot be created without a working xenstore, and it's important not to introduce a bootstrap problem here)
  • instantiates the server functor with the shared memory inter-domain IO module.

The Makefile looks like a regular Makefile, invoking ocamlbuild. The whole lot is built with OASIS with a small extension added by Anil to set a few options required for building Xen kernels rather than regular binaries.

... and it all works!

The code is in two separate repositories:

  • ocaml-xenstore: contains all the generic stuff
  • ocaml-xenstore-xen: contains the unix userspace and xen stub domain IO modules and "main" functions
  • (optional) To regenerate the OASIS file, grab the add-xen branch from this OASIS fork.

Example build instructions

If you want to try building it yourself, try the following on a modern 64-bit OS. I've tested these instructions on a fresh install of Debian Wheezy.

First install OCaml and the usual build tools:

    apt-get install ocaml build-essential git curl rsync

Then install the OCamlPro opam package manager to simplify the installation of extra packages

    git clone git://
    cd opam
    make install
    cd ..

Initialise OPAM with the default packages:

    opam --yes init
    eval `opam config -env`

Add the "mirage" development package source (this step will not be needed once the package definitions are upstreamed)

    opam remote -add dev git://

Switch to the special "mirage" version of the OCaml compiler

    opam --yes switch -install 3.12.1+mirage-xen
    opam --yes switch 3.12.1+mirage-xen
    eval `opam config -env`

Install the generic Xenstore protocol libraries

    opam --yes install xenstore

Install the Mirage development libraries

    opam --yes install mirage

If this fails with "+ runtime/dietlibc/lib/atof.c:1: sorry, unimplemented: 64-bit mode not compiled in" it means you need a 64-bit build environment. Next, clone the xen stubdom tree

    git clone git://

Build the Xen stubdom

    cd ocaml-xenstore-xen

The binary now lives in xen/_build/src/server_xen.xen

Deploying on a Xen system

Running a stub Xenstored is a little tricky because it depends on the latest and greatest Xen and Linux PVops kernel. In the future it'll become much easier (and probably the default) but for now you need the following:

  • xen-4.2 with XSM (Xen Security Modules) turned on
  • A XSM/FLASK policy which allows the stubdom to call the "domctl getdomaininfo". For the moment it's safe to skip this step with the caveat that xenstored will leak connections when domains die.
  • a Xen-4.2-compatible toolstack (either the bundled xl/libxl or xapi with some patches)
  • Linux-3.5 PVops domain 0 kernel
  • the domain builder binary init-xenstore-domain from xen-4.2/tools/xenstore.

To turn the stub xenstored on, you need to edit whichever init.d script is currently starting xenstore and modify it to call

    init-xenstore-domain /path/to/server_xen.xen 256 flask_label


Connected Cloud Control: OpenFlow in MirageOS

By Richard Mortier

Due to continuing development, some of the details in this blog post are now out-of-date. It is archived here.

Something we've been working on for a little while now that we're pretty excited about is an OpenFlow implementation for MirageOS. For those who're not networking types, in short, OpenFlow is a protocol and framework for devolving network control to software running on platforms other than the network elements themselves. It consists of three main parts:

  • a controller, responsible for exercising control over the network;
  • switches, consisting of switching hardware, with flow tables that apply forwarding behaviours to matching packets; and
  • the protocol, by which controllers and switches communicate.

For more -- and far clearer! -- explanations, see any of the many online OpenFlow resources such as OpenFlowHub.

Within MirageOS we have an OpenFlow implementation in two parts: individual libraries that provide controller and switch functionality. Linking the switch library enables your application to become a software-based OpenFlow switch. Linking in the controller library enables your application to exercise direct control over OpenFlow network elements.

The controller is modelled after the NOX open-source controller and currently provides only relatively low-level access to the OpenFlow primitives: a very cool thing to build on top of it would be a higher-level abstraction such as that provided by Nettle or Frenetic.

The switch is primarily intended as an experimental platform -- it is hopefully easier to extend than some of the existing software switches while still being sufficiently high performance to be interesting!

By way of a sample of how it fits together, here's a skeleton for a simple controller application:

type mac_switch = {
  addr: OP.eaddr; 
  switch: OP.datapath_id;

type switch_state = {
  mutable mac_cache: 
        (mac_switch, OP.Port.t) Hashtbl.t;
  mutable dpid: OP.datapath_id list

let switch_data = {
  mac_cache = Hashtbl.create 7; 
  dpid = [];

let join_cb controller dpid evt =
  let dp = match evt with
      | OE.Datapath_join c -> c
      | _ -> invalid_arg "bogus datapath_join"
  switch_data.dpid <- switch_data.dpid @ [dp]

let packet_in_cb controller dpid evt =
  (* algorithm details omitted for space *)

let init ctrl = 
  OC.register_cb ctrl OE.DATAPATH_JOIN join_cb;
  OC.register_cb ctrl OE.PACKET_IN packet_in_cb

let main () =
  Net.Manager.create (fun mgr interface id ->
    let port = 6633 in 
    OC.listen mgr (None, port) init

We've written up some of the gory details of the design, implementation and performance in a short paper to the ICC Software Defined Networking workshop. Thanks to some sterling work by Haris and Balraj, the headline numbers are pretty good though: the unoptimised Mirage controller implementation is only 30--40% lower performance than the highly optimised NOX destiny-fast branch, which drops most of the programmability and flexibility of NOX; but is about six times higher performance than the fully flexible current NOX release. The switch's performance running as a domU virtual machine is indistinguishable from the current Open vSwitch release.

For more details see the paper or contact Mort, Haris or Anil. Please do get in touch if you've any comments or questions, or you do anything interesting with it!


An Outing to CUFP 2011

By Anil Madhavapeddy

The team signed up to do a tutorial at CUFP on the topic of Building a Functional OS, which meant zooming off to Tokyo! This was the first public show of the project, and resulted in a furious flurry of commits from the whole team to get it ready. The 45-strong crowd at the tutorial were really full of feedback, and particular thanks to Michael for organising the event, and Yaron, Marius, Steve, Wil, Adrian and the rest for shouting out questions regularly!

  • The tutorial is a Mirage application, so you can clone it and view it locally through your web browser. The content is mirrored at, although it does require cleanup to make it suitable to an online audience. The SVG integration is awkward and it only works on Chrome/Safari, so I will probably rewrite it using deck.js soon. The tutorial is a good showcase of Mirage, as it compiles to Xen, UNIX (both kernel sockets and direct tuntap) with a RAMdisk or external filesystem, and is a good way to mess around with application synthesis (look at the Makefile targets in slides/).

  • Installation: instructions have been simplified, and we now only require OCaml on the host and include everything else in-tree. Thomas has also made Emacs and Vim plugins that are compatible with the ocamlbuild layout.

  • Lwt: a new tutorial which walks you through the cooperative threading library we use, along with exercises (all available in mirage-tutorial). Raphael and Balraj are looking for feedback on this, so get in touch!

  • Javascript: via node.js did not work in time for the tutorial, as integrating I/O is a tangled web that will take some time to sort out. Raphael is working on this in a separate tree for now. As part of this effort though, he integrated a pure OCaml regular expression library that does not require C bindings, and is surprisingly fast.

  • Devices: we can now synthesise binaries that share common code but have very different I/O interfaces. This is due to a new device manager, and David also heroically wrote a complete FAT12/16/32 library that we demonstrated. Yaron Minsky suggested a different approach to the device manager using first-class modules instead of objects, so I am experimentally trying this before writing documentation on it.

  • TCP: the notorious Mirage stack is far more robust due to our resident networking guru Balraj hunting down last-minute bugs. Although it held together with sticky tape during the tutorial, he is now adding retransmission and congestion control to make it actually standards-compliant. Still, if you dont have any packet loss, the unikernel version of this website does actually serve pages.

  • OpenFlow: is a new standard for Software Defined Networking, and Haris and Mort have been hacking away at a complete implementation directly in Mirage! We will be giving a tutorial on this at the OFELIA summer school in November (it is summer somewhere, I guess). The prospect of a high-speed unikernel switching fabric for the cloud, programmed in a functional style, is something I am really looking forward to seeing!

  • Jane Street Core: preceeding us was Yaron's Core tutorial. Since Mirage provides it own complete standard library, we can adopt portions of Core that do not require OS threads or UNIX-specific features. I really like the idea that Mirage enforces a discipline on writing portable interfaces, as dependencies on OS-specific features do sneak in insiduously and make switching to different platforms very difficult (e.g. Windows support). Incidentally, Yaron's ACM Queue article is a great introduction to OCaml.

So as you can see, it has been a busy few months! Much of the core of Mirage is settling down now, and we are writing a paper with detailed performance benchmarks of our various backends. Keep an eye on the Github milestone for the preview release, join our new mailing list, or follow the newly sentient openmirage on twitter!


Portable Regular Expressions

By Raphael Proust

MirageOS targets different backends: micro-kernels for the Xen hypervisor, Unix executables and Javascript programs. The recent inclusion of the Javascript backend makes many C bindings unsuitable. In order to push backend incompatibilities closer to the application level, it is necessary to either reimplement the C bindings in Javascript or OCaml, or remove them completely. This is particularly important for the standard library.

The Str module has to go!

Str provides regular expressions in a non-reentrant, non-functional fashion. While the OCaml distribution provides it in otherlibs, it is installed by default and so widely used, and implemented under the hood via a C library. Regular expressions are used in several places in MirageOS, mainly for small operations (splitting, getting an offset, etc.), and so having a portable fallback written in pure OCaml would be very useful.

There are several possible ways to replace the Str module, each with its own set of perks and drawbacks:

  • Use a backend-neutral regexp library which "translates" to either Str or Pcre for the Xen and Unix backends or Javascript native regexps for the Javascript backend. This solution may be hard to maintain, especially if a fourth backend is to be included. Moreover each regexp library uses a slightly different convention for regexps (e.g. see the magic option in vim) which means that a lot of translation code might be needed.
  • Do string processing without regexps (using String.index and the likes). This solution is portable and potentially efficient. However, the potential efficiency comes from a low-level way of doing things.
  • Use an OCaml regexp library without C bindings. We expected such a library to be slower than Str and needed an estimation of performance cost in order to assess the practicality of the solution.

Benchmarking Str

There is a purely OCaml regexp library readily available, called Regexp and developed by Claude Marché from the LRI laboratory. You can find the documentation and the source on the associated webpage. After getting rid of mutexes (which, in MirageOS, are of no use, because of the Lwt based concurrency), we benchmarked it against Str. We also included the popular Pcre (Perl Compatible Regular Expression) library that is widely used.

The benchmark (available on github) is really simple and measures three different factors:

  • regexp construction: the transformation of a string (or another representation available to the programmer) into the internal representation of regexps used by the library
  • regexp usage: the execution of operations using regexps
  • string size: the length of the string being matched

MirageOS uses regexp in a specific pattern: a limited number of regexp constructions with a potentially high number of invocation (e.g. HTTP header parsing). The size of the strings on which regexps are used may vary. Because of this pattern, our benchmark does not take regexp construction overhead into account.

Here are the execution times of approximately 35000 string matching operations on strings of 20 to 60 bytes long.

Quite surprisingly for the string matching operation, the C based Str module is less efficient than the pure OCaml Regexp. The Pcre results were even worse than Str. Why?

A simple library for a simple task

The Regexp library is lightweight, and so far faster than its C based counterparts. One of the features Regexp lacks is "group capture": the ability to refer to blocks of a previously matched string. In Pcre it is possible to explicitly and selectively turn group capturing off via special syntax, instead of the regular parentheses. Str does not offer this, and thus imposes the runtime cost of capture even when not necessary. In other words, the slowdown/group capturing "is not a feature, it's a bug!"

The MirageOS Regexp library

With the introduction of Regexp into the tree, the libraries available to MirageOS applications are now Str-free and safer to use across multiple backends. The main drawback is a slight increase in verbosity of some parts of the code. Benchmarking the substitution operation is also necessary to assess the performance gain/loss (which we will do shortly).

In addition to cosmetic and speed considerations, it is important to consider the portability increase: MirageOS's standard library is Node.js compatible, a feature we will explore shortly!


Delimited Continuations vs Lwt for Threads

By Anil Madhavapeddy

MirageOS is a fully event-driven system, with no support for conventional preemptive threads. Instead, programs are woken by events such as incoming network packets, and event callbacks execute until they themselves need to block (due to I/O or timers) or complete their task.

Event-driven systems are simple to implement, scalable to lots of network clients, and very hip due to frameworks like node.js. However, programming event callbacks directly leads to the control logic being scattered across many small functions, and so we need some abstractions to hide the interruptions of registering and waiting for an event to trigger.

OCaml has the excellent Lwt threading library that utilises a monadic approach to solving this. Consider this simplified signature:

  val return : 'a -> 'a Lwt.t 
  val bind : 'a Lwt.t -> ('a -> 'b Lwt.t) -> 'b Lwt.t
  val run : 'a Lwt.t -> 'a

Threads have the type 'a Lwt.t, which means that the thread will have a result of type 'a when it finishes. The return function is the simplest way to construct such a thread from an OCaml value.

If we then wish to use the value of thread, we must compose a function that will be called in the future when the thread completes. This is what the bind function above is for. For example, assume we have a function that will let us sleep for some time:

  val sleep: int -> unit Lwt.t

We can now use the bind function to do something after the sleep is complete:

  let x = sleep 5 in
  let y = bind x (fun () -> print_endline "awake!") in
  run y

x has the type unit Lwt.t, and the closure passed to bind will eventually be called with unit when the sleep finishes. Note that we also need a function to actually begin evaluating an Lwt thread, which is the run function.


MirageOS currently uses Lwt extensively, and we have been very happy with using it to build a network stack. However, I was surprised to hear a lot of debate at the 2011 OCaml Users Group meeting that Lwt is not to everyone's tastes. There are a few issues:

  • The monadic style means that existing code will not just work. Any code that might block must be adapted to use return and bind, which makes integrating third-party code problematic.

  • More concerningly, any potential blocking points require the allocation of a closure. This allocation is very cheap in OCaml, but is still not free. Jun Furuse notes that combinator-based systems are slower during the development of his Planck parser.

Lwt addresses the first problem via a comprehensive syntax extension which provides Lwt equivalents for many common operations. For example, the above example with sleep can be written as:

  lwt x = sleep 5 in
  print_endline "awake"

The lwt keyword indicates the result of the expression should be passed through bind, and this makes it possible to write code that looks more OCaml-like. There are also other keywords like for_lwt and match_lwt that similarly help with common control flow constructs.


After the meeting, I did get thinking about using alternatives to Lwt in MirageOS. One exciting option is the delimcc library which implements delimited continuations for OCaml. These can be used to implement restartable exceptions: a program can raise an exception which can be invoked to resume the execution as if the exception had never happened. Delimcc can be combined with Lwt very elegantly, and Jake Donham did just this with the Lwt_fiber library. His post also has a detailed explanation of how delimcc works.

The interface for fibers is also simple:

  val start: (unit -> 'a) -> 'a Lwt.t
  val await : 'a Lwt.t -> 'a

A fiber can be launched with start, and during its execution can block on another thread with await. When it does block, a restartable exception saves the program stack back until the point that start was called, and it will be resumed when the thread it blocked on completes.


I put together a few microbenchmarks to try out the performance of Lwt threads versus fibers. The fiber test looks like this:

  module Fiber = struct
    let basic fn yields =
      for i = 1 to 15000 do
        for x = 1 to yields do
          Lwt_fiber.await (fn ())

    let run fn yields =
      Lwt_fiber.start (fun () -> basic fn yields)

We invoke the run function with two arguments: a thread to use for blocking and the number of times we should yield serially (so we can confirm that an increasing number of yields scales linearly). The Lwt version is pretty similar:

  module LWT = struct
    let basic fn yields =
      for_lwt i = 1 to 15000 do
        for_lwt x = 1 to yields do
          fn ()
    let run = basic

We do not need to do anything special to launch a thread since we are already in the Lwt main loop, and the syntax extension makes the for loops look like the Fiber example above.

The choice of blocking function is important. The first test runs using a fast Lwt.return () that returns immediately:

The x-axis on the above graph represents the number of yields in each loop. Both Lwt_fiber and pure Lwt optimise the case where a thread returns immediately, and so this graph simply tells us that the fast path is working (which is nice!). The next test replaces the blocking function with two alternatives that force the thread to yield:

There are two blocking functions used in the graph above:

  • the "slow" version is Lwt_unix.sleep 0.0 which forces the registration of a timeout.
  • the "medium" version is Lwt.pause () which causes the thread to pause and drop into the thread scheduler. In the case of Lwt_fiber, this causes an exception to be raised so we can benchmark the cost of using a delimited continuation.

Interestingly, using a fiber is slower than normal Lwt here, even though our callstack is not very deep. I would have hoped that fibers would be significantly cheaper with a small callstack, as the amount of backtracking should be quite low. Lets confirm that fibers do in fact slow down as the size of the callstack increases via this test:

  module Fiber = struct
    let recurse fn depth =
      let rec sum n = 
        Lwt_fiber.await (fn ());
        match n with
        |0 -> 0
        |n -> n + (sum (n-1)) 
      for i = 1 to 15000 do
        ignore(sum depth)

    let run fn depth = 
      Lwt_fiber.start (fun () -> recurse fn depth)

The recurse function is deliberately not tail-recursive, so that the callstack increases as the depth parameter grows. The Lwt equivalent is slightly more clunky as we have to rewrite the loop to bind and return:

  module LWT = struct
    let recurse fn depth =
      let rec sum n =
        lwt () = fn () in
        match n with
        |0 -> return 0
        |n ->
          lwt n' = sum (n-1) in 
          return (n + n')
      for_lwt i = 1 to 15000 do
        lwt res = sum depth in
        return ()

   let run = recurse

We then run the experiment using the slow Lwt_unix.sleep 0.0 function, and get this graph:

The above graph shows the recursive Lwt_fiber getting slower as the recursion depth increases, with normal Lwt staying linear. The graph also overlays the non-recursing versions as a guideline (*-basic-slow).


This first benchmark was a little surprising for me:

  • I would have thought that delimcc to be ahead of Lwt when dealing with functions with a small call-depth and a small amount of blocking (i.e. the traffic pattern that loaded network servers see). The cost of taking a restartable exception seems quite high however.
  • The fiber tests still use the Lwt machinery to manage the callback mechanism (i.e. a select loop and the timer priority queue). It may be possible to create a really light-weight version just for delimcc, but the Lwt UNIX backend is already pretty lean and mean and uses the libev to interface with the OS.
  • The problem of having to rewrite code to be Lwt-like still exists unfortunately, but it is getting better as the pa_lwt syntax extension matures and is integrated into my favourite editor (thanks Raphael!)
  • Finally, by far the biggest benefit of Lwt is that it can be compiled straight into Javascript using the js_of_ocaml compiler, opening up the possibility of cool browser visualisations and tickets to cool node.js parties that I don't normally get invited to.

I need to stress that these benchmarks are very micro, and do not take into account other things like memory allocation. The standalone code for the tests is online at Github, and I would be delighted to hear any feedback.

Retesting recursion [18th Jun 2011]

Jake Donham comments:

I speculated in my post that fibers might be faster if the copy/restore were amortized over a large stack. I wonder if you would repeat the experiment with versions where you call fn only in the base case of sum, instead of at every call. I think you're getting N^2 behavior here because you're copying and restoring the stack on each iteration.

When writing the test, I figured that calling the thread waiting function more often wouldn't alter the result (careless). So I modified the test suite to have a recurse test that only waits a single time at the end of a long call stack (see below) as well as the original N^2 version (now called recurse2).

  module Fiber = struct
    let recurse fn depth =
      let rec sum n = 
        match n with
        |0 -> Lwt_fiber.await (fn ()); 0
        |n -> n + (sum (n-1)) 
      for i = 1 to 15000 do
        ignore(sum depth)

    let run fn depth = 
      Lwt_fiber.start (fun () -> recurse fn depth)

The N^2 version below of course looks the same as the previously run tests, with delimcc getting much worse as it yields more often:

However, when we run the recurse test with a single yield at the end of the long callstack, the situation reverses itself and now delimcc is faster. Note that this test ran with more iterations than the recurse2 test to make the results scale, and so the absolute time taken cannot be compared.

The reason for Lwt being slower in this becomes more clear when we examine what the code looks like after it has been passed through the pa_lwt syntax extension. The code before looks like:

  let recurse fn depth =
    let rec sum n =
      match n with
      | 0 -> 
          fn () >> return 0
      | n ->
          lwt n' = sum (n-1) in 
          return (n + n') in

and after pa_lwt macro-expands it:

  let recurse fn depth =
    let rec sum n =
      match n with
      | 0 ->
          Lwt.bind (fn ()) (fun _ -> return 0)
      | n ->
          let __pa_lwt_0 = sum (n - 1)
          in Lwt.bind __pa_lwt_0 (fun n' -> return (n + n')) in

Every iteration of the recursive loop requires the allocation of a closure (the Lwt.bind call). In the delimcc case, the function operates as a normal recursive function that uses the stack, until the very end when it needs to save the stack in one pass.

Overall, I'm convinced now that the performance difference is insignificant for the purposes of choosing one thread system over the other for MirageOS. Instead, the question of code interoperability is more important. Lwt-enabled protocol code will work unmodified in Javascript, and Delimcc code helps migrate existing code over.

Interestingly, Javascript 1.7 introduces a yield operator, which has been shown to have comparable expressive power to the shift-reset delimcc operators. Perhaps convergence isn't too far away after all...


A Spring Wiki Cleaning

By Anil Madhavapeddy

We've been plugging away on Mirage for the last few months, and things are starting to take shape nicely. As the older blog entries were out-of-date, we have shifted the descriptive material to a new wiki section instead. What else has been happening?

  • The Xen unikernel backend is fully event-driven (no interrupts) and very stable under stress testing now. The TCP stack is also complete enough to self-host this website, and you can try it out by navigating to The stack doesnt actually do retransmissions yet, so your user experience may "vary". Check out the installation and hello world guides to try it out for yourself.
  • Richard Mortier has put together a performance testing framework that lets us analyse the performance of Mirage applications on different backends (e.g. UNIX vs Xen), and against other conventional applications (e.g. BIND for DNS serving). Read more in the wiki here.
  • Thomas Gazagnaire has rewritten the website to use the COW syntax extensions. He has also started a new job with OCamlPro doing consultancy on OCaml, so congratulations are in order!
  • Thomas has also started integrating experimental Node.js support to fill in our buzzword quota for the year (and more seriously, to explore alternative VM backends for Mirage applications).
  • The build system (often a bugbear of such OS projects) now fully uses ocamlbuild for all OCaml and C dependencies, and so the whole OS can be rebuilt with different compilers (e.g. LLVM) or flags with a single invocation.

There are some exciting developments coming up later this year too!


Self-hosting MirageOS website

By Anil Madhavapeddy

Welcome to the new self-hosting website for the Mirage project! As we go about preparing a release for later in the year, this blog will contain technical musings and work-in-progress reports of various bits of the operating system as they mature. Since there's so much to talk about, we decided to start with a blog format, and eventually collect things into a proper document as they stabilise.

Feel free to subscribe to the Atom feed to keep up-to-date with our progress, or just e-mail us or comment on individual posts with any queries.