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