Skip to content
academia.sh

Lesson 07 / 11

Hypervisor Types

Counting the boundary a hypervisor draws across two designs: the number of layers between guest and hardware, the items each layer adds to the trusted base, the fraction of items reached when a layer falls, and, for the same set of targets, the items required to cross the boundary together with the candidate items that item could have been chosen from.

Published Updated

In this lesson

  • Distinguishes type 1 and type 2 hypervisors by the number of layers between guest and hardware
  • Counts the items each layer adds to the trusted base and computes the fraction of items reached when a layer falls
  • States isolation strength as two numbers: the items required to cross the boundary and the candidate items that item could have been chosen from
Contents

The previous lesson tied troubleshooting to the number of possibilities eliminated per hypothesis, and closed the topic with a gap: every boundary measured was the boundary of a single machine. That machine’s hardware belonged to it alone, its kernel spoke directly to that hardware, and the boundary between user and kernel was the only boundary. On the branch servers of the regional measurement network this is no longer true: a single branch server runs four separate guest systems, and all four use the same processor, the same memory, and the same disk. Below the boundary the operating system draws there is a second boundary, and the layer that draws it is called the hypervisor.

This lesson distinguishes hypervisors not by an adjective but by two numbers. The first number is how many layers stand between the guest and the hardware. The second number is how many items each layer adds to the trusted base — that is, the number of items that must behave correctly for the guest boundary to hold. These two numbers place a design not against the other, but against its own set of items.

VR1 — The measurement object is a branch server of the fictional regional measurement network; four guest systems run on it. The item counts are a model, the run and the arithmetic are real. VR2 — The hypervisor is referred to only by its two type names: the type 1 hypervisor and the type 2 hypervisor. VR3 — The trusted base is the set of items that must behave correctly for the guest boundary to hold. VR4 — A layer falling means exactly one item in that layer behaving incorrectly. VR5 — The number of items required to cross the boundary is the number of layers that must be wrong at the same time; one item per layer is enough. VR6 — A candidate item is the total of items that could be wrong in those layers.

Layers and the Trusted Base

A type 1 hypervisor installs directly on top of the hardware; there is no general-purpose operating system beneath it. A type 2 hypervisor runs as a process on top of an operating system, and guest systems run inside that process. The difference is not an installation detail but the length of the layer list beneath the guest.

// layers.mjs — layers between guest and hardware, and the trusted base (MODEL).
// Fictional branch server: four guest systems run on it. Type names are used, not product names.

// Item sets in the system: short key -> [name, item count]
export const ITEM_SETS = {
  gd: ['guest disk image', 4], gm: ['guest memory content', 4], gn: ['guest network interface', 4],
  hy: ['hypervisor config item', 3], hu: ['host user-space file', 7],
  fw: ['firmware setting', 4],
};
// [layer, trusted-base items, on the guest-hardware path?, sets reached if it falls]
export const DESIGN = {
  'type 1 hypervisor': [
    ['management interface', 3, false, ['gd', 'gn', 'hy']],
    ['hypervisor kernel', 6, true, ['gd', 'gm', 'gn', 'hy']],
    ['firmware and boot chain', 4, true, ['gd', 'gm', 'gn', 'hy', 'fw']],
  ],
  'type 2 hypervisor': [
    ['host user space', 7, false, ['gd', 'gn', 'hy', 'hu']],
    ['hypervisor process', 5, true, ['gd', 'gm', 'gn', 'hy', 'hu']],
    ['host kernel', 9, true, ['gd', 'gm', 'gn', 'hy', 'hu', 'fw']],
    ['firmware and boot chain', 4, true, ['gd', 'gm', 'gn', 'hy', 'hu', 'fw']],
  ],
};
export const sum = (keys) => keys.reduce((t, a) => t + ITEM_SETS[a][1], 0);
// Item universe covered by the design: every set the layers can reach
export const universe = (name) => sum([...new Set(DESIGN[name].flatMap(([, , , e]) => e))]);

