Files
AXP-Resurection/scripts/009-bacon-axp-stage1-combo4-cve-hi-releasekeys.sh
sunfish-adminandCursor 2d145c2878 009: Polish bacon fixes, OTA host scripts, and migration docs.
Restore disable_configstore, EuiccDisabler and thermal CDEV guards, branding
and rebuild/sync helpers; document device roles, laptop migration, and Gitea.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-16 21:06:45 +02:00

675 lines
30 KiB
Bash
Executable File

#!/bin/bash
# 009 bacon — ship pack with project release keys (US-S2).
# Stage1+combo4+FORTIFY + CVE clean + hi-batch1+2+3 + PRODUCT_DEFAULT_DEV_CERTIFICATE.
# CVE-2022-49980 ships as the lock-scope port; the AXP one-liner (uevent under
# udc_lock) stays denylisted because it hung LAB at the Google logo.
# Requires $HOME/.android-certs/releasekey.{pk8,x509.pem}.
# Lunch is lineage_sunfish-user (same flavor GOLDEN reports). PRO is branding,
# not a different lunch target.
set -e
cd /srv/rom/src/lineage
# Default ship keeps vbmeta flags 3. The isolated flags-0 candidate sets
# AXP_AVB_FLAGS=0, which exports WITH_AVB=true before lunch. That is the only
# effect in device/google/sunfish/BoardConfigLineage.mk: skip `--flags 3`.
AXP_AVB_FLAGS="${AXP_AVB_FLAGS:-3}"
if [[ "$AXP_AVB_FLAGS" == "0" ]]; then
export WITH_AVB=true
fi
echo "AXP_AVB_FLAGS=$AXP_AVB_FLAGS WITH_AVB=${WITH_AVB:-unset}"
export HOME=/home/android-build
export CCACHE_DIR=/srv/rom/ccache
export USE_CCACHE=1
export PATH=/home/android-build/bin:/usr/local/bin:/usr/bin:/bin:$PATH
export ANDROID_BUILD_TOP=/srv/rom/src/lineage
unset OUT_DIR || true
export SUNFISH_A16_MAGISK_PREROOT=1
export MAGISK_APK=/srv/rom/prebuilts/magisk/Magisk-v30.7.apk
LOGDIR=/srv/rom/logs/009-build
mkdir -p "$LOGDIR"
LOG=$LOGDIR/bacon-axp-stage1-combo4-cve-hi-releasekeys.log
exec > >(tee -a "$LOG") 2>&1
echo "=== START 009-bacon-axp-cve-hi-releasekeys $(date -Is) ==="
if [[ -z "${ANDROID_CERTS_DIR:-}" ]]; then
if [[ -f /srv/rom/secrets/android-certs/releasekey.pk8 ]]; then
CERT_DIR=/srv/rom/secrets/android-certs
else
CERT_DIR="$HOME/.android-certs"
fi
else
CERT_DIR="$ANDROID_CERTS_DIR"
fi
if [[ ! -f "$CERT_DIR/releasekey.pk8" || ! -f "$CERT_DIR/releasekey.x509.pem" ]]; then
echo "FAIL: missing $CERT_DIR/releasekey.{pk8,x509.pem}" >&2
exit 1
fi
export PRODUCT_DEFAULT_DEV_CERTIFICATE="$CERT_DIR/releasekey"
echo "OK: PRODUCT_DEFAULT_DEV_CERTIFICATE=$PRODUCT_DEFAULT_DEV_CERTIFICATE"
LIVE=kernel/google/msm-4.14
# Use the kernel revision selected by the pinned LOS manifest. Repeated bacons
# are safe because all project patches are uncommitted and HEAD stays at the
# upstream commit. AXP_KERNEL_BASE is only for an explicit historical bisect.
BASE=${AXP_KERNEL_BASE:-$(git -C "$LIVE" rev-parse HEAD)}
KH=vendor/sunfish-a16/patches/009-kernel-hardening
LIST_CLEAN="$KH/cve-clean-offset.list"
LIST_HI1="$KH/cve-high-offset-batch1.list"
LIST_HI2="$KH/cve-high-offset-batch2.list"
LIST_HI3="$KH/cve-high-offset-batch3.list"
test -f "$LIST_CLEAN"
test -f "$LIST_HI1"
test -f "$LIST_HI2"
test -f "$LIST_HI3"
test -d "$LIVE"
# Re-run ship gates + artifact publish from existing out/ (after a post-mka FAIL).
if [[ "${AXP_POSTFLIGHT_ONLY:-0}" == 1 ]]; then
echo "=== AXP_POSTFLIGHT_ONLY: ship gates + artifacts only $(date -Is) ==="
AXP_DEBLOB_WAVE4="${AXP_DEBLOB_WAVE4:-1}"
PROD=out/target/product/sunfish
shopt -s nullglob
_ota_zip=( "$PROD"/lineage-*-PRO-sunfish.zip )
shopt -u nullglob
if [[ ${#_ota_zip[@]} -lt 1 ]]; then
echo "FAIL: missing $PROD/lineage-*-PRO-sunfish.zip (run full bacon first)" >&2
exit 1
fi
_self="$(cd "$(dirname "$0")" && pwd)/$(basename "$0")"
_start=$(grep -n '^# Post-ship gates:' "$_self" | head -1 | cut -d: -f1)
[[ -n "$_start" ]] || { echo "FAIL: postflight marker missing in $_self" >&2; exit 1; }
_pf=$(mktemp /tmp/axp-postflight-XXXX.sh)
sed -n "${_start},\$p" "$_self" > "$_pf"
bash "$_pf"
ec_pf=$?
rm -f "$_pf"
exit "$ec_pf"
fi
echo "=== reset kernel @ $BASE ==="
git -C "$LIVE" reset --hard "$BASE"
git -C "$LIVE" clean -fdq
chmod +x "$KH"/apply_stage1_combo4.sh "$KH"/apply_cve_strict.sh \
"$KH"/apply_stage1.sh "$KH"/apply_combo4_fortify.sh 2>/dev/null || true
bash "$KH/apply_stage1_combo4.sh" "$LIVE"
# AXP hunks that no longer apply cleanly, ported onto this msm-4.14.
# misc/0021 stays omitted: LOS already has its KSYM_NAME_LEN=192 result.
# slab/0010 and slab/0018 are already covered by virt_to_cache() plus
# CONFIG_SLAB_FREELIST_HARDENED.
grep -q '^#define KSYM_NAME_LEN 192$' "$LIVE/include/linux/kallsyms.h" \
|| { echo "FAIL: KSYM_NAME_LEN is not 192 after Stage1" >&2; exit 1; }
for PORT in \
"$KH/ports/allocsize-0007-vmalloc-alloc-size.patch" \
"$KH/ports/ro-0007-0034-sysctl-read-only.patch"
do
test -f "$PORT"
patch -d "$LIVE" -p1 --forward --dry-run < "$PORT"
patch -d "$LIVE" -p1 --forward < "$PORT"
echo "applied $(basename "$PORT")"
done
# US-S5-1 — SLAB_FREELIST_RANDOM; keep Yama unset like GOLDEN/AXP.
chmod +x "$KH/apply_uss5_1_defconfig.sh" 2>/dev/null || true
bash "$KH/apply_uss5_1_defconfig.sh" "$LIVE"
export CVE_STRICT_LIST="$LIST_CLEAN"
unset CVE_STRICT_CONTINUE || true
export CVE_MAX_FUZZ=1
export CVE_MAX_ABS_OFFSET=200
bash "$KH/apply_cve_strict.sh" "$LIVE"
export CVE_MAX_ABS_OFFSET=9999
for L in "$LIST_HI1" "$LIST_HI2" "$LIST_HI3"; do
echo "=== high-offset $(basename "$L") ==="
export CVE_STRICT_LIST="$L"
bash "$KH/apply_cve_strict.sh" "$LIVE"
done
PORT49980="$KH/ports/cve-2022-49980-lock-scope.patch"
test -f "$PORT49980"
patch -d "$LIVE" -p1 --forward --dry-run < "$PORT49980"
patch -d "$LIVE" -p1 --forward < "$PORT49980"
grep -q 'device_add() emits a uevent' "$LIVE/drivers/usb/gadget/udc/core.c" \
|| { echo "FAIL: CVE-2022-49980 lock-scope missing" >&2; exit 1; }
echo "applied $(basename "$PORT49980")"
DEF=$LIVE/arch/arm64/configs/sunfish_defconfig
if grep -qE "^CONFIG_KSU.*=y" "$DEF"; then
echo "FAIL: KernelSU in defconfig" >&2
exit 1
fi
for k in FORTIFY_SOURCE INIT_ON_ALLOC_DEFAULT_ON INIT_ON_FREE_DEFAULT_ON \
SECURITY_DMESG_RESTRICT SLAB_FREELIST_HARDENED \
SLAB_FREELIST_RANDOM; do
grep -q "^CONFIG_${k}=y" "$DEF" || { echo "FAIL missing $k" >&2; exit 1; }
done
grep -q '^# CONFIG_SECURITY_YAMA is not set$' "$DEF" \
|| { echo "FAIL: SECURITY_YAMA still enabled (GOLDEN leaves it unset)" >&2; exit 1; }
grep -q '^# CONFIG_USERFAULTFD is not set$' "$DEF" \
|| { echo "FAIL: USERFAULTFD still enabled (GOLDEN leaves it unset)" >&2; exit 1; }
if grep -q '^CONFIG_USERFAULTFD=y$' "$DEF"; then
echo "FAIL: CONFIG_USERFAULTFD=y" >&2
exit 1
fi
grep -q "ret = __underlying_strlen(q);" "$LIVE/include/linux/string.h"
grep -q "mmget_still_valid" "$LIVE/mm/madvise.c"
echo "OK: Stage1+combo4+FORTIFY + CVE-clean + hi-batch1+2+3 markers present"
rm -f out/target/product/sunfish/obj/KERNEL_OBJ/arch/arm64/boot/Image.lz4 \
out/target/product/sunfish/obj/KERNEL_OBJ/arch/arm64/boot/Image \
out/target/product/sunfish/kernel
python3 vendor/sunfish-a16/patches/005-sigspoof/apply_005_sigspoof.py
python3 vendor/sunfish-a16/patches/008-call-rec-flag-secure/apply_008_call_rec_flag_secure.py
grep -q 'SUNFISH_A16_008_CALL_REC_FLAG_SECURE' \
frameworks/base/core/java/android/provider/Settings.java
grep -q 'extendrom_call_recording' \
packages/apps/Dialer/java/com/android/incallui/call/CallRecorder.java
grep -q 'extendrom_call_recording' \
packages/apps/Settings/res/xml/development_settings.xml
python3 vendor/sunfish-a16/patches/009-axp-los23-port/apply_009_disable_user_apps.py
python3 vendor/sunfish-a16/patches/009-axp-los23-port/apply_009_private_dns_providers.py
python3 vendor/sunfish-a16/patches/009-axp-los23-port/apply_009_connectivity_check.py
python3 vendor/sunfish-a16/patches/009-axp-los23-port/apply_009_supl_privacy.py
python3 vendor/sunfish-a16/patches/009-axp-los23-port/apply_009_product_includes.py
grep -q 'sunfish-a16-009-product-includes' device/google/sunfish/lineage_sunfish.mk
grep -q 'sunfish-a16-009-board-config' device/google/sunfish/BoardConfigLineage.mk
export ANDROID_CERTS_DIR="${ANDROID_CERTS_DIR:-/srv/rom/secrets/android-certs}"
python3 vendor/sunfish-a16/patches/009-axp-los23-port/apply_009_axp_identity.py
test -f vendor/sunfish-a16/certs/avb.pem
test -f vendor/sunfish-a16/certs/avb_pkmd.bin
test -f vendor/lineage-priv/keys/releasekey.pk8
python3 vendor/sunfish-a16/patches/009-axp-los23-port/apply_009_axp_branding.py
grep -q '^VENDOR_SECURITY_PATCH = 2023-09-05$' device/google/sunfish/device-common.mk
python3 vendor/sunfish-a16/patches/009-axp-los23-port/apply_009_yama_ptrace.py
python3 vendor/sunfish-a16/patches/009-axp-los23-port/apply_009_cmdline_mode.py
grep -q 'CHECKCALL(chmod("/proc/cmdline", 0440));' \
system/core/init/first_stage_init.cpp
python3 vendor/sunfish-a16/patches/009-axp-los23-port/apply_009_a15_small.py
python3 vendor/sunfish-a16/patches/009-axp-los23-port/apply_009_a15_reboot_timeouts.py
python3 vendor/sunfish-a16/patches/009-axp-los23-port/apply_009_a15_settings_timeouts.py
grep -q 'settings_reboot_after_timeout' \
frameworks/base/core/java/android/provider/Settings.java
grep -q 'isAllowedWhenLocked' \
frameworks/base/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
grep -q 'auto_reboot' packages/apps/Settings/res/xml/security_dashboard_settings.xml
grep -q 'bluetooth_timeout' packages/apps/Settings/res/xml/connected_devices.xml
grep -q 'wifi_timeout' packages/apps/Settings/res/xml/wifi_configure_settings.xml
grep -q 'MAX_FAILED_ATTEMPTS_LOCKOUT_PERMANENT = 5;' \
frameworks/base/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/LockoutFrameworkImpl.java
grep -q 'final String buildSerial = Build.UNKNOWN;' \
frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
grep -q 'AXP/Calyx 0042' \
frameworks/base/packages/SystemUI/src/com/android/systemui/screenshot/ImageExporter.java
grep -q 'Serial number constraint not permitted' \
bootable/recovery/install/install.cpp
# openeuicc-deps 5402167 was generated against SDK 37. LOS 23.2 ships
# prebuilts/sdk/37 without public/android.jar, so compile these prebuilt
# libraries against 36 (Android 16).
sed -i 's/^ sdk_version: "37",$/ sdk_version: "36",/' prebuilts/openeuicc-deps/Android.bp
if grep -q 'sdk_version: "37"' prebuilts/openeuicc-deps/Android.bp; then
echo "FAIL: openeuicc-deps still asks for SDK 37" >&2
exit 1
fi
python3 vendor/sunfish-a16/patches/009-axp-los23-port/apply_009_drop_openssh_packages.py
python3 vendor/sunfish-a16/patches/009-axp-los23-port/apply_009_drop_euicc_google.py
python3 vendor/sunfish-a16/patches/009-axp-los23-port/apply_009_euicc_disabler_guard.py
python3 vendor/sunfish-a16/patches/009-axp-los23-port/apply_009_thermal_cdev_log.py
python3 vendor/sunfish-a16/patches/009-axp-los23-port/apply_009_drop_carrier_google.py
python3 vendor/sunfish-a16/patches/009-axp-los23-port/apply_009_drop_lineage_tools.py
# Wave 4 (diag/bpftool/imsrcsd) is mandatory for OTAs.
# scripts/009-rebuild-from-los.sh and 009-bacon-hardened-malloc.sh set
# AXP_DEBLOB_WAVE4=1. Default 0 only for a debug bacon that is not an OTA.
AXP_DEBLOB_WAVE4="${AXP_DEBLOB_WAVE4:-0}"
if [[ "$AXP_DEBLOB_WAVE4" == 1 ]]; then
python3 vendor/sunfish-a16/patches/009-axp-los23-port/apply_009_drop_lab_only_commands.py
fi
# Jelly stays (owner ship gate). Gallery: LOS Gallery2 and/or Fossify — not gated.
# Host prebuilts → vendor/sunfish-a16/prebuilt (rsync --delete must not wipe these).
mkdir -p vendor/sunfish-a16/prebuilt
link_prebuilt() {
local name="$1" src="$2"
local dst="vendor/sunfish-a16/prebuilt/$name"
ln -sfn "$src" "$dst"
test -e "$dst" || { echo "FAIL: missing prebuilt $dst -> $src" >&2; exit 1; }
}
link_prebuilt Phonesky.apk /srv/rom/prebuilts/phonesky/Phonesky.apk
link_prebuilt GmsCore.apk /srv/rom/prebuilts/microg/GmsCore.apk
link_prebuilt GsfProxy.apk /srv/rom/prebuilts/microg/GsfProxy.apk
link_prebuilt AuroraStore.apk /srv/rom/prebuilts/axp-apps/AuroraStore.apk
link_prebuilt Droidify.apk /srv/rom/prebuilts/axp-apps/Droidify.apk
link_prebuilt FossifyGallery.apk /srv/rom/prebuilts/axp-apps/FossifyGallery.apk
# GmsCore JNI is deflated with extractNativeLibs=true. Without these files in
# /system_ext/app/GmsCore/lib/arm64, Conscrypt/Cronet fail and Google Maps stays brown.
GMS_JNI=vendor/sunfish-a16/prebuilt/GmsCore-jni/arm64
rm -rf "$GMS_JNI"
mkdir -p "$GMS_JNI"
unzip -q -j -o vendor/sunfish-a16/prebuilt/GmsCore.apk 'lib/arm64-v8a/*.so' -d "$GMS_JNI"
test -s "$GMS_JNI/libconscrypt_gmscore_jni.so" || {
echo "FAIL: GmsCore arm64 JNI not extracted into $GMS_JNI" >&2; exit 1; }
AAPT=/srv/rom/src/lineage/prebuilts/sdk/tools/linux/bin/aapt
"$AAPT" dump permissions vendor/sunfish-a16/prebuilt/Phonesky.apk 2>/dev/null | grep -q USE_EXACT_ALARM
# Early hook before version.mk (identity itself is apply_009_axp_identity.py).
mkdir -p vendor/extra
ln -sfn ../sunfish-a16/extra-product.mk vendor/extra/product.mk
test -f vendor/extra/product.mk
MAGISK_STAGE_DIR=/srv/rom/src/lineage/out/.magisk \
vendor/sunfish-a16/magisk/prepare_magisk.sh
python3 vendor/sunfish-a16/magisk/apply_releasetools_hook.py --top "$ANDROID_BUILD_TOP" --force
test -f out/.magisk/READY
rm -rf out/soong/.intermediates/build/make/tools/releasetools/releasetools_common
rm -rf out/soong/.intermediates/build/make/tools/releasetools/add_img_to_target_files
rm -rf out/target/product/sunfish/obj/PACKAGING/target_files_intermediates
rm -f out/target/product/sunfish/lineage-*.zip \
out/target/product/sunfish/boot.img \
out/target/product/sunfish/system/priv-app/Phonesky/Phonesky.apk
touch frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java
set +u
# shellcheck disable=SC1091
source build/envsetup.sh
set -e
# re-export after envsetup (some trees clear it). signing.mk sets the same path.
export PRODUCT_DEFAULT_DEV_CERTIFICATE=vendor/lineage-priv/keys/releasekey
breakfast sunfish user
test "${TARGET_RELEASE:-}" = "bp4a"
BT_SEPOL=$(get_build_var MAINLINE_BLUETOOTH_SEPOLICY_DEV_CERTIFICATES)
echo "MAINLINE_BLUETOOTH_SEPOLICY_DEV_CERTIFICATES=$BT_SEPOL"
case "$BT_SEPOL" in
*vendor/lineage-priv/keys*) ;;
*) echo "FAIL: BT sepolicy certs not vendor/lineage-priv/keys ($BT_SEPOL)" >&2; exit 1 ;;
esac
SYS_CERT=$(get_build_var DEFAULT_SYSTEM_DEV_CERTIFICATE)
B_TYPE=$(get_build_var LINEAGE_BUILDTYPE)
L_VER=$(get_build_var LINEAGE_VERSION)
echo "DEFAULT_SYSTEM_DEV_CERTIFICATE=$SYS_CERT LINEAGE_BUILDTYPE=$B_TYPE LINEAGE_VERSION=$L_VER"
case "$SYS_CERT" in
vendor/lineage-priv/keys/releasekey) ;;
*) echo "FAIL: system cert $SYS_CERT would tag dev-keys" >&2; exit 1 ;;
esac
[[ "$B_TYPE" == "PRO" ]] || { echo "FAIL: LINEAGE_BUILDTYPE=$B_TYPE (want PRO)" >&2; exit 1; }
case "$L_VER" in
*UNOFFICIAL*) echo "FAIL: version still UNOFFICIAL ($L_VER)" >&2; exit 1 ;;
esac
AVB_KEY=$(get_build_var BOARD_AVB_KEY_PATH)
AVB_ALG=$(get_build_var BOARD_AVB_ALGORITHM)
echo "BOARD_AVB_KEY_PATH=$AVB_KEY"
echo "BOARD_AVB_ALGORITHM=$AVB_ALG"
case "$AVB_KEY" in
*vendor/sunfish-a16/certs/avb.pem) ;;
*) echo "FAIL: AVB key not project certs/avb.pem ($AVB_KEY)" >&2; exit 1 ;;
esac
[[ "$AVB_ALG" == "SHA256_RSA4096" ]] || { echo "FAIL: AVB alg=$AVB_ALG" >&2; exit 1; }
PRODUCT_PKGS=$(get_build_var PRODUCT_PACKAGES)
for _gms in GmsCore GsfProxy Phonesky; do
case " $PRODUCT_PKGS " in
*" $_gms "*) ;;
*) echo "FAIL: PRODUCT_PACKAGES missing $_gms (gms.mk not inherited?)" >&2; exit 1 ;;
esac
done
for _app in AuroraStore Droidify FossifyGallery OpenEUICC; do
case " $PRODUCT_PKGS " in
*" $_app "*) ;;
*) echo "FAIL: PRODUCT_PACKAGES missing $_app (apps.mk not inherited?)" >&2; exit 1 ;;
esac
done
echo "OK: bundled apps in PRODUCT_PACKAGES (AuroraStore Droidify FossifyGallery OpenEUICC)"
echo "=== mka bacon $(date -Is) signing=$PRODUCT_DEFAULT_DEV_CERTIFICATE ==="
set -o pipefail
mka bacon -j8
ec=${PIPESTATUS[0]}
echo "bacon_exit=$ec $(date -Is)"
[ "$ec" -eq 0 ] || exit "$ec"
# Post-ship gates: drop pipefail. Early-exit awk/head + large pipes otherwise
# yield exit=141 (SIGPIPE) and skip Magisk/AVB/US-S4 checks.
set +o pipefail
PROD=out/target/product/sunfish
BID=$(date -u +%Y%m%dT%H%M%SZ)
DEST=/srv/rom/artifacts/009-axp-los23-port/${BID}-axp-stage1-combo4-cve-hi-releasekeys
mkdir -p "$DEST"
# Ship pair = target_files IMAGES (Magisk boot + matching vbmeta). OUT/*.img can be
# stock make images that do NOT match the OTA payload — do not prefer those.
TF_IMG=$(find "$PROD/obj/PACKAGING/target_files_intermediates" -type d -path '*/IMAGES' 2>/dev/null | head -1)
[[ -n "${TF_IMG:-}" && -d "$TF_IMG" ]] || { echo "FAIL: missing target_files IMAGES" >&2; exit 1; }
TF_IMG=$(readlink -f "$TF_IMG")
for f in boot.img vbmeta.img vbmeta_system.img dtbo.img; do
[[ -f "$TF_IMG/$f" ]] || { echo "FAIL: missing $TF_IMG/$f" >&2; exit 1; }
cp -v "$TF_IMG/$f" "$DEST/"
done
# Large partition images: hardlink into DEST so verify_image stays repeatable
# after OUT is cleaned (same FS). Fallback: absolute symlink, then full copy.
for f in vendor.img product.img system.img system_ext.img; do
[[ -f "$TF_IMG/$f" ]] || continue
rm -f "$DEST/$f"
if cp -al "$TF_IMG/$f" "$DEST/$f" 2>/dev/null; then
echo "hardlink $f -> $DEST/$f"
elif ln -sfn "$TF_IMG/$f" "$DEST/$f"; then
echo "symlink-abs $f -> $TF_IMG/$f"
else
cp -v "$TF_IMG/$f" "$DEST/$f"
fi
done
# Keep make OUT boot for forensics only (often stock / non-Magisk).
cp -v "$PROD/boot.img" "$DEST/boot-make-out.img" 2>/dev/null || true
cp -v "$PROD"/lineage-*-PRO-sunfish.zip "$DEST/" 2>/dev/null || \
cp -v "$PROD"/lineage-*.zip "$DEST/" 2>/dev/null || true
# Publish AXP Resurection Pro ship name (hardlink/copy). Lineage bacon.mk still
# emits lineage-*.zip; do not rebuild solely to rename.
ZIP_LINEAGE=$(ls "$DEST"/lineage-*.zip 2>/dev/null | head -1 || true)
if [[ -n "${ZIP_LINEAGE:-}" ]]; then
# lineage-23.2-YYYYMMDD-PRO-sunfish.zip
# → AXP-Resurection-Pro-23.2-YYYYMMDD-sunfish.zip
ZIP_BASE=$(basename "$ZIP_LINEAGE")
ZIP_AXP=$(echo "$ZIP_BASE" | sed -E \
's/^lineage-([0-9]+\.[0-9]+)-([0-9]{8})(_[0-9]{6})?-PRO-sunfish\.zip$/AXP-Resurection-Pro-\1-\2\3-sunfish.zip/')
if [[ "$ZIP_AXP" == "$ZIP_BASE" ]]; then
ZIP_AXP="AXP-Resurection-Pro-${ZIP_BASE#lineage-}"
fi
ln -f "$ZIP_LINEAGE" "$DEST/$ZIP_AXP" 2>/dev/null || cp -v "$ZIP_LINEAGE" "$DEST/$ZIP_AXP"
echo "ship_zip=$ZIP_AXP (from $ZIP_BASE)"
fi
WLAN=$PROD/vendor/lib/modules/wlan.ko
if [ -f "$WLAN" ] && [ -f "$PROD/obj/KERNEL_OBJ/Module.symvers" ]; then
KCRC=$(awk '$2=="kmalloc_caches"{print $1; exit}' "$PROD/obj/KERNEL_OBJ/Module.symvers")
MCRC=$(/sbin/modprobe --dump-modversions "$WLAN" 2>/dev/null | awk '$2=="kmalloc_caches"{print $1; exit}')
echo "wlan kmalloc_caches kernel=$KCRC module=$MCRC"
fi
# Avoid pipefail SIGPIPE: never `strings | grep` on large boot.img.
_BOOT_STR=$(mktemp)
strings "$DEST/boot.img" >"$_BOOT_STR"
if grep -iE 'KernelSU|CONFIG_KSU_SUSFS' "$_BOOT_STR" >/dev/null; then
rm -f "$_BOOT_STR"
echo "FAIL: KernelSU in boot" >&2
exit 1
fi
rm -f "$_BOOT_STR"
# Magisk preroot must be present on the ship boot (magiskboot cpio test: 0=stock, 1=Magisk).
MAGISKBOOT="$ANDROID_BUILD_TOP/out/.magisk/magiskboot"
[[ -x "$MAGISKBOOT" ]] || { echo "FAIL: missing $MAGISKBOOT" >&2; exit 1; }
MB_TMP=$(mktemp -d)
cp "$DEST/boot.img" "$MB_TMP/boot.img"
set +e
( cd "$MB_TMP" && "$MAGISKBOOT" unpack boot.img >/dev/null )
( cd "$MB_TMP" && "$MAGISKBOOT" cpio ramdisk.cpio test )
MB_EC=$?
set -e
rm -rf "$MB_TMP"
# magiskboot cpio test: 0=stock, non-zero (usually 1) = Magisk present
[[ "$MB_EC" -ne 0 ]] || { echo "FAIL: ship boot is stock (magiskboot cpio test=$MB_EC); expected Magisk" >&2; exit 1; }
echo "magiskboot_cpio_test=$MB_EC (non-zero => Magisk present)"
for bp in "$PROD/system/build.prop" "$PROD/product/etc/build.prop"; do
[[ -f "$bp" ]] || continue
if grep -q '^ro\.[a-z.]*build\.tags=' "$bp" && ! grep -q '^ro\.[a-z.]*build\.tags=release-keys$' "$bp"; then
echo "FAIL: $bp build.tags is not release-keys" >&2
exit 1
fi
done
grep -q '^ro.lineage.releasetype=PRO$' "$PROD/product/etc/build.prop" || {
echo "FAIL: ro.lineage.releasetype is not PRO" >&2
exit 1
}
echo "identity OK (release-keys, ro.lineage.releasetype=PRO)"
ZIP=$(ls "$DEST"/lineage-*.zip 2>/dev/null | head -1 || true)
if [[ -n "$ZIP" ]]; then
unzip -p "$ZIP" META-INF/com/android/otacert 2>/dev/null | openssl x509 -noout -subject 2>/dev/null \
| tee "$DEST/otacert-subject.txt" || true
if grep -qi 'Test Keys\|Android\|AOSP' "$DEST/otacert-subject.txt" 2>/dev/null; then
echo "WARN: otacert subject looks like test/AOSP — check keys wire" | tee -a "$DEST/README.txt"
fi
fi
# US-S3 artifact gates — fail closed (signing infrastructure; NOT runtime AVB harden).
# flags=3 => HASHTREE_DISABLED|VERIFICATION_DISABLED (libavb); Magisk-friendly unlocked LAB.
AVBTOOL="$ANDROID_BUILD_TOP/out/host/linux-x86/bin/avbtool"
AVB_PEM="$ANDROID_BUILD_TOP/vendor/sunfish-a16/certs/avb.pem"
AVB_PKMD="$ANDROID_BUILD_TOP/vendor/sunfish-a16/certs/avb_pkmd.bin"
[[ -x "$AVBTOOL" ]] || { echo "FAIL: missing $AVBTOOL" >&2; exit 1; }
[[ -f "$DEST/vbmeta.img" ]] || { echo "FAIL: missing $DEST/vbmeta.img" >&2; exit 1; }
[[ -f "$DEST/boot.img" ]] || { echo "FAIL: missing $DEST/boot.img" >&2; exit 1; }
[[ -f "$AVB_PEM" ]] || { echo "FAIL: missing $AVB_PEM" >&2; exit 1; }
if [[ ! -f "$AVB_PKMD" ]]; then
"$AVBTOOL" extract_public_key --key "$AVB_PEM" --output "$AVB_PKMD"
fi
"$AVBTOOL" info_image --image "$DEST/vbmeta.img" | tee "$DEST/vbmeta-info.txt"
EXP=$(sha1sum "$AVB_PKMD" | awk '{print $1}')
GOT=$(awk '/^Public key \(sha1\):/{print $NF; exit}' "$DEST/vbmeta-info.txt")
FLAGS=$(awk '/^Flags:/{print $2; exit}' "$DEST/vbmeta-info.txt")
echo "avb_pubkey_sha1 artifact=$GOT project=$EXP flags=$FLAGS"
[[ -n "$GOT" ]] || { echo "FAIL: could not parse vbmeta public key sha1" >&2; exit 1; }
[[ "$GOT" == "$EXP" ]] || { echo "FAIL: vbmeta pubkey != project avb_pkmd.bin" >&2; exit 1; }
[[ "$FLAGS" == "$AXP_AVB_FLAGS" ]] || { echo "FAIL: expected vbmeta Flags $AXP_AVB_FLAGS, got '$FLAGS'" >&2; exit 1; }
# Cryptographic check against TF sibling images in $DEST (hardlinked/copied).
# Magisk boot footer is Algorithm NONE — chain trust is top-level vbmeta + project key.
# Does NOT mean the device will enforce AVB at boot (flags 3 disables that).
set +e
(
cd "$DEST"
"$AVBTOOL" verify_image --image vbmeta.img --key "$AVB_PEM" \
--expected_chain_partition "vbmeta_system:1:$AVB_PKMD"
) >"$DEST/vbmeta-verify.txt" 2>&1
ec_v=$?
set -e
cat "$DEST/vbmeta-verify.txt"
[[ "$ec_v" -eq 0 ]] || { echo "FAIL: avbtool verify_image vbmeta.img (TF Magisk boot chain)" >&2; exit 1; }
set +e
(
cd "$DEST"
"$AVBTOOL" verify_image --image vbmeta_system.img --key "$AVB_PEM"
) >"$DEST/vbmeta_system-verify.txt" 2>&1
ec_s=$?
set -e
cat "$DEST/vbmeta_system-verify.txt"
[[ "$ec_s" -eq 0 ]] || { echo "FAIL: avbtool verify_image vbmeta_system.img" >&2; exit 1; }
# Boot footer integrity (unsigned Magisk footer is OK); do not require --key match.
set +e
(
cd "$DEST"
"$AVBTOOL" verify_image --image boot.img
) >"$DEST/boot-verify.txt" 2>&1
ec_b=$?
set -e
cat "$DEST/boot-verify.txt"
[[ "$ec_b" -eq 0 ]] || { echo "FAIL: avbtool verify_image boot.img footer/hash" >&2; exit 1; }
# US-S4-1 — AXP PRO product surface (binaries gone; init.openssh.rc kept).
SIMG="$ANDROID_BUILD_TOP/out/host/linux-x86/bin/simg2img"
[[ -x "$SIMG" ]] || { echo "FAIL: missing $SIMG" >&2; exit 1; }
[[ -e "$DEST/product.img" ]] || { echo "FAIL: missing $DEST/product.img" >&2; exit 1; }
PROD_RAW=$(mktemp /tmp/axp-product-XXXX.img)
"$SIMG" "$DEST/product.img" "$PROD_RAW"
for p in /bin/sshd /bin/ssh /bin/scp /bin/sftp /bin/rsync /bin/start-ssh; do
if debugfs -R "stat $p" "$PROD_RAW" 2>&1 | grep -q 'Inode:'; then
rm -f "$PROD_RAW"
echo "FAIL: US-S4 expected absent $p on product.img" >&2
exit 1
fi
done
if ! debugfs -R "stat /etc/init/init.openssh.rc" "$PROD_RAW" 2>&1 | grep -q 'Inode:'; then
rm -f "$PROD_RAW"
echo "FAIL: US-S4 expected init.openssh.rc present (AXP surface)" >&2
exit 1
fi
echo "US-S4-1 product surface OK (no ssh/rsync bins; init.openssh.rc present)"
# Lineage tools absent on GOLDEN. curl and vim stay.
SYS_RAW=$(mktemp /tmp/axp-system-XXXX.img)
SYSEXT_RAW=$(mktemp /tmp/axp-systemext-XXXX.img)
"$SIMG" "$DEST/system.img" "$SYS_RAW"
"$SIMG" "$DEST/system_ext.img" "$SYSEXT_RAW"
if debugfs -R "stat /addon.d/50-lineage.sh" "$SYS_RAW" 2>&1 | grep -q 'Inode:'; then
rm -f "$PROD_RAW" "$SYS_RAW" "$SYSEXT_RAW"
echo "FAIL: expected system/addon.d/50-lineage.sh absent" >&2
exit 1
fi
if ! debugfs -R "stat /bin/curl" "$SYS_RAW" 2>&1 | grep -q 'Inode:'; then
rm -f "$PROD_RAW" "$SYS_RAW" "$SYSEXT_RAW"
echo "FAIL: expected system/bin/curl present" >&2
exit 1
fi
for p in /bin/bash /bin/htop /bin/nano /bin/unrar; do
if debugfs -R "stat $p" "$SYSEXT_RAW" 2>&1 | grep -q 'Inode:'; then
rm -f "$PROD_RAW" "$SYS_RAW" "$SYSEXT_RAW"
echo "FAIL: expected system_ext$p absent" >&2
exit 1
fi
done
if [[ "$AXP_DEBLOB_WAVE4" == 1 ]]; then
for p in /bin/bpftool /bin/alloctop /bin/pbtombstone /bin/apexd-lsof.sh; do
if debugfs -R "stat $p" "$SYS_RAW" 2>&1 | grep -q 'Inode:'; then
rm -f "$PROD_RAW" "$SYS_RAW" "$SYSEXT_RAW"
echo "FAIL: expected system$p absent" >&2
exit 1
fi
done
fi
if ! debugfs -R "stat /bin/vim" "$SYSEXT_RAW" 2>&1 | grep -q 'Inode:'; then
rm -f "$PROD_RAW" "$SYS_RAW" "$SYSEXT_RAW"
echo "FAIL: expected system_ext/bin/vim present" >&2
exit 1
fi
if ! debugfs -R "stat /app/GmsCore/lib/arm64/libconscrypt_gmscore_jni.so" "$SYSEXT_RAW" 2>&1 | grep -q 'Inode:'; then
rm -f "$PROD_RAW" "$SYS_RAW" "$SYSEXT_RAW"
echo "FAIL: expected system_ext/app/GmsCore/lib/arm64 JNI (GOLDEN layout)" >&2
exit 1
fi
rm -f "$SYS_RAW" "$SYSEXT_RAW"
echo "lineage tools surface OK (no bash/htop/nano/unrar/addon.d; curl+vim kept)"
if [[ "$AXP_DEBLOB_WAVE4" == 1 ]]; then
VENDOR_RAW=$(mktemp /tmp/axp-vendor-XXXX.img)
# target_files can ship vendor.img as raw ext4; simg2img rejects non-sparse input.
if [[ "$(od -An -tx4 -N4 "$DEST/vendor.img" | tr -d ' ')" == ed26ff3a ]]; then
"$SIMG" "$DEST/vendor.img" "$VENDOR_RAW"
else
cp "$DEST/vendor.img" "$VENDOR_RAW"
fi
for p in /bin/cnss_diag /bin/diag_klog /bin/diag_mdlog /bin/diag_socket_log /bin/diag_uart_log /bin/ssr_diag /bin/test_diag /bin/imsrcsd /etc/init/imsrcsd.rc; do
if debugfs -R "stat $p" "$VENDOR_RAW" 2>&1 | grep -q 'Inode:'; then
rm -f "$PROD_RAW" "$VENDOR_RAW"
echo "FAIL: expected vendor$p absent" >&2
exit 1
fi
done
# disable_configstore must override HIDL configstore HAL (A16 VINTF has no
# ISurfaceFlingerConfigs). Expect stub present; real HAL + init rc absent.
for p in /bin/hw/android.hardware.configstore@1.1-service /etc/init/android.hardware.configstore@1.1-service.rc; do
if debugfs -R "stat $p" "$VENDOR_RAW" 2>&1 | grep -q 'Inode:'; then
rm -f "$PROD_RAW" "$VENDOR_RAW"
echo "FAIL: expected vendor$p absent (use disable_configstore override)" >&2
exit 1
fi
done
if ! debugfs -R "stat /bin/disable_configstore" "$VENDOR_RAW" 2>&1 | grep -q 'Inode:'; then
rm -f "$PROD_RAW" "$VENDOR_RAW"
echo "FAIL: expected vendor/bin/disable_configstore present (HAL override)" >&2
exit 1
fi
for p in /bin/loc_launcher /bin/lowi-server /bin/xtra-daemon; do
if ! debugfs -R "stat $p" "$VENDOR_RAW" 2>&1 | grep -q 'Inode:'; then
rm -f "$PROD_RAW" "$VENDOR_RAW"
echo "FAIL: expected vendor$p present" >&2
exit 1
fi
done
rm -f "$VENDOR_RAW"
echo "lab-only commands surface OK (diag/imsrcsd gone; configstore HAL overridden; GPS kept)"
fi
# US-S4-2 — ship gate: Jelly browser on product (owner requirement). Gallery app
# (LOS Gallery2 vs Fossify) is not gated; either is fine.
if ! debugfs -R "stat /app/Jelly/Jelly.apk" "$PROD_RAW" 2>&1 | grep -q 'Inode:'; then
rm -f "$PROD_RAW"
echo "FAIL: US-S4-2 expected Jelly.apk on product.img" >&2
exit 1
fi
for _bundled in AuroraStore Droidify FossifyGallery; do
if debugfs -R "stat /app/${_bundled}/${_bundled}.apk" "$PROD_RAW" 2>&1 | grep -q 'Inode:'; then
rm -f "$PROD_RAW"
echo "FAIL: US-S4-3 AXP parity: ${_bundled} must not be on product.img" >&2
exit 1
fi
done
SYSEXT_RAW=$(mktemp /tmp/axp-systemext-XXXX.img)
"$SIMG" "$DEST/system_ext.img" "$SYSEXT_RAW"
for _bundled in AuroraStore Droidify FossifyGallery; do
if ! debugfs -R "stat /app/${_bundled}/${_bundled}.apk" "$SYSEXT_RAW" 2>&1 | grep -q 'Inode:'; then
rm -f "$PROD_RAW" "$SYSEXT_RAW"
echo "FAIL: US-S4-3 expected system_ext/app/${_bundled}/${_bundled}.apk" >&2
exit 1
fi
done
rm -f "$PROD_RAW" "$SYSEXT_RAW"
echo "US-S4-2/3 surface OK (Jelly product + Aurora/Droidify/FossifyGallery system_ext)"
{
echo "artifact=$DEST"
echo "list_clean=$LIST_CLEAN (429)"
echo "list_hi=batch1+2+3"
echo "kernel_stage1=30 (misc/0021 omitted; base KSYM_NAME_LEN=192 retained)"
echo "hardening=Stage1+combo4+FORTIFY + CVE-clean + hi-batch1+2+3 + US-S4-1-no-openssh + US-S4-2-jelly + US-S5-1-slabrand-no-yama + CVE-2022-49980-lock-scope"
echo "vendor_security_patch=2023-09-05"
echo "branding=AXP-Resurection-Pro"
echo "signing=$PRODUCT_DEFAULT_DEV_CERTIFICATE"
echo "avb_infra=$AVB_KEY alg=$AVB_ALG flags=$AXP_AVB_FLAGS"
echo "avb_runtime_enforce=no (unlocked Magisk track; not AVB-hardened boot)"
echo "avb_pubkey_sha1=$GOT"
echo "magiskboot_cpio_test=$MB_EC"
echo "ship_images=target_files/IMAGES (not OUT make images)"
if [[ "$AXP_DEBLOB_WAVE4" == 1 ]]; then
echo "deblob=wave1-euicc-google+wave2-carrier-google-and-aosp-rcs+wave3-lineage-tools+wave4-lab-only-commands"
else
echo "deblob=wave1-euicc-google+wave2-carrier-google-and-aosp-rcs+wave3-lineage-tools"
fi
echo "ship_zip_brand=AXP-Resurection-Pro-*-sunfish.zip (sibling of lineage-*.zip; not slim)"
echo "flash=sideload AXP-Resurection-Pro zip (or lineage twin); boot-only insufficient"
echo "recover=20261002T085915Z-axp-stage1-combo4-cve-hi-releasekeys"
echo "bootloader_lock=wont"
echo "oem_unlock=leave_enabled_escape_hatch_not_guaranteed_recovery"
date -u +"built=%Y-%m-%dT%H:%M:%SZ"
} | tee "$DEST/README.txt"
(cd "$DEST" && sha256sum boot.img boot-make-out.img vbmeta.img vbmeta_system.img dtbo.img \
lineage-*.zip AXP-Resurection-Pro-*.zip 2>/dev/null || true) \
| tee "$DEST/SHA256SUMS"
# AXP publishes a hash file next to the zip and a detached signature of that
# file. Sign only when this user already has a secret key.
shopt -s nullglob
for pro in "$DEST"/AXP-Resurection-Pro-*.zip; do
(cd "$DEST" && sha512sum "$(basename "$pro")") | tee "$pro.sha512sum"
if gpg --batch --list-secret-keys 2>/dev/null | grep -q '^sec'; then
gpg --batch --yes --detach-sign --armor -o "$pro.sha512sum.asc" "$pro.sha512sum" \
|| echo "WARN: gpg detach-sign failed for $pro.sha512sum"
else
echo "SKIP: no GPG secret key; left $pro.sha512sum unsigned"
fi
done
shopt -u nullglob
echo "FLASH_READY $DEST"
echo "=== DONE $DEST $(date -Is) ==="
exit 0