Skip to content

install to-disk --block-setup tpm2-luks hangs: libdevmapper udev cookie semaphore deadlock in container IPC namespace #2089

Description

@andrewdunndev

Summary

bootc install to-disk --block-setup tpm2-luks hangs indefinitely at cryptsetup luksOpen. The root cause is a SysV semaphore deadlock between libdevmapper and udevd across the container's isolated IPC namespace.

This affects all tpm2-luks installs regardless of TPM2 hardware, PCR configuration, or token state. The hang occurs on any LUKS device-mapper activation inside bootc's container environment when the container uses a separate IPC namespace (the podman default).

Users who happened to pass --ipc=host to podman would not have hit this bug, which may explain why some users reported TPM2-related issues (#421, #476, #477, #561) without mentioning the install hang -- they may have bypassed the semaphore deadlock without realizing it.

Root cause

libdevmapper uses SysV semaphores ("udev cookies") to synchronize device-mapper operations with udevd. When bootc runs inside a container (the standard podman run --privileged --pid=host invocation), the container has an isolated IPC namespace by default. udevd runs on the host in the host's IPC namespace.

The sequence:

  1. cryptsetup luksFormat creates a LUKS volume. libdevmapper creates a SysV semaphore in the container's IPC namespace.
  2. cryptsetup luksOpen tries to activate a dm-crypt mapping. libdevmapper attempts to acquire the udev cookie semaphore and waits for udevd to signal completion.
  3. udevd (on the host) cannot see the semaphore because it is in a different IPC namespace. The semaphore is never released.
  4. luksOpen blocks forever on semop().

Kernel stack trace of the hanging process:

[<0>] __do_semtimedop+0x3a8/0xd50
[<0>] do_semtimedop+0x15e/0x1a0
[<0>] do_syscall_64+0x7e/0x6b0
[<0>] entry_SYSCALL_64_after_hwframe+0x76/0x7e

SysV semaphore visible inside the container's IPC namespace:

$ nsenter -t <cryptsetup_pid> --ipc ipcs -s
key        semid      owner      perms      nsems
0x0d4d3b80 0          root       600        1

Reproduction

# This hangs (default IPC namespace):
podman run --rm --privileged --pid=host \
    --security-opt label=type:unconfined_t \
    -v /dev:/dev \
    -v /var/lib/containers:/var/lib/containers \
    quay.io/fedora/fedora-bootc:42 \
    bootc install to-disk --wipe --block-setup tpm2-luks \
    --filesystem xfs /dev/sdX

Fix

Set DM_DISABLE_UDEV=1 in the environment before cryptsetup device-mapper operations. This tells libdevmapper to skip udev synchronization.

What DM_DISABLE_UDEV=1 does:

  • Tells libdevmapper to skip creating/waiting on udev cookie semaphores for dm operations
  • The kernel still creates device nodes (/dev/mapper/root) via devtmpfs -- this does not depend on udev
  • udev rules for dm events do not fire, so /dev/disk/by-uuid/ and /dev/disk/by-id/dm-uuid-* symlinks for the dm-crypt device are not created during install

Why this is safe during installation:

  • bootc references the dm device directly as /dev/mapper/root (baseline.rs line 380), never via udev symlinks
  • No other code in the install path depends on udev symlinks for the dm-crypt device
  • udev_settle() for partition discovery (line 332) is unaffected -- it operates on partition devices, not device-mapper
  • There are no concurrent device-mapper consumers during install
  • The env var is scoped to the Tpm2Luks code path only, not set globally for all bootc operations

What DM_DISABLE_UDEV=1 does NOT affect:

  • Partition device nodes (/dev/sdb1-4) -- created by kernel, managed by udev in the host namespace, unrelated to dm cookies
  • udev_settle() after partitioning -- still works normally
  • The installed system's boot-time LUKS unlock -- DM_DISABLE_UDEV is only set in the install container's environment, not persisted to the installed system

Two workarounds confirm the diagnosis:

# Workaround 1: DM_DISABLE_UDEV=1 (skips udev sync)
podman run ... -e DM_DISABLE_UDEV=1 ... bootc install to-disk ...
# Result: Installation complete!

# Workaround 2: --ipc=host (shares IPC namespace with host)
podman run ... --ipc=host ... bootc install to-disk ...
# Result: Installation complete!

Note on architecture

Running cryptsetup and device-mapper operations from inside a container is inherently fragile due to namespace isolation issues like this one. As @jmpolom noted in #421, an external workflow that prepares disks (including LUKS) before invoking bootc install to-filesystem would avoid this entire class of problems. See also #542 for the broader proposal of bootc-as-library for installer applications. This fix addresses the immediate hang for users of the existing install to-disk path.

Note on systemd 258 PCR default change

Separately from this hang, systemd-cryptenroll changed its default --tpm2-pcrs from PCR 7 (systemd <=257) to no PCRs (systemd >=258, commit 4b840414). bootc calls systemd-cryptenroll --tpm2-device=auto without specifying --tpm2-pcrs, so the PCR binding behavior depends on which systemd version is in the container image. This is tracked in #476 and #561 but worth noting as a separate concern.

Test environment

  • Fedora 42 (Cloud Edition), kernel 6.19.7-100.fc42.x86_64
  • systemd 257.11-1.fc42
  • cryptsetup 2.8.4
  • podman 5.8.0
  • GCP n2-standard-8 with 20GB PD-SSD target disk
  • Tested with stock bootc from quay.io/fedora/fedora-bootc:42

Related issues

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions