===================================================== AVB enforcement code path (LoadImageAndAuth / libavb) ===================================================== This page documents the **runtime** Android Verified Boot enforcement inside ABL: the ``LoadImageAndAuth`` → ``avb_slot_verify`` call path, how the device lock-state gates it, rollback-index handling, the per-result failure behavior (halt vs warn-and-continue), the boot-state colour it sets, and the ``DeviceInfo`` lock-state code it reads. It is deliberately scoped to the *code*: - the AVB *metadata* (``vbmeta`` contents, signing keys, descriptor digests) and *why the whole chain is bypassable* live in :doc:`/boot/secure-boot`; - the ``devinfo`` struct layout and byte offsets are below, in `DeviceInfo lock-state enforcement (DeviceInfo.c)`_; - the fastboot *command surface* (no ``oem unlock``) lives in :doc:`/abl/container`. This page cross-references those and does not restate them. RVAs are LinuxLoader-relative (= DXE file offset − ``0xb8``). ``LoadImageAndAuth`` (VB2 core, RVA ``0x5398``) =============================================== ``LinuxLoaderEntry`` calls ``LoadImageAndAuth`` before ``BootLinux`` (:doc:`/abl/linuxloader`). The device runs AVB 2.0, so control enters ``LoadImageAndAuthVB2`` (source ``QcomModulePkg/Library/avb/VerifiedBoot.c``). radare2 merges this function with its file-local helpers (``ResultShouldContinue``, ``ComputeVbMetaDigest``, the boot-state switch) into one large function at RVA ``0x5398`` — identified unambiguously by its string set (``Total loaded partition %d``, ``androidboot.verifiedbootstate=``, the corrupt-device message, the ``AvbSlotVerify returned`` logs). Pipeline: .. list-table:: :header-rows: 1 :widths: 32 68 * - Step - Behavior / binary basis * - Lock-state gate - ``AllowVerificationError = IsUnlocked ()`` — the single boolean that makes AVB advisory (unlocked) or strict (locked). See `Lock-state gate`_. * - Rollback pre-check - ``avb_should_update_rollback`` decides whether TZ updates rollback versions. See `Rollback-index handling`_. * - ``avb_slot_verify`` - The libavb entry that hashes/verifies each requested partition. See `The avb_slot_verify call path`_. * - Result handling - Branch on ``AvbSlotVerifyResult`` and lock state. See `Failure behavior`_. * - Boot-state colour - Set GREEN / YELLOW / ORANGE / RED and push ROT to Keymaster. See `Boot state, ROT and command line`_. * - Boot-state menu - RED halts; YELLOW/ORANGE/EIO warn then continue. See `Boot-state menu`_. Lock-state gate =============== Everything hinges on ``IsUnlocked ()`` (``DeviceInfo.c``), which returns ``DevInfo.is_unlocked``. This unit reads ``is_unlocked = 0`` → **locked** (:doc:`/boot/secure-boot`, decoded below). In ``LoadImageAndAuthVB2``:: AllowVerificationError = IsUnlocked (); VerifyFlags = AllowVerificationError ? AVB_SLOT_VERIFY_FLAGS_ALLOW_VERIFICATION_ERROR : AVB_SLOT_VERIFY_FLAGS_NONE; So on this locked device ``avb_slot_verify`` runs in strict mode: any verification failure is fatal (see `Failure behavior`_). But this ABL is test-key-signed (:doc:`/boot/secure-boot`), and as far as the stock code goes, nothing in it would prevent a patched ABL from reporting ``is_unlocked = 0`` while skipping the check entirely — a bypass that has not been empirically verified by actually flashing such a patched image. The avb_slot_verify call path ============================= ``avb_slot_verify`` (libavb, RVA ``0x8478``) is the verification engine. The libavb sources compile to a self-contained cluster in ``.text``, each file identifiable by its ASSERT path string: .. list-table:: :header-rows: 1 :widths: 22 26 52 * - RVA - libavb file - Role * - ``0x8478`` - ``avb_slot_verify.c`` - Top-level slot verify: walk requested partitions, load vbmeta, chain, and assemble ``AvbSlotVerifyData`` (cmdline, loaded partitions). * - ``0xad74`` - ``avb_slot_verify.c`` - Per-partition helper (recursive chain/hash-descriptor load). * - ``0xcf88`` - ``avb_vbmeta_image.c`` - ``avb_vbmeta_image_verify`` — vbmeta header + signature check. * - ``0xfdc8`` - ``avb_rsa.c`` - RSA signature verification of the vbmeta hash. * - ``0xf4dc`` - ``avb_hash_descriptor.c`` - Whole-image hash descriptor (boot/dtbo/recovery) validation. * - ``0xe48c`` - ``avb_chain_partition_descriptor.c`` - Chain-partition descriptor (delegates ``vbmeta_system``). * - ``0xf4f4`` - ``avb_kernel_cmdline_descriptor.c`` - Kernel-cmdline descriptor → the ``VBCmdLine`` consumed by :doc:`/abl/bootlinux`. * - ``0x83c0`` / ``0xe9e4`` / ``0xee3c`` / ``0xf140`` / ``0xbe30`` - property / descriptor / footer / util - Descriptor iteration, footer parse, byte-swap/util helpers. The descriptor set here matches the ``vbmeta_a`` structure documented in :doc:`/boot/secure-boot` (chain → ``vbmeta_system``; hash descriptors for ``boot``/``dtbo``/``recovery``; two hashtree descriptors). This is the code that consumes that metadata; the metadata's validity is analysed there. Rollback-index handling ======================= ABL does **not** store rollback indices itself. ``avb_should_update_rollback`` gates a call into TrustZone that owns the rollback versions: *"Check if slot is bootable and call into TZ to update rollback version … the secure environment make the decision on updating rollback version"* (source comment, ``VerifiedBoot.c`` ~line 1162). The ``avb_slot_verify`` result ``AVB_SLOT_VERIFY_RESULT_ERROR_ROLLBACK_INDEX`` is one of the failure codes handled below; the stored rollback counters live in TZ/QFPROM, not in any partition (:doc:`/open-questions`), so their live values are not statically recoverable from this dump. Failure behavior ================ After ``avb_slot_verify`` returns, the result is classified by the inlined ``ResultShouldContinue`` and combined with the lock state: .. list-table:: :header-rows: 1 :widths: 40 30 30 * - ``AvbSlotVerifyResult`` - ``ResultShouldContinue`` - Effect * - ``OOM`` / ``IO`` / ``INVALID_METADATA`` / ``UNSUPPORTED_VERSION`` / ``INVALID_ARGUMENT`` - **False** - Fatal even when unlocked → ``BootState = RED``. * - ``VERIFICATION`` / ``ROLLBACK_INDEX`` / ``PUBLIC_KEY_REJECTED`` - **True** - Continue **only if unlocked** (``AllowVerificationError``); otherwise ``BootState = RED``. * - ``OK`` - **True** - Verified. So the branch is: **locked + any non-OK result → RED** (the ``ERROR: Device State Locked, AvbSlotVerify returned %a`` path); **unlocked + continuable result → warn and proceed** (``State: Unlocked, AvbSlotVerify returned %a, continue boot``); a small class of structural errors (OOM/IO/…) halts regardless of lock state. Boot state, ROT and command line ================================ On success the colour is chosen purely from lock state and key origin:: if (AllowVerificationError) Info->BootState = ORANGE; // unlocked else if (UserData->IsUserKey) Info->BootState = YELLOW; // locked, custom key else Info->BootState = GREEN; // locked, OEM key The colour, the unlocked flag and the public key are pushed to Keymaster as the Root-of-Trust (``Data.Color`` / ``Data.IsUnlocked`` / ``Data.PublicKey``), and ``AppendVBCommonCmdLine`` / ``AppendVBCmdLine`` build the ``androidboot.verifiedbootstate=…`` command line that :doc:`/abl/bootlinux` splices in as ``VBCmdLine``. On a stock locked boot this device is **GREEN** (OEM key, locked) — consistent with the release-keys AVB metadata in :doc:`/boot/secure-boot`. Boot-state menu =============== The tail of ``LoadImageAndAuthVB2`` dispatches on the colour (``switch (BootState)``), calling ``DisplayVerifiedBootMenu`` (``VerifiedBootMenu.c``): .. list-table:: :header-rows: 1 :widths: 20 80 * - Colour - Action * - RED - ``DisplayVerifiedBootMenu (DISPLAY_MENU_RED)`` + ``"Your device is corrupt. It can't be trusted and will not boot."`` — **no** ``WaitForExitKeysDetection``; the device does not continue to boot. * - YELLOW - warning menu + ``WaitForExitKeysDetection`` (timed), then continue. * - ORANGE - unlocked-device warning + ``WaitForExitKeysDetection`` (timed), then continue. * - EIO - ``DISPLAY_MENU_EIO`` + ``WaitForExitKeysDetection``, then continue. RED is the only halting outcome; the others are advisory delays. The menu draw itself (``ID: %a``, key prompts) is in the menu/DrawUI cluster around RVA ``0x20ab8``. DeviceInfo lock-state enforcement (``DeviceInfo.c``) ==================================================== The lock state the AVB gate reads is managed by ``DeviceInfo.c``: - ``IsUnlocked ()`` / ``IsUnlockCritical ()`` return the cached ``DevInfo`` booleans, populated by ``DeviceInfoInit`` via ``ReadWriteDeviceInfo (READ_CONFIG, …)`` from the ``devinfo`` partition. - On first init the ``DEVICE_MAGIC`` (``"ANDROID-BOOT!"``) is checked; if absent the struct is (re)written with ``is_unlocked = FALSE`` / ``is_unlock_critical = FALSE`` by default (the ``TARGET_DEFINITION`` unlocked default is not taken here). - ``SetDeviceUnlockValue`` / the unlock-critical setter are the *only* writers of the lock booleans, each guarded (``if (IsUnlocked () != State)``) and persisted with ``ReadWriteDeviceInfo (WRITE_CONFIG, …)``. These setters are the enforcement point, but on this build no fastboot command reaches them (next section). ``devinfo.bin`` struct — byte offsets and values on this unit ---------------------------------------------------------------- The ABL ``device_info`` RAM struct (RVA ``0x60618``) is a 1:1 image of ``devinfo.bin`` (LUN4, 4 KB), magic ``ANDROID-BOOT!`` at ``+0``. Getters exist only for three fields: .. list-table:: :header-rows: 1 :widths: 14 30 56 * - Offset - Field - Value on this unit * - ``+0x0d`` - ``is_unlocked`` - byte 13 = **0 (LOCKED)** * - ``+0x0e`` - ``is_unlock_critical`` - byte 14 = 0 * - ``+0x0f`` - ``charger_screen_enabled`` - byte 15 = **1 (ENABLED)** There is no ``is_tampered``/``is_verified`` byte in this struct — byte 15 is ``charger_screen_enabled``, not a tamper flag; do not confuse the two. The rollback-index fields elsewhere in the struct are all-zero on this unit (factory state) — see `Rollback-index handling`_ above for where the live rollback counters actually live (TZ/QFPROM, not this struct's cache). ABL also sets HLOS tamper fuses via KeyMaster (``TZ_HLOS_IMG_TAMPER_FUSE``, ``KeyMasterSetRotAndBootState``) — a separate anti-rollback path from the AVB rollback-index mechanism above. The lock state here is independent of the QFPROM ``SEC_BOOT`` fuse — the device reports "locked" while the chain is still test-key-signed (:doc:`/boot/secure-boot`). UnlockMenu — plumbing present, no command path ============================================== ``UnlockMenu.c`` (``DisplayUnlockMenu`` and its confirm handler) is compiled in, and the internal unlock plumbing is present in the binary — ``IsAllowUnlock is %d`` (RVA ``0x2fe3c`` / ``0x30518``), the unlock-key-detect event and ``Failed to update the unlock status`` (RVA ``0x1fa00`` / ``0x20228``). But at full-function level nothing dispatches to it: there is no ``flashing unlock`` / ``oem unlock`` / ``get_unlock_ability`` command in the fastboot table (:doc:`/abl/container`), and ``LinuxLoaderEntry`` never calls ``DisplayUnlockMenu``. The unlock UI and ``SetDeviceUnlockValue`` writer exist but are unreachable through any exposed command — so ``is_unlocked`` cannot be flipped from fastboot on the stock image. This is independent of, and weaker than, the deeper test-key-signing bypass in :doc:`/boot/secure-boot`. Provenance ========== :Source: ``../artifacts/abl_a/abl_dxe_fv.bin`` (RVA ``0x5398`` ``LoadImageAndAuthVB2`` + boot-state dispatch; libavb cluster ``0x8478``/``0xad74``/``0xcf88``/``0xfdc8``/``0xf4dc``/``0xe48c``/``0xf4f4``…; unlock plumbing ``0x1fa00``/``0x2fe3c``). Structural correlation against ``QcomModulePkg/Library/avb/VerifiedBoot.c``, ``libavb/*``, ``QcomModulePkg/Library/BootLib/DeviceInfo.c``, ``VerifiedBootMenu.c``, ``UnlockMenu.c`` at CodeLinaro tag ``LA.UM.9.12.1.r2-05100-SMxx50.QSSI12.0`` (QSSI12; version gap is an open question — :doc:`/abl/function-map`, :doc:`/open-questions`). :Method: functions located by embedded ASSERT source paths and DEBUG strings (AArch64 ``adrp``/``add`` literals resolved with Capstone); the lock-state gate, ``ResultShouldContinue`` classification, colour selection and boot-state switch read from ``VerifiedBoot.c`` and matched to the ``0x5398`` body. ``IsUnlocked``/``SetDeviceUnlockValue`` semantics from ``DeviceInfo.c``. The ``0x5398`` boundary merge is a radare2 auto-analysis artifact. :Cross-refs: :doc:`/boot/secure-boot` (AVB metadata, keys, why unenforced), :doc:`/abl/container` (fastboot surface, no unlock command), :doc:`/abl/linuxloader` (caller), :doc:`/abl/bootlinux` (``VBCmdLine`` consumer), :doc:`/abl/function-map` (function inventory).