if (import.meta.url === `file://${process.argv[1]}`) {
  const s = (x, n) => String(x).padStart(n);
  for (const [name, layers] of Object.entries(DESIGN)) {
    const base = layers.reduce((t, [, k]) => t + k, 0);
    const onPath = layers.filter(([, , y]) => y).length;
    const all = universe(name);
    console.log(`\n${name}: trusted base ${base} items, on path ${onPath} layers, universe ${all} items`);
    console.log(`${'layer'.padEnd(28)}${s('items', 7)}${s('on path', 9)}${s('reached if fallen', 20)}`);
    for (const [k, n, y, e] of layers)
      console.log(`${k.padEnd(28)}${s(n, 7)}${s(y ? 'yes' : 'no', 9)}${s(`${sum(e)}/${all}`, 20)}`);
  }
}
type 1 hypervisor: trusted base 13 items, on path 2 layers, universe 19 items
layer                         items  on path   reached if fallen
management interface              3       no               11/19
hypervisor kernel                 6      yes               15/19
firmware and boot chain           4      yes               19/19

type 2 hypervisor: trusted base 25 items, on path 3 layers, universe 26 items
layer                         items  on path   reached if fallen
host user space                   7       no               18/26
hypervisor process                5      yes               22/26
host kernel                       9      yes               26/26
firmware and boot chain           4      yes               26/26

The two designs’ layer counts diverge in the expected direction: the type 1 design has two layers between guest and hardware, the type 2 design has three. Looking at this number alone, one might say the type 2 design is protected more deeply; the second column breaks that claim. The cost of carrying that extra layer is the trusted base climbing from thirteen items to twenty-five. Most of the added items are there not for the guest but for the host’s own work: the seven-item host user space is made of components the guest never needs, from the session manager to the package manager. The guest boundary still depends on them, because the hypervisor process sits in the same place as that user’s files.

The third column gives the real finding. In the type 1 design, the least damaging layer that can fall is the management interface: falling, it makes eleven of nineteen items reachable; guest memory content stays out, because the management interface creates and deletes the guest but does not read the running guest’s memory. In the type 2 design, the least damaging fall is the host user space, and there eighteen of twenty-six items become reachable. As a percentage, that is fifty-eight against sixty-nine. Adding a layer lengthened the path, but it did not narrow the surface the first fall opens.

The sharpest row is the type 2 design’s second row. When the hypervisor process falls, the number of items reached is twenty-two, and that is only four short of the number reached when the host kernel falls. The reason is structural: guest disk images sit in the host’s file system as files belonging to the identity that runs the hypervisor process. Whoever reaches the process reaches the four guests’ disks as files. The third layer beneath the guest protects the hardware, not the guest’s disk.

Items Required to Cross the Boundary

Layer count alone is not isolation strength. The measure is how many items must be wrong at the same time to reach a given target. The run below measures the same four targets separately in both designs and writes two numbers for each target: the items required and the number of candidates that item could have been chosen from.

// boundary.mjs — the same target set in both designs: items required to cross the boundary and candidate items.
// "Required" = number of layers that must be wrong at the same time. "Candidate" = the item total of those layers.
import { DESIGN, sum, universe } from './layers.mjs';

const TARGETS = [                    // [target, item sets that must be reached]
  ["neighbor guest's disk", ['gd']], ["neighbor guest's memory", ['gm']],
  ['changing a firmware setting', ['fw']], ['direct access to hardware', null],
];
const measure = (name, need) => {
  const layers = DESIGN[name];
  if (need === null) {               // hardware: one item must be wrong in every layer on the path
    const onPath = layers.filter(([, , y]) => y);
    return [onPath.length, onPath.reduce((t, [, n]) => t + n, 0)];
  }
  const qualifying = layers.filter(([, , , e]) => need.every((g) => e.includes(g)));
  return [1, qualifying.reduce((t, [, n]) => t + n, 0)];
};

