2.4. AVB enforcement code path (LoadImageAndAuth / libavb)

This page documents the runtime Android Verified Boot enforcement inside ABL: the LoadImageAndAuthavb_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:

This page cross-references those and does not restate them. RVAs are LinuxLoader-relative (= DXE file offset − 0xb8).

2.4.1. LoadImageAndAuth (VB2 core, RVA 0x5398)

LinuxLoaderEntry calls LoadImageAndAuth before BootLinux (ABL entry point and top-level orchestration). 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:

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.

2.4.2. Lock-state gate

Everything hinges on IsUnlocked () (DeviceInfo.c), which returns DevInfo.is_unlocked. This unit reads is_unlocked = 0locked (Secure boot and image signing, 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 (Secure boot and image signing), 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.

2.4.3. 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:

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 Kernel/DTB/ramdisk load and the kernel hand-off.

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 Secure boot and image signing (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.

2.4.4. 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 (Open questions and limits of analysis), so their live values are not statically recoverable from this dump.

2.4.5. Failure behavior

After avb_slot_verify returns, the result is classified by the inlined ResultShouldContinue and combined with the lock state:

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.

2.4.6. 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 Kernel/DTB/ramdisk load and the kernel hand-off splices in as VBCmdLine. On a stock locked boot this device is GREEN (OEM key, locked) — consistent with the release-keys AVB metadata in Secure boot and image signing.

2.4.7. Boot-state menu

The tail of LoadImageAndAuthVB2 dispatches on the colour (switch (BootState)), calling DisplayVerifiedBootMenu (VerifiedBootMenu.c):

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.

2.4.8. 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).

2.4.8.1. 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:

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 (Secure boot and image signing).

2.4.9. 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 (ABL overview, key handling, fastboot), 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 Secure boot and image signing.

2.4.10. 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 — ABL function map and upstream-source correlation, Open questions and limits of analysis).

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:

Secure boot and image signing (AVB metadata, keys, why unenforced), ABL overview, key handling, fastboot (fastboot surface, no unlock command), ABL entry point and top-level orchestration (caller), Kernel/DTB/ramdisk load and the kernel hand-off (VBCmdLine consumer), ABL function map and upstream-source correlation (function inventory).