xen: more reliable Xen/guest detection + container awareness (#173)

Better detect Xen guest type + add container detection

CVE-2017-5754: when we see Xen but we're inside a container,
/proc/xen/capabilities isn't exposed and dmesg is the host's,
so dom0 vs PV DomU can't be told apart. Don't report VULN in
that case, but UNKNOWN instead, and ask to rerun the script on the host.

(cherry picked from commit 1211c21261)
This commit is contained in:
Stéphane Lesimple
2026-06-06 15:39:46 +02:00
parent 1161089b2e
commit d7129649d2
2 changed files with 112 additions and 30 deletions
+94 -25
View File
@@ -1,25 +1,41 @@
# vim: set ts=4 sw=4 sts=4 et:
# Check whether the system is running as a Xen paravirtualized guest
# Returns: 0 if Xen PV, 1 otherwise
is_xen() {
local ret
if [ ! -d "$g_procfs/xen" ]; then
return 1
# Probe Xen presence and guest type using the most reliable sources available.
# Prefer /sys/hypervisor when avalable, fallback to dmesg otherwise.
# Caches results in g_xen (1/0) and g_xen_guest_type (PV|PVH|HVM|'').
_detect_xen() {
[ "${g_xen_cached:-0}" = 1 ] && return
g_xen=0
g_xen_guest_type=''
g_xen_cached=1
# Most reliable: /sys/hypervisor/type is 'xen' on any Xen domain (dom0
# included), and /sys/hypervisor/guest_type reports PV, PVH or HVM.
if [ -r /sys/hypervisor/type ] && [ "$(cat /sys/hypervisor/type 2>/dev/null)" = xen ]; then
g_xen=1
if [ -r /sys/hypervisor/guest_type ]; then
g_xen_guest_type=$(cat /sys/hypervisor/guest_type 2>/dev/null)
fi
return
fi
# XXX do we have a better way that relying on dmesg?
dmesg_grep 'Booting paravirtualized kernel on Xen$'
ret=$?
if [ "$ret" -eq 2 ]; then
pr_warn "dmesg truncated, Xen detection will be unreliable. Please reboot and relaunch this script"
return 1
elif [ "$ret" -eq 0 ]; then
return 0
else
return 1
# Fallback for kernels without /sys/hypervisor: /proc/xen plus a dmesg probe.
if [ -d "$g_procfs/xen" ]; then
dmesg_grep 'Booting paravirtualized kernel on Xen$'
case $? in
0) g_xen=1 ;;
2) pr_warn "dmesg truncated, Xen detection will be unreliable. Please reboot and relaunch this script" ;;
esac
fi
}
# Check whether the system is running on Xen (any domain type, dom0 included).
# Returns: 0 if Xen, 1 otherwise
is_xen() {
_detect_xen
[ "$g_xen" = 1 ]
}
# Check whether the system is a Xen Dom0 (privileged domain)
# Returns: 0 if Dom0, 1 otherwise
is_xen_dom0() {
@@ -34,31 +50,77 @@ is_xen_dom0() {
fi
}
# Check whether the system is a Xen DomU (unprivileged PV guest)
# Returns: 0 if DomU, 1 otherwise
# Check whether the system is running as a Xen PV DomU (the only Xen guest type
# affected by Meltdown, which needs Xen-level mitigation).
# Returns: 0 if PV DomU, 1 otherwise
is_xen_domU() {
local ret
if ! is_xen; then
return 1
fi
# PVHVM guests also print 'Booting paravirtualized kernel', so we need this check.
if is_xen_dom0; then
return 1
fi
# When the reliable guest type is known, only PV domains (which aren't
# dom0, checked above) are the PV DomU case. PVH and HVM guests are not.
if [ -n "$g_xen_guest_type" ]; then
[ "$g_xen_guest_type" = PV ] && return 0
return 1
fi
# Fallback (no /sys/hypervisor/guest_type): PVHVM guests also print the
# 'Booting paravirtualized kernel' line, so exclude them via dmesg.
dmesg_grep 'Xen HVM callback vector for event delivery is enabled$'
ret=$?
if [ "$ret" -eq 0 ]; then
return 1
fi
if ! is_xen_dom0; then
return 0
else
return 1
fi
return 0
}
# Check whether the system is running as a guest inside a virtual machine.
# Check whether we're running inside an OS-level container (LXC, Docker,
# systemd-nspawn, etc.). Containers share the host kernel, so host/hypervisor
# introspection (e.g. telling a Xen dom0 from a domU) is unreliable from inside
# one: /proc/xen is exposed but empty, dmesg is the host's, etc. (issue #173)
# Returns: 0 if in a container, 1 otherwise
# Sets: g_is_container (1/0), g_container_reason
is_running_in_container() {
local ctype
if [ "${g_is_container_cached:-0}" != 1 ]; then
g_is_container=0
g_container_reason=''
# systemd and most runtimes export 'container=' to PID 1's environment
if [ -r "$g_procfs/1/environ" ]; then
ctype=$(tr '\0' '\n' <"$g_procfs/1/environ" 2>/dev/null | sed -n 's/^container=//p' | head -n1)
if [ -n "$ctype" ]; then
g_is_container=1
g_container_reason="container=$ctype in $g_procfs/1/environ"
fi
fi
# Docker (and some others) drop a marker file at the filesystem root
if [ "$g_is_container" = 0 ] && [ -e /.dockerenv ]; then
g_is_container=1
g_container_reason="/.dockerenv present"
fi
# cgroup membership often reveals the runtime (lxc, docker, kubepods, ...)
if [ "$g_is_container" = 0 ] && [ -r "$g_procfs/1/cgroup" ]; then
if grep -qE '(^|[:/])(lxc|docker|kubepods|libpod|containerd|machine\.slice)([/.]|$)' "$g_procfs/1/cgroup" 2>/dev/null; then
g_is_container=1
g_container_reason="container runtime found in $g_procfs/1/cgroup"
fi
fi
g_is_container_cached=1
fi
[ "$g_is_container" = 1 ]
}
# Check whether the system is running as a guest inside a VM.
# Uses the 'hypervisor' CPUID feature flag exposed in /proc/cpuinfo by KVM,
# VMware, Hyper-V, VirtualBox, and most other type-1 and type-2 hypervisors.
# Xen PV/PVH DomUs don't set that flag, so they're detected separately.
# Returns: 0 if running as a VM guest, 1 otherwise
# Sets: g_is_guest_vm (1=guest, 0=not a guest), g_is_guest_vm_reason
is_running_as_guest() {
@@ -69,6 +131,13 @@ is_running_as_guest() {
g_is_guest_vm=1
g_is_guest_vm_reason="'hypervisor' flag in $g_procfs/cpuinfo"
fi
# Xen PV/PVH DomUs don't expose the 'hypervisor' CPUID flag. Don't
# classify a container on a Xen host as a guest here: we can't tell
# dom0 from domU from inside a container (handled separately).
if [ "$g_is_guest_vm" = 0 ] && is_xen && ! is_xen_dom0 && ! is_running_in_container; then
g_is_guest_vm=1
g_is_guest_vm_reason="Xen ${g_xen_guest_type:-PV} DomU"
fi
g_is_guest_vm_cached=1
fi
[ "$g_is_guest_vm" = 1 ]