const NAMES = Object.keys(DESIGN);
const s = (x, n) => String(x).padStart(n);
console.log(`${''.padEnd(29)}${'type 1'.padStart(19)}${'type 2'.padStart(21)}`);
console.log(`${'target'.padEnd(29)}${s('required', 8)}${s('candidate', 11)}${s('required', 10)}${s('candidate', 11)}`);
for (const [h, g] of TARGETS) {
  const [a1, b1] = measure(NAMES[0], g), [a2, b2] = measure(NAMES[1], g);
  console.log(`${h.padEnd(29)}${s(a1, 8)}${s(b1, 11)}${s(a2, 10)}${s(b2, 11)}`);
}
const single = TARGETS.filter(([, g]) => g !== null).length;
console.log(`\n${single} of the targets need 1 item in both designs; the difference is in the candidate count`);
for (const name of NAMES) {
  const k = DESIGN[name];
  const atLeast = Math.min(...k.map(([, , , e]) => sum(e)));
  console.log(`${name.padEnd(27)} base ${s(k.reduce((t, [, n]) => t + n, 0), 2)} items,`
    + ` a single layer falling reaches at least ${atLeast}/${universe(name)} items`);
}
                                          type 1               type 2
target                       required  candidate  required  candidate
neighbor guest's disk               1         13         1         25
neighbor guest's memory             1         10         1         18
changing a firmware setting         1          4         1         13
direct access to hardware           2         10         3         18

3 of the targets need 1 item in both designs; the difference is in the candidate count
type 1 hypervisor           base 13 items, a single layer falling reaches at least 11/19 items
type 2 hypervisor           base 25 items, a single layer falling reaches at least 18/26 items

In three of the four targets, the number of items required is one in both designs. Reaching a neighboring guest’s disk takes one item from a single layer; the same is true for the neighbor’s memory. The difference in layer count shows up in none of these three rows, because the layers stand one after another between guest and hardware, not between guest and neighboring guest. What separates two guests standing side by side is the same hypervisor, and that hypervisor is a single set of items.

The difference is in the candidate column. For the neighbor’s disk there are thirteen candidate items in the type 1 design and twenty-five in the type 2 design — roughly double. The firmware-setting row widens the ratio further: in the type 1 design the firmware setting is only reachable through the boot chain’s four items, while in the type 2 design the host kernel can also write the firmware, so the candidate count climbs from four to thirteen. Isolation strength is the number of items required; since that number is one in both, what separates the two designs is not the strength of the boundary but how many candidates that single item could come from.

Only in the last row is the required number greater than one: direct access to the hardware demands two items being wrong at the same time in the type 1 design, three in the type 2 design. This is the one place the type 2 design gains, and what it gains is the hardware — not the guest’s data.

Root of the Chain

The layer common to both designs is the firmware and boot chain, and in both tables it is the layer that reaches everything when it falls: 19/19 in the type 1 design, 26/26 in the type 2 design. Counting those four items separately is not enough, because there is an order among them: each link loads the next one. Secure boot places a check on that order — each link is verified because it was signed by the one before it, and the root of the chain is the trusted platform module in the hardware.

// boot.mjs — how signed boot changes the required and candidate items in the boot chain (MODEL).
// The root of the chain is the trusted platform module in the hardware; each link is verified by the one before it.
const CHAIN = ['boot firmware', 'boot loader', 'kernel image', 'hypervisor module'];

// unsigned: index of the first unverified link (null verifies the whole chain)
const measure = (signed, unsigned = null) => {
  if (!signed) return [1, CHAIN.length, 0];          // each link can be swapped on its own
  const cut = unsigned === null ? CHAIN.length : CHAIN.indexOf(unsigned);
  // whatever comes after the cut link is not verified: one item is enough, candidates are those remaining links
  if (cut < CHAIN.length) return [1, CHAIN.length - cut, CHAIN.length - 1];
  return [CHAIN.length, 1, CHAIN.length];            // root key is the sole candidate, links get re-signed
};

