Created
May 5, 2026 12:01
-
-
Save khoipro/fbcbfa478c3fd58b001a2dc3d620bcdf to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/bin/bash | |
| # CVE-2026-31431 ("Copy Fail") — pure-bash static vulnerability checker | |
| # | |
| # Exit codes: | |
| # 0 = NOT VULNERABLE (patched kernel or preconditions not met) | |
| # 1 = INCONCLUSIVE | |
| # 2 = LIKELY VULNERABLE | |
| # 3 = MITIGATED (workaround applied, kernel not yet patched) | |
| # | |
| # Use only on hosts you own or are explicitly authorized to test. | |
| KERNEL=$(uname -r) | |
| ARCH=$(uname -m) | |
| ALG_NAME="authencesn(hmac(sha256),cbc(aes))" | |
| RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m' | |
| BOLD='\033[1m'; DIM='\033[2m'; NC='\033[0m' | |
| # ─── result variables ───────────────────────────────────────────────────────── | |
| R_KVER="" # patched | vulnerable | not_affected | unknown | |
| R_KVER_NOTE="" | |
| R_CFG="" # m | y | not_set | unknown | |
| R_MOD="" # loaded | on_disk | absent | |
| R_AF_ALG="" # available | unavailable | |
| R_CRYPTO="" # present | absent | |
| R_WORKAROUND="" # blacklist | cmdline | both | none | |
| R_WA_NOTE="" | |
| R_CONTAINER="" # no | docker | container | systemd-nspawn | |
| # ─── checks (silent) ───────────────────────────────────────────────────────── | |
| check_kernel_version() { | |
| local rel="${KERNEL%%-*}" | |
| local major minor patch | |
| IFS='.' read -r major minor patch <<< "$rel" | |
| patch="${patch%%[^0-9]*}" | |
| major="${major:-0}"; minor="${minor:-0}"; patch="${patch:-0}" | |
| if [ "$major" -lt 4 ] || { [ "$major" -eq 4 ] && [ "$minor" -lt 14 ]; }; then | |
| R_KVER="not_affected"; R_KVER_NOTE="< 4.14, predates vulnerable code"; return | |
| fi | |
| # ── RHEL-family kernels ────────────────────────────────────────────────── | |
| # Format: major.minor.0-BUILD.elX[_Y].ARCH (e.g. 5.14.0-214.el9.x86_64) | |
| # The upstream sublevel is always .0; the RHEL build number carries the | |
| # real patch level. We cannot compare patch against the upstream LTS | |
| # table, so we check the RPM changelog for the actual CVE fix. | |
| if echo "$KERNEL" | grep -qE "\.el[0-9]"; then | |
| local rhel_build el_tag kern_pkg | |
| rhel_build=$(echo "$KERNEL" | sed 's/.*-\([0-9]*\)\..*/\1/') | |
| el_tag=$(echo "$KERNEL" | grep -oE "el[0-9]+(_[0-9]+)?" | head -1) | |
| if command -v rpm >/dev/null 2>&1; then | |
| # Find the exact RPM that owns this kernel's vmlinuz | |
| kern_pkg=$(rpm -qf "/boot/vmlinuz-${KERNEL}" 2>/dev/null | head -1) | |
| # Fallback: kernel-core package by NEVRA | |
| [ -z "$kern_pkg" ] && \ | |
| kern_pkg=$(rpm -qa 2>/dev/null | grep -E "^kernel(-core)?-" \ | |
| | grep "${KERNEL%.*}" | head -1) | |
| if [ -n "$kern_pkg" ]; then | |
| if rpm -q --changelog "$kern_pkg" 2>/dev/null \ | |
| | grep -qF "CVE-2026-31431"; then | |
| R_KVER="patched" | |
| R_KVER_NOTE="RHEL backport confirmed (${el_tag} build ${rhel_build})" | |
| else | |
| R_KVER="vulnerable" | |
| R_KVER_NOTE="RHEL ${el_tag} build ${rhel_build} — CVE-2026-31431 not in kernel changelog" | |
| fi | |
| else | |
| # Package query failed (container without /boot, or non-RPM overlay) | |
| R_KVER="unknown" | |
| R_KVER_NOTE="RHEL ${el_tag} — run: rpm -q --changelog kernel-core-\$(uname -r) | grep CVE-2026-31431" | |
| fi | |
| else | |
| R_KVER="unknown" | |
| R_KVER_NOTE="RHEL-family ${el_tag} build ${rhel_build} — check Red Hat advisory for CVE-2026-31431" | |
| fi | |
| return | |
| fi | |
| # ── Upstream LTS version table ─────────────────────────────────────────── | |
| local patched=0 min_patch="" | |
| case "$major.$minor" in | |
| 5.10) min_patch=254; [ "$patch" -ge 254 ] && patched=1 ;; | |
| 5.15) min_patch=204; [ "$patch" -ge 204 ] && patched=1 ;; | |
| 6.1) min_patch=170; [ "$patch" -ge 170 ] && patched=1 ;; | |
| 6.6) min_patch=137; [ "$patch" -ge 137 ] && patched=1 ;; | |
| 6.12) min_patch=85; [ "$patch" -ge 85 ] && patched=1 ;; | |
| 6.18) min_patch=22; [ "$patch" -ge 22 ] && patched=1 ;; | |
| 6.19) min_patch=12; [ "$patch" -ge 12 ] && patched=1 ;; | |
| esac | |
| if [ "$patched" -eq 1 ]; then | |
| R_KVER="patched"; R_KVER_NOTE="patched release" | |
| elif [ -n "$min_patch" ]; then | |
| R_KVER="vulnerable"; R_KVER_NOTE="vulnerable, patched >= $major.$minor.$min_patch" | |
| elif { [ "$major" -gt 6 ] || { [ "$major" -eq 6 ] && [ "$minor" -ge 20 ]; }; }; then | |
| # Beyond the highest tracked LTS — may or may not be patched | |
| R_KVER="unknown"; R_KVER_NOTE="beyond tracked versions — check distro advisory" | |
| else | |
| # 4.14–6.19 non-LTS or EOL: in vulnerable range, no upstream fix for this series | |
| R_KVER="vulnerable" | |
| R_KVER_NOTE="non-LTS/EOL upstream series ${major}.${minor} — no upstream fix available" | |
| fi | |
| } | |
| check_kernel_config() { | |
| local val | |
| val=$(grep -E "^CONFIG_CRYPTO_USER_API_AEAD=" /boot/config-"$KERNEL" 2>/dev/null \ | |
| || zcat /proc/config.gz 2>/dev/null | grep -E "^CONFIG_CRYPTO_USER_API_AEAD=") | |
| case "$val" in | |
| *=m) R_CFG="m" ;; | |
| *=y) R_CFG="y" ;; | |
| "") | |
| # Distinguish: config file readable but key absent = not compiled | |
| # config file unreadable = unknown state | |
| if [ -r "/boot/config-${KERNEL}" ] || [ -r /proc/config.gz ]; then | |
| R_CFG="not_set" | |
| else | |
| R_CFG="unknown" | |
| fi | |
| ;; | |
| *) R_CFG="unknown" ;; | |
| esac | |
| } | |
| check_module() { | |
| if lsmod 2>/dev/null | grep -q "^algif_aead"; then | |
| R_MOD="loaded"; return | |
| fi | |
| local ko | |
| ko=$(find /lib/modules/"$KERNEL" -name "algif_aead.ko*" 2>/dev/null | head -1) | |
| [ -n "$ko" ] && R_MOD="on_disk" || R_MOD="absent" | |
| } | |
| check_af_alg() { | |
| if grep -qE "^ALG\b" /proc/net/protocols 2>/dev/null \ | |
| || lsmod 2>/dev/null | grep -q "^af_alg"; then | |
| R_AF_ALG="available" | |
| else | |
| R_AF_ALG="unavailable" | |
| fi | |
| } | |
| check_proc_crypto() { | |
| if [ ! -r /proc/crypto ]; then | |
| R_CRYPTO="absent"; return | |
| fi | |
| grep -qF "name : $ALG_NAME" /proc/crypto 2>/dev/null \ | |
| && R_CRYPTO="present" || R_CRYPTO="absent" | |
| } | |
| check_workaround() { | |
| local bl=0 cl=0 gb=0 | |
| # 1. modprobe.d blacklist (=m case, effective immediately after rmmod) | |
| if grep -rl "install algif_aead /bin/false" /etc/modprobe.d/ 2>/dev/null | grep -q .; then | |
| bl=1 | |
| fi | |
| # 2. initcall_blacklist active in CURRENT boot (=y case, effective now) | |
| grep -q "initcall_blacklist=algif_aead_init" /proc/cmdline 2>/dev/null && cl=1 | |
| # 3. initcall_blacklist configured in bootloader but NOT yet active (pending reboot) | |
| if [ $cl -eq 0 ]; then | |
| if command -v grubby >/dev/null 2>&1 \ | |
| && grubby --info=ALL 2>/dev/null \ | |
| | grep -qF "initcall_blacklist=algif_aead_init"; then | |
| gb=1 | |
| elif grep -q "initcall_blacklist=algif_aead_init" /etc/default/grub 2>/dev/null; then | |
| gb=1 | |
| fi | |
| fi | |
| if [ $bl -eq 1 ] && [ $cl -eq 1 ]; then | |
| R_WORKAROUND="both"; R_WA_NOTE="blacklist + cmdline" | |
| elif [ $bl -eq 1 ]; then | |
| R_WORKAROUND="blacklist"; R_WA_NOTE="/etc/modprobe.d/" | |
| elif [ $cl -eq 1 ]; then | |
| R_WORKAROUND="cmdline"; R_WA_NOTE="initcall_blacklist active in /proc/cmdline" | |
| elif [ $gb -eq 1 ]; then | |
| R_WORKAROUND="pending"; R_WA_NOTE="configured in bootloader — reboot required to activate" | |
| else | |
| R_WORKAROUND="none"; R_WA_NOTE="" | |
| fi | |
| } | |
| check_container() { | |
| if [ -f /.dockerenv ]; then | |
| R_CONTAINER="docker" | |
| elif grep -qE "lxc|kubepods|docker|containerd" /proc/1/cgroup 2>/dev/null; then | |
| R_CONTAINER="container" | |
| elif [ -n "${container:-}" ]; then | |
| R_CONTAINER="systemd-nspawn" | |
| else | |
| R_CONTAINER="no" | |
| fi | |
| } | |
| # ─── run all checks silently ───────────────────────────────────────────────── | |
| check_kernel_version | |
| check_kernel_config | |
| check_module | |
| check_af_alg | |
| check_proc_crypto | |
| check_workaround | |
| check_container | |
| # ─── helper: colored label ──────────────────────────────────────────────────── | |
| clabel() { | |
| # clabel COLOR "TEXT" | |
| printf "${1}%-12s${NC}" "$2" | |
| } | |
| ok() { clabel "$GREEN" "$1"; } | |
| bad() { clabel "$RED" "$1"; } | |
| meh() { clabel "$YELLOW" "$1"; } | |
| dim() { printf "${DIM}%s${NC}" "$1"; } | |
| # ─── status table ───────────────────────────────────────────────────────────── | |
| W=54 | |
| DIV=$(printf '─%.0s' $(seq 1 $W)) | |
| echo -e "${BOLD}CVE-2026-31431 \"Copy Fail\"${NC} · $KERNEL · $ARCH" | |
| echo "$DIV" | |
| printf " %-18s %-14s %s\n" "CHECK" "VALUE" "NOTE" | |
| echo "$DIV" | |
| # 1. Kernel version | |
| printf " %-18s " "Kernel" | |
| case "$R_KVER" in | |
| patched) ok "patched" ;; | |
| not_affected) ok "not affected" ;; | |
| vulnerable) bad "vulnerable" ;; | |
| *) meh "unknown" ;; | |
| esac | |
| printf " %s\n" "$(dim "$R_KVER_NOTE")" | |
| # 2. Kernel config | |
| printf " %-18s " "Config" | |
| case "$R_CFG" in | |
| not_set) ok "not set" ;; | |
| m) meh "=m" ;; | |
| y) bad "=y" ;; | |
| *) meh "unknown" ;; | |
| esac | |
| case "$R_CFG" in | |
| m) printf " %s\n" "$(dim "loadable module")" ;; | |
| y) printf " %s\n" "$(dim "built-in, rmmod N/A")" ;; | |
| not_set) printf " %s\n" "$(dim "not compiled")" ;; | |
| unknown) printf " %s\n" "$(dim "config file unreadable")" ;; | |
| *) printf "\n" ;; | |
| esac | |
| # 3. Module | |
| printf " %-18s " "algif_aead" | |
| case "$R_MOD" in | |
| loaded) bad "loaded" ;; | |
| on_disk) meh "not loaded" ;; | |
| absent) ok "absent" ;; | |
| esac | |
| case "$R_MOD" in | |
| on_disk) printf " %s\n" "$(dim "module file on disk")" ;; | |
| *) printf "\n" ;; | |
| esac | |
| # 4. AF_ALG | |
| printf " %-18s " "AF_ALG socket" | |
| case "$R_AF_ALG" in | |
| available) meh "available" ;; | |
| unavailable) ok "unavailable" ;; | |
| esac | |
| printf "\n" | |
| # 5. authencesn | |
| printf " %-18s " "authencesn" | |
| case "$R_CRYPTO" in | |
| present) bad "instantiated" ;; | |
| absent) ok "absent" ;; | |
| esac | |
| printf " %s\n" "$(dim "/proc/crypto")" | |
| # 6. Workaround | |
| printf " %-18s " "Workaround" | |
| case "$R_WORKAROUND" in | |
| both|blacklist|cmdline) ok "${R_WORKAROUND}" ;; | |
| pending) meh "pending" ;; | |
| none) bad "none" ;; | |
| esac | |
| [ -n "$R_WA_NOTE" ] && printf " %s\n" "$(dim "$R_WA_NOTE")" || printf "\n" | |
| # 7. Container (only shown when detected — skipping host means checks may mislead) | |
| if [ "$R_CONTAINER" != "no" ]; then | |
| printf " %-18s " "Environment" | |
| meh "$R_CONTAINER" | |
| printf " %s\n" "$(dim "running inside container — apply workaround on HOST")" | |
| fi | |
| echo "$DIV" | |
| # ─── verdict ───────────────────────────────────────────────────────────────── | |
| verdict() { | |
| if [ "$R_KVER" = "not_affected" ]; then | |
| printf " ${GREEN}${BOLD}NOT VULNERABLE${NC} kernel predates vulnerable code (< 4.14)\n" | |
| return 0 | |
| fi | |
| if [ "$R_KVER" = "patched" ]; then | |
| printf " ${GREEN}${BOLD}NOT VULNERABLE${NC} running a patched kernel\n" | |
| return 0 | |
| fi | |
| if [ "$R_CFG" = "not_set" ] && [ "$R_MOD" = "absent" ]; then | |
| printf " ${GREEN}${BOLD}NOT VULNERABLE${NC} algif_aead not compiled into this kernel\n" | |
| return 0 | |
| fi | |
| if [ "$R_WORKAROUND" = "pending" ] && [ "$R_MOD" != "loaded" ]; then | |
| printf " ${YELLOW}${BOLD}MITIGATED${NC} workaround configured — ${RED}REBOOT REQUIRED${NC} to activate\n" | |
| return 3 | |
| fi | |
| if [ "$R_WORKAROUND" != "none" ] && [ "$R_WORKAROUND" != "pending" ] && [ "$R_MOD" != "loaded" ]; then | |
| printf " ${YELLOW}${BOLD}MITIGATED${NC} workaround active — upgrade kernel to apply permanent fix\n" | |
| return 3 | |
| fi | |
| if [ "$R_AF_ALG" = "available" ] && [ "$R_CRYPTO" = "present" ] && [ "$R_MOD" = "loaded" ]; then | |
| printf " ${RED}${BOLD}LIKELY VULNERABLE${NC} all preconditions met — apply workaround or upgrade kernel\n" | |
| return 2 | |
| fi | |
| # Module on disk (=m) or built-in (=y) with no workaround: | |
| # any unprivileged user can trigger auto-load via AF_ALG socket — treat as exploitable | |
| if [ "$R_WORKAROUND" = "none" ] && { [ "$R_MOD" = "on_disk" ] || [ "$R_CFG" = "y" ]; }; then | |
| printf " ${RED}${BOLD}LIKELY VULNERABLE${NC} module loadable/built-in, no workaround — apply workaround or upgrade kernel\n" | |
| return 2 | |
| fi | |
| printf " ${YELLOW}${BOLD}INCONCLUSIVE${NC} not all preconditions confirmed — dynamic test recommended\n" | |
| return 1 | |
| } | |
| verdict | |
| EXIT_CODE=$? | |
| echo "$DIV" | |
| exit $EXIT_CODE |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/bin/bash | |
| # Workaround for CVE-2026-31431 ("Copy Fail") | |
| # Disables algif_aead to prevent page-cache corruption via AF_ALG splice. | |
| # Supports: Debian/Ubuntu, RHEL/CentOS/Fedora, Arch, SUSE, and derivatives. | |
| set -e | |
| # --------------------------------------------------------------------------- | |
| # Pre-flight: must run as root | |
| # --------------------------------------------------------------------------- | |
| if [ "$EUID" -ne 0 ]; then | |
| if command -v sudo >/dev/null 2>&1; then | |
| exec sudo "$0" "$@" | |
| else | |
| echo "[-] This script must be run as root." >&2 | |
| exit 1 | |
| fi | |
| fi | |
| # --------------------------------------------------------------------------- | |
| # Container detection — modprobe blacklist written here affects only THIS | |
| # container's namespace; it does NOT protect the host kernel. The built-in | |
| # (=y) GRUB path is also ineffective because /etc/default/grub inside the | |
| # container is not the host's bootloader config. | |
| # --------------------------------------------------------------------------- | |
| IS_CONTAINER=0 | |
| if [ -f /.dockerenv ] \ | |
| || grep -qE "lxc|kubepods|docker|containerd" /proc/1/cgroup 2>/dev/null \ | |
| || [ -n "${container:-}" ]; then | |
| IS_CONTAINER=1 | |
| echo "[!] Container environment detected." | |
| echo " Workaround applied here affects only this container's namespace." | |
| echo " Apply this script on the HOST system to protect the host kernel." | |
| echo "" | |
| fi | |
| # --------------------------------------------------------------------------- | |
| # Detect distro (for logging only — tool detection drives behaviour) | |
| # --------------------------------------------------------------------------- | |
| DISTRO="unknown" | |
| if [ -f /etc/os-release ]; then | |
| DISTRO=$(. /etc/os-release && echo "${NAME:-unknown}") | |
| fi | |
| # --------------------------------------------------------------------------- | |
| # Detect initramfs rebuild tool | |
| # --------------------------------------------------------------------------- | |
| if command -v dracut >/dev/null 2>&1; then | |
| INITRAMFS_CMD="dracut -f" | |
| elif command -v update-initramfs >/dev/null 2>&1; then | |
| INITRAMFS_CMD="update-initramfs -u" | |
| elif command -v mkinitcpio >/dev/null 2>&1; then | |
| INITRAMFS_CMD="mkinitcpio -P" | |
| elif command -v mkinitfs >/dev/null 2>&1; then | |
| INITRAMFS_CMD="mkinitfs" # Alpine Linux | |
| else | |
| INITRAMFS_CMD="" | |
| fi | |
| # --------------------------------------------------------------------------- | |
| # Detect GRUB config tool and target config path | |
| # --------------------------------------------------------------------------- | |
| if command -v grub2-mkconfig >/dev/null 2>&1; then | |
| GRUB_MKCFG="grub2-mkconfig" | |
| elif command -v grub-mkconfig >/dev/null 2>&1; then | |
| GRUB_MKCFG="grub-mkconfig" | |
| else | |
| GRUB_MKCFG="" | |
| fi | |
| # grubby is the preferred kernel-cmdline tool on RHEL/CentOS/Fedora; | |
| # it handles both legacy GRUB and BLS (Boot Loader Specification) entries. | |
| if command -v grubby >/dev/null 2>&1; then | |
| GRUB_KERNEL_TOOL="grubby" | |
| else | |
| GRUB_KERNEL_TOOL="grub" | |
| fi | |
| detect_grub_cfg_path() { | |
| if [ -d /sys/firmware/efi ]; then | |
| # Try distro-specific EFI path first to avoid picking the wrong entry | |
| # when multiple distros share the same EFI partition. | |
| local distro_id cfg | |
| distro_id=$(. /etc/os-release 2>/dev/null && echo "${ID:-}" || echo "") | |
| if [ -n "$distro_id" ] && [ -f "/boot/efi/EFI/${distro_id}/grub.cfg" ]; then | |
| echo "/boot/efi/EFI/${distro_id}/grub.cfg" | |
| return | |
| fi | |
| # Exclude the generic BOOT fallback directory to avoid stale entries. | |
| cfg=$(find /boot/efi/EFI -maxdepth 2 -name "grub.cfg" 2>/dev/null \ | |
| | grep -iv "/BOOT/" | head -1) | |
| [ -z "$cfg" ] && cfg=$(find /boot/efi -name "grub.cfg" 2>/dev/null | head -1) | |
| if [ -z "$cfg" ]; then | |
| echo "[-] UEFI boot detected but no grub.cfg found under /boot/efi" >&2 | |
| exit 1 | |
| fi | |
| echo "$cfg" | |
| elif [ -f /boot/grub2/grub.cfg ]; then | |
| echo "/boot/grub2/grub.cfg" | |
| elif [ -f /boot/grub/grub.cfg ]; then | |
| echo "/boot/grub/grub.cfg" | |
| else | |
| echo "[-] Cannot locate grub.cfg — searched /boot/grub2 and /boot/grub" >&2 | |
| exit 1 | |
| fi | |
| } | |
| # --------------------------------------------------------------------------- | |
| # Read kernel config | |
| # --------------------------------------------------------------------------- | |
| KERNEL=$(uname -r) | |
| CONFIG_VAL=$(grep -E "^CONFIG_CRYPTO_USER_API_AEAD=" /boot/config-"$KERNEL" 2>/dev/null \ | |
| || zcat /proc/config.gz 2>/dev/null | grep -E "^CONFIG_CRYPTO_USER_API_AEAD=" \ | |
| || echo "NOT_FOUND") | |
| echo "[*] Distro : $DISTRO" | |
| echo "[*] Kernel : $KERNEL" | |
| echo "[*] CONFIG_CRYPTO_USER_API_AEAD: $CONFIG_VAL" | |
| echo "[*] initramfs tool: ${INITRAMFS_CMD:-NOT FOUND}" | |
| echo "[*] GRUB mkconfig : ${GRUB_MKCFG:-NOT FOUND}" | |
| echo "[*] kernel cmdline: ${GRUB_KERNEL_TOOL}" | |
| echo "" | |
| # --------------------------------------------------------------------------- | |
| case "$CONFIG_VAL" in | |
| CONFIG_CRYPTO_USER_API_AEAD=m) | |
| echo "[*] Module mode — rmmod + blacklist + initramfs rebuild" | |
| # Unload with modprobe -r to handle dependencies gracefully. | |
| # Do NOT exit if unload fails (e.g. module in use, container) — | |
| # the blacklist still prevents reload after next reboot. | |
| if lsmod | grep -q "^algif_aead"; then | |
| if modprobe -r algif_aead 2>/dev/null; then | |
| echo "[+] algif_aead unloaded" | |
| else | |
| echo "[!] Could not unload algif_aead (module in use or insufficient privileges)" | |
| echo " Blacklist will prevent reload. Reboot to fully apply." | |
| fi | |
| else | |
| echo "[*] algif_aead not currently loaded, skipping unload" | |
| fi | |
| BLACKLIST_FILE="/etc/modprobe.d/disable-algif_aead.conf" | |
| if [ -f "$BLACKLIST_FILE" ]; then | |
| echo "[*] Blacklist already present at $BLACKLIST_FILE" | |
| else | |
| echo "install algif_aead /bin/false" | tee "$BLACKLIST_FILE" > /dev/null | |
| echo "[+] Blacklisted at $BLACKLIST_FILE" | |
| if [ -z "$INITRAMFS_CMD" ]; then | |
| echo "[-] No initramfs rebuild tool found (dracut / update-initramfs / mkinitcpio)." >&2 | |
| echo " Rebuild initramfs manually before next boot." >&2 | |
| else | |
| $INITRAMFS_CMD | |
| echo "[+] initramfs rebuilt ($INITRAMFS_CMD)" | |
| fi | |
| fi | |
| ;; | |
| CONFIG_CRYPTO_USER_API_AEAD=y) | |
| echo "[!] Built-in mode — must use initcall_blacklist via GRUB kernel cmdline" | |
| PARAM="initcall_blacklist=algif_aead_init" | |
| if [ "$GRUB_KERNEL_TOOL" = "grubby" ]; then | |
| # RHEL / CentOS / Fedora: grubby handles both legacy GRUB and BLS entries. | |
| if grubby --info=ALL 2>/dev/null | grep -qF "$PARAM"; then | |
| echo "[*] $PARAM already present in kernel args (grubby), skipping" | |
| else | |
| grubby --update-kernel=ALL --args="$PARAM" | |
| echo "[+] Added $PARAM to all kernel entries via grubby" | |
| echo "[+] Reboot to apply. Verify: cat /proc/cmdline | grep initcall_blacklist" | |
| fi | |
| else | |
| # Debian / Ubuntu / Arch / SUSE: edit /etc/default/grub then mkconfig | |
| GRUB_FILE="/etc/default/grub" | |
| if ! [ -f "$GRUB_FILE" ]; then | |
| echo "[-] $GRUB_FILE not found — cannot configure GRUB automatically." >&2 | |
| echo " Add '$PARAM' to your bootloader's kernel command line manually." >&2 | |
| exit 1 | |
| fi | |
| if grep -q "$PARAM" "$GRUB_FILE"; then | |
| echo "[*] $PARAM already present in $GRUB_FILE, skipping" | |
| else | |
| # Prefer GRUB_CMDLINE_LINUX (all entries) over _DEFAULT (default entry only) | |
| if grep -q "^GRUB_CMDLINE_LINUX=" "$GRUB_FILE"; then | |
| GRUB_VAR="GRUB_CMDLINE_LINUX" | |
| elif grep -q "^GRUB_CMDLINE_LINUX_DEFAULT=" "$GRUB_FILE"; then | |
| GRUB_VAR="GRUB_CMDLINE_LINUX_DEFAULT" | |
| else | |
| echo "[-] Neither GRUB_CMDLINE_LINUX nor GRUB_CMDLINE_LINUX_DEFAULT found in $GRUB_FILE" >&2 | |
| echo " Add '$PARAM' to your bootloader's kernel command line manually." >&2 | |
| exit 1 | |
| fi | |
| sed -i "s|\(${GRUB_VAR}=\"[^\"]*\)\"|\1 ${PARAM}\"|" "$GRUB_FILE" | |
| # Verify the parameter was actually inserted | |
| if ! grep -q "$PARAM" "$GRUB_FILE"; then | |
| echo "[-] sed substitution failed — $PARAM not found in $GRUB_FILE after edit." >&2 | |
| echo " Add '$PARAM' to ${GRUB_VAR} in $GRUB_FILE manually." >&2 | |
| exit 1 | |
| fi | |
| echo "[+] Added $PARAM to $GRUB_VAR in $GRUB_FILE" | |
| if [ -z "$GRUB_MKCFG" ]; then | |
| echo "[-] No GRUB config tool found (grub2-mkconfig / grub-mkconfig)." >&2 | |
| echo " Regenerate your GRUB config manually before rebooting." >&2 | |
| exit 1 | |
| fi | |
| GRUB_CFG=$(detect_grub_cfg_path) | |
| echo "[*] Regenerating GRUB config at $GRUB_CFG" | |
| $GRUB_MKCFG -o "$GRUB_CFG" | |
| echo "[+] GRUB updated — reboot required to apply" | |
| echo " After reboot, verify with: cat /proc/cmdline | grep initcall_blacklist" | |
| fi | |
| fi | |
| ;; | |
| NOT_FOUND) | |
| echo "[-] Kernel config not found." >&2 | |
| echo " Try: grep CONFIG_CRYPTO_USER_API_AEAD /boot/config-$KERNEL" >&2 | |
| exit 1 | |
| ;; | |
| *) | |
| echo "[-] Unexpected config value: $CONFIG_VAL" >&2 | |
| exit 1 | |
| ;; | |
| esac | |
| # --------------------------------------------------------------------------- | |
| # Verification | |
| # --------------------------------------------------------------------------- | |
| echo "" | |
| echo "[+] Done." | |
| case "$CONFIG_VAL" in | |
| CONFIG_CRYPTO_USER_API_AEAD=m) | |
| if lsmod | grep -q "^algif_aead"; then | |
| echo "[!] WARNING: algif_aead is still loaded — unload failed." | |
| else | |
| echo "[+] algif_aead is NOT loaded. Workaround active immediately." | |
| fi | |
| ;; | |
| CONFIG_CRYPTO_USER_API_AEAD=y) | |
| echo "[*] Built-in module — workaround takes effect after reboot." | |
| echo " Post-reboot check: cat /proc/cmdline | grep initcall_blacklist" | |
| ;; | |
| esac |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment