{"id":4156,"date":"2026-05-27T13:11:48","date_gmt":"2026-05-27T13:11:48","guid":{"rendered":"https:\/\/rssfeedtelegrambot.bnaya.co.il\/index.php\/2026\/05\/27\/mitigating-cve-2026-31431-copy-fail-in-docker-engine\/"},"modified":"2026-05-27T13:11:48","modified_gmt":"2026-05-27T13:11:48","slug":"mitigating-cve-2026-31431-copy-fail-in-docker-engine","status":"publish","type":"post","link":"https:\/\/rssfeedtelegrambot.bnaya.co.il\/index.php\/2026\/05\/27\/mitigating-cve-2026-31431-copy-fail-in-docker-engine\/","title":{"rendered":"Mitigating CVE-2026-31431 (\u201cCopy Fail\u201d) in Docker Engine"},"content":{"rendered":"<p>CVE-2026-31431 is a Linux kernel vulnerability that was recently disclosed. This CVE does not compromise Docker infrastructure.<\/p>\n<p>That said, Docker Engine\u2019s default profiles prior to v29.4.3 allowed containers to create AF_ALG sockets, which is the syscall surface the exploit uses. <strong>You are not exposed if you are running Docker Engine v29.4.3 or later, OR a patched host kernel.<\/strong> If either of those is missing, you have exposure on that host, and you should read the rest of this post.<\/p>\n<p>As of writing, the kernel patch is available on Debian (<a href=\"https:\/\/security-tracker.debian.org\/tracker\/CVE-2026-31431\" rel=\"nofollow noopener\" target=\"_blank\">CVE-2026-31431<\/a>) and RHEL 9 (<a href=\"https:\/\/access.redhat.com\/security\/vulnerabilities\/RHSB-2026-002\" rel=\"nofollow noopener\" target=\"_blank\">RHSB-2026-002<\/a>) but<a href=\"https:\/\/ubuntu.com\/security\/CVE-2026-31431\" rel=\"nofollow noopener\" target=\"_blank\"> not yet on Ubuntu<\/a>. For users on distros that haven\u2019t shipped a kernel fix, upgrading Docker Engine is the mitigation you can apply today.<\/p>\n<h2 class=\"wp-block-heading\"><strong>Why you should read about Copy-Fail<\/strong><\/h2>\n<p>This CVE drew a lot of attention because the exploit became public before many Linux distributions had kernel patches available. As a result, most distros were still vulnerable and had no ready fix at the time of disclosure. It was especially notable because the bug affected Linux kernels going back to around 2017, making the potential impact unusually broad.<\/p>\n<p>On the Docker Engine team, I started investigating what we could do from our end to protect users on vulnerable hosts. It turned out the mitigation was more involved than it first looked, and the first attempt broke 32-bit binaries. This post is what we shipped, what broke, what we learned, and where things stand now.<\/p>\n<h2 class=\"wp-block-heading\"><strong>What Copy Fail is<\/strong><\/h2>\n<p>On April 29, researchers disclosed CVE-2026-31431, dubbed \u201cCopy Fail,\u201d a privilege escalation vulnerability in the Linux kernel\u2019s AF_ALG crypto subsystem.<\/p>\n<p>The flaw is in the <code>algif_aead<\/code> module. It allows any unprivileged user with access to an AF_ALG socket to perform controlled writes to the page cache. Since the page cache backs file reads across the entire system, an attacker can temporarily modify the contents of any readable file as seen by every process on the host. Corrupting a setuid binary is the most direct path to local root, but the primitive itself is more general.<\/p>\n<p>The exploit is trivial and works on every unpatched Linux kernel shipped since 2017.<\/p>\n<p>The correct fix is a kernel update. The mitigations described below reduce exposure for containers running on unpatched kernels, but they do not fix the underlying vulnerability. If your kernel vendor has released a patch, apply it.<\/p>\n<h2 class=\"wp-block-heading\">What does this mean for containers?<\/h2>\n<p>Inside a container running with default security profiles, an attacker with code execution can use Copy Fail to corrupt pages in the page cache. One possible outcome is escalating to root inside the container by corrupting setuid binaries.<\/p>\n<p>But the page cache is shared across the host, so the impact is not confined to the attacker\u2019s container. Modified pages are visible to the host and to every other container that maps the same file, including shared image layers. Other workloads on the same node can be affected.<\/p>\n<p>The attack does not require any special capabilities or privileges beyond what a default container provides. The only requirement is the ability to create an <code>AF_ALG<\/code> socket, which was previously allowed by Docker\u2019s default security profiles.<\/p>\n<h2 class=\"wp-block-heading\"><strong>First attempt: seccomp (v29.4.2)<\/strong><\/h2>\n<p>We updated Docker Engine\u2019s default <a href=\"https:\/\/github.com\/moby\/profiles\/pull\/20\" rel=\"nofollow noopener\" target=\"_blank\">seccomp profile<\/a> to block <code>AF_ALG<\/code> sockets. The seccomp filter inspects the first argument to <code>socket(2)<\/code> and denies address families <code>AF_ALG<\/code> and <code>AF_VSOCK<\/code> (which was already blocked).<\/p>\n<p>Blocking <code>socket(2)<\/code> is not enough on its own. There is another way to create sockets on x86_64 Linux: <code>socketcall(2)<\/code>, an older multiplexed syscall that wraps <code>socket<\/code>, <code>bind<\/code>, <code>connect<\/code>, and other socket operations behind a single syscall number.<\/p>\n<p>There is another way to create sockets on Linux: <a href=\"https:\/\/man7.org\/linux\/man-pages\/man2\/socketcall.2.html\" rel=\"nofollow noopener\" target=\"_blank\"><code>socketcall(2)<\/code><\/a>, an older multiplexed syscall that wraps <code>socket<\/code>, <code>bind<\/code>, <code>connect<\/code>, and other socket operations behind a single syscall number.<\/p>\n<p>The problem for seccomp is that <code>socketcall<\/code> packs the real arguments (including the address family) into a userspace array and passes a pointer, which BPF cannot dereference and inspect. There is no way to selectively block <code>AF_ALG<\/code> through <code>socketcall<\/code> with seccomp.<\/p>\n<p>Linux 4.3 already added direct socket syscalls for i386 and s390, so we assumed most modern binaries would already use the new <code>socket<\/code> syscall and that <code>socketcall<\/code> would only matter for old binaries. So we blocked it entirely and shipped Docker Engine v29.4.2 (<a href=\"https:\/\/github.com\/moby\/moby\/releases\/tag\/docker-v29.4.2\" rel=\"nofollow noopener\" target=\"_blank\">release notes<\/a>).<\/p>\n<h2 class=\"wp-block-heading\">What broke<\/h2>\n<p>The <code>socketcall<\/code> deny turned out to be too broad.<\/p>\n<p>Older versions of glibc on i386 route all socket operations through <code>socketcall<\/code>, the Go runtime uses it unconditionally for <code>GOARCH=386<\/code> (independent of glibc), and many legacy and gaming workloads (SteamCMD, Wine) depend on it.<\/p>\n<p>Blocking <code>socketcall<\/code> broke networking for a lot of 32-bit binaries running inside a container (<a href=\"https:\/\/github.com\/moby\/moby\/issues\/52506\" rel=\"nofollow noopener\" target=\"_blank\">moby\/moby#52506<\/a>).<\/p>\n<p>And this is not just an i386 problem. On amd64, any process can switch into ia32 compatibility mode with <code>int $0x80<\/code> and invoke <code>socketcall<\/code> directly, bypassing the <code>socket(2)<\/code> arg filter entirely. You do not need a 32-bit container or a 32-bit binary to reach that path.<\/p>\n<p>Affected containers could work around this by using a <a href=\"https:\/\/github.com\/moby\/profiles\/releases\/tag\/seccomp%2Fv0.2.1\" rel=\"nofollow noopener\" target=\"_blank\">custom seccomp profile<\/a> that re-enables <code>socketcall<\/code> while keeping <code>AF_ALG<\/code> blocked for the direct <code>socket(2)<\/code> path.<\/p>\n<p>But that just pokes a hole in the hardening for those containers, since an attacker inside them could still reach <code>AF_ALG<\/code> through <code>socketcall<\/code>.<\/p>\n<h2 class=\"wp-block-heading\"><strong>Second attempt: LSM-based enforcement (v29.4.3)<\/strong><\/h2>\n<p>The fundamental problem is that seccomp operates at the syscall boundary, and <code>socketcall<\/code> multiplexes many operations behind a single syscall number with pointer arguments. You cannot selectively block <code>AF_ALG<\/code> through socketcall with seccomp alone.<\/p>\n<p>AppArmor and SELinux operate on a different level. Linux Security Modules hook directly into the kernel\u2019s <code>security_socket_create()<\/code> callback, which fires when the kernel actually creates the socket object, regardless of which syscall entry point was used. An LSM can deny <code>AF_ALG<\/code> specifically while leaving all other socketcall usage intact.<\/p>\n<p>In v29.4.3 (<a href=\"https:\/\/github.com\/moby\/moby\/releases\/tag\/docker-v29.4.3\" rel=\"nofollow noopener\" target=\"_blank\">release notes<\/a>), we:<\/p>\n<ol class=\"wp-block-list\">\n<li><strong>Reverted the <code>socketcall<\/code> seccomp deny<\/strong> to restore 32-bit compatibility.<\/li>\n<li><strong>Added <code>deny network alg,<\/code> to the default AppArmor profile<\/strong> (<a href=\"https:\/\/github.com\/moby\/profiles\/pull\/22\" rel=\"nofollow noopener\" target=\"_blank\">moby\/profiles#22<\/a>).<br \/>On systems with AppArmor enabled (e.g. Ubuntu, Debian), this blocks <code>AF_ALG<\/code> through both <code>socket(2)<\/code> and <code>socketcall(2)<\/code>.<\/li>\n<li><strong>Integrated a SELinux CIL policy module<\/strong> for systems running SELinux (Fedora, RHEL, CentOS).<br \/>The module denies <code>alg_socket<\/code> creation for all <code>container_domain<\/code> types and can be loaded via <code>semodule<\/code>.<br \/>SELinux enforcement requires the daemon to be running with <code>--selinux-enabled<\/code>.<\/li>\n<li><strong>Kept the seccomp <code>socket(AF_ALG)<\/code> arg filter<\/strong> as defense-in-depth for the direct <code>socket(2)<\/code> syscall path.<\/li>\n<\/ol>\n<h2 class=\"wp-block-heading\">What you should do<\/h2>\n<ol class=\"wp-block-list\">\n<li><strong>Patch your kernel.<\/strong><br \/>This is the real fix.<br \/>Check with your distribution for a kernel update that addresses CVE-2026-31431.<\/li>\n<li><strong>Upgrade Docker Engine to v29.4.3 or later.<\/strong> You get the updated seccomp + AppArmor + SELinux defaults. A systemctl restart docker (or equivalent) is enough; no host reboot required.<\/li>\n<li><strong>If you cannot upgrade the kernel or the engine immediately:<\/strong><\/li>\n<\/ol>\n<ul class=\"wp-block-list\">\n<li>Blacklist the kernel modules: add <code>blacklist af_alg<\/code> and <code>blacklist algif_aead<\/code> to <code>\/etc\/modprobe.d\/<\/code>.<br \/>This only works if the modules are built as loadable modules (<code>CONFIG_CRYPTO_USER_API=m<\/code>), not compiled into the kernel.<\/li>\n<li>Apply a <a href=\"https:\/\/github.com\/moby\/profiles\/releases\/tag\/seccomp%2Fv0.2.2\" rel=\"nofollow noopener\" target=\"_blank\">custom seccomp profile<\/a> that denies <code>AF_ALG<\/code> using <code>--security-opt seccomp=\/path\/to\/profile.json<\/code> or the <code>seccomp-profile<\/code> option in <code>daemon.json<\/code>.<\/li>\n<\/ul>\n<h2 class=\"wp-block-heading\">Closing thoughts<\/h2>\n<p>Security comes in layers, and sometimes no single layer is enough. Seccomp blocks <code>socket(AF_ALG)<\/code> on every system but is blind to <code>socketcall<\/code>. AppArmor and SELinux block both paths, but they depend on host configuration. Together, they cover what neither can alone.<\/p>\n<p>On systems without an LSM, the <code>socketcall<\/code> path remains unblocked from Docker\u2019s side. Ultimately, the kernel bug is what needs to be fixed.<\/p>\n<p>Kernel vulnerabilities will keep coming. When they do, the container runtime is often the fastest place to deploy a mitigation, because updating the engine is one change that protects every container on the host. The Copy Fail timeline made that especially clear: the embargo broke before distros had fixes ready, and for several days the engine was the only place users could mitigate anything without waiting for a kernel rebuild.<\/p>\n<p>Keeping Docker Engine up to date is not just about new features. It is one of the most effective ways to shrink the window between a kernel CVE going public and your workloads being protected against it.<\/p>","protected":false},"excerpt":{"rendered":"<p>CVE-2026-31431 is a Linux kernel vulnerability that was recently disclosed. This CVE does not compromise Docker infrastructure. That said, Docker [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":94,"comment_status":"","ping_status":"","sticky":false,"template":"","format":"standard","meta":{"site-sidebar-layout":"default","site-content-layout":"","ast-site-content-layout":"default","site-content-style":"default","site-sidebar-style":"default","ast-global-header-display":"","ast-banner-title-visibility":"","ast-main-header-display":"","ast-hfb-above-header-display":"","ast-hfb-below-header-display":"","ast-hfb-mobile-header-display":"","site-post-title":"","ast-breadcrumbs-content":"","ast-featured-img":"","footer-sml-layout":"","ast-disable-related-posts":"","theme-transparent-header-meta":"","adv-header-id-meta":"","stick-header-meta":"","header-above-stick-meta":"","header-main-stick-meta":"","header-below-stick-meta":"","astra-migrate-meta-layouts":"default","ast-page-background-enabled":"default","ast-page-background-meta":{"desktop":{"background-color":"var(--ast-global-color-4)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"tablet":{"background-color":"","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"mobile":{"background-color":"","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""}},"ast-content-background-meta":{"desktop":{"background-color":"var(--ast-global-color-5)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"tablet":{"background-color":"var(--ast-global-color-5)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"mobile":{"background-color":"var(--ast-global-color-5)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""}},"footnotes":""},"categories":[4],"tags":[],"class_list":["post-4156","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-docker"],"_links":{"self":[{"href":"https:\/\/rssfeedtelegrambot.bnaya.co.il\/index.php\/wp-json\/wp\/v2\/posts\/4156","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/rssfeedtelegrambot.bnaya.co.il\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/rssfeedtelegrambot.bnaya.co.il\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/rssfeedtelegrambot.bnaya.co.il\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/rssfeedtelegrambot.bnaya.co.il\/index.php\/wp-json\/wp\/v2\/comments?post=4156"}],"version-history":[{"count":0,"href":"https:\/\/rssfeedtelegrambot.bnaya.co.il\/index.php\/wp-json\/wp\/v2\/posts\/4156\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/rssfeedtelegrambot.bnaya.co.il\/index.php\/wp-json\/wp\/v2\/media\/94"}],"wp:attachment":[{"href":"https:\/\/rssfeedtelegrambot.bnaya.co.il\/index.php\/wp-json\/wp\/v2\/media?parent=4156"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/rssfeedtelegrambot.bnaya.co.il\/index.php\/wp-json\/wp\/v2\/categories?post=4156"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/rssfeedtelegrambot.bnaya.co.il\/index.php\/wp-json\/wp\/v2\/tags?post=4156"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}