const s = (x, n) => String(x).padStart(n);
console.log(`boot chain ${CHAIN.length} links: ${CHAIN.join(' -> ')}`);
console.log(`${'arrangement'.padEnd(34)}${s('required', 8)}${s('candidate', 11)}${s('re-signed', 19)}`);
for (const [ad, signed, unsigned] of [['unsigned boot', false, null], ['signed boot', true, null],
  ['signed boot, last link unsigned', true, 'hypervisor module'],
  ['signed boot, kernel unsigned', true, 'kernel image']]) {
  const [g, a, y] = measure(signed, unsigned);
  console.log(`${ad.padEnd(34)}${s(g, 8)}${s(a, 11)}${s(y, 19)}`);
}
boot chain 4 links: boot firmware -> boot loader -> kernel image -> hypervisor module
arrangement                       required  candidate          re-signed
unsigned boot                            1          4                  0
signed boot                              4          1                  4
signed boot, last link unsigned          1          1                  3
signed boot, kernel unsigned             1          2                  3

In the unsigned arrangement, any one of the four links can be swapped on its own: required is one, candidate is four. In the signed arrangement, swapping the last link requires changing the entire chain from it up to the root; required climbs to four, while candidate drops to the single root key in the hardware. This is the lesson’s only numerical improvement — the required number grows and the candidate number shrinks at the same time.

The last two rows show cost and fragility together. Every update to the signed chain requires re-signing three or four links; that is a fixed task added to the patch process. The fragility is sharper: if the chain has a single unsigned link, the required item drops from four to one. When the last link is left unsigned, required is one and candidate is one too — meaning one unverified hypervisor module erases the entire gain of the four-link chain. When the kernel is left unsigned, candidate rises to two. The chain is only as strong as its first unverified link, not its weakest one.

Where the Cost Is Written

The type 1 design’s thirteen-item base does not come for free. On that design the machine runs only the guest system: the host has no browser, no local tool, no file sharing, and no field team’s daily work. The type 2 design’s seven-item host user space is not waste — it is that machine’s legitimate work. On field teams’ laptops that work cannot be given up; because it can be given up on a branch server, the type 1 design makes sense there.

The second item of cost is management. In the type 1 design the management interface was counted as a separate layer, and falling, it reached eleven items. In the type 2 design there is no separate management layer, because management is done from the host’s own session, and that session already reaches eighteen of twenty-six items. Moving management out into a separate layer narrows the items reached at the cost of adding one item — this is the first trade-off measured in this course, and it buys a seven-item narrowing for a cost of three items.

None of these measurements rank one type over the other. On a branch server where the host has no work of its own, the type 1 design asks to move management out to a separate interface in exchange for a base smaller by twelve items and a path shorter by one layer. On a field laptop, because that work cannot be given up, the type 2 design’s twenty-five-item base is not a choice but that machine’s definition. All the measure says is which numbers the chosen design comes with.

Summary

  • The type 1 hypervisor has two layers between guest and hardware, the type 2 hypervisor has three; the trusted base is 13 and 25 items respectively.
  • The smallest fraction of items reached when a single layer falls is 11/19 in the type 1 design and 18/26 in the type 2 design: adding a layer did not narrow the surface the first fall opens.
  • In the type 2 design, 22/26 items are reached when the hypervisor process falls, because the four guests’ disk images sit in the host’s file system as files belonging to that process’s identity.
  • In three of the four targets, the number of items required to cross the boundary is one in both designs; the difference is in the candidate count (13 against 25, 10 against 18, 4 against 13).
  • Only for direct access to hardware does the number rise to two and three; the type 2 design’s extra layer protects the hardware, not the guest’s disk.
  • In the four-link boot chain, signed boot raises the required item from 1 to 4 and lowers the candidate item from 4 to 1; a single unverified link drops the required item back to 1.

Next Step

This lesson’s entire measurement counted layers stacked on top of one another: guest, hypervisor, host, hardware. But there is not just a single stack between guest and host; while both run, they use a set of items at the same time. The same scheduler queues both sides’ threads, the same physical memory pages get merged, the disk and network card the guest sees are emulation code running on the host, and on top of that there are items opened for convenience, like a shared folder and a clipboard. The next lesson counts the escape risk through these shared items: how many items the guest and host share, how many paths each shared item opens, and how many paths remain once those items are closed.

To keep your progress and take notes, Log in

My notes

Log in to take notes.

Start typing to search.

↑↓ ↵ Esc navigate · open · close