NixOS: Quick fix for PHP CVE-2026-109618 and CVE-2026-109619

21 Oct 2026

php2shell

PHP disclosed two CVSS 9.2 unauthenticated RCE vulnerabilities on 21 October 2026, with exploitation expected within minutes. Patched releases like PHP 8.3.36 or PHP 8.4.27 were available immediately, but nixpkgs takes days to catch up: PR, review, Hydra rebuild of PHP and its reverse dependencies, channel advance...

Use an overlay to get an immidiate fix

An overlay swaps only the PHP source.

{ lib, ... }:
let
  patched = {
    php83 = { version = "8.3.36"; hash = "sha256-RvS0XjaI7SsYGQFae1MsFbUcwFW907E/0A3f7r7tzV8="; };
    php84 = { version = "8.4.27"; hash = "sha256-slMKh3xyL0sIyeF0owywi6wrfhkD7m6eMV8KqfX4LCQ="; };
  };

  mkPhpOverride = name: attrs: _final: prev: {
    ${name} = prev.${name}.overrideAttrs (_: {
      inherit (attrs) version;
      src = mkPatchedSrc prev attrs;
    });
  };
in
{
  nixpkgs.overlays = lib.mkAfter (map (name: mkPhpOverride name patched.${name}) (builtins.attrNames patched));
}

overrideAttrs on the buildEnv wrapper propagates to the unwrapped derivation and rebuilds every enabled extension, lib.mkAfter makes this override win over others.

The sandbox problem

The build fails on three new tests in ext/filter:

FAIL filter_input() doesn't reach hard limit [tests/oom_filter_input.phpt]
FAIL filter_input_array() doesn't reach hard limit [tests/oom_filter_input_array.phpt]
FAIL filter.default=encoded doesn't reach hard limit while parsing POST data [tests/oom_filter_default_encoded.phpt]

They spawn php-cgi with memory_limit=12M and assert on the fatal-error output, which doesn't reproduce in the Nix build sandbox.

doCheck = false on the extension does not work, so we patch the source instead:

mkPatchedSrc = prev: { version, hash }:
  let
    src = prev.fetchurl {
      url = "https://www.php.net/distributions/php-${version}.tar.bz2";
      inherit hash;
    };
  in
  prev.runCommand "php-${version}-src" { } ''
    mkdir -p "$out"
    tar -xf ${src} -C "$out" --strip-components=1
    rm -f "$out"/ext/filter/tests/oom_filter_input.phpt \
          "$out"/ext/filter/tests/oom_filter_input_array.phpt \
          "$out"/ext/filter/tests/oom_filter_default_encoded.phpt
  '';

This makes the build succeed!

Whole overlay module file

{ lib, ... }:
let
  # 21 Oct 2026 security releases for CVE-2026-109618 and CVE-2026-109619.
  patched = {
    php83 = {
      version = "8.3.36";
      hash = "sha256-RvS0XjaI7SsYGQFae1MsFbUcwFW907E/0A3f7r7tzV8=";
    };
    php84 = {
      version = "8.4.27";
      hash = "sha256-slMKh3xyL0sIyeF0owywi6wrfhkD7m6eMV8KqfX4LCQ=";
    };
  };

  # Drop OOM regression tests which do not reproduce inside the Nix build sandbox.
  mkPatchedSrc = prev: { version, hash }:
    let
      src = prev.fetchurl {
        url = "https://www.php.net/distributions/php-${version}.tar.bz2";
        inherit hash;
      };
    in
    prev.runCommand "php-${version}-src" { } ''
      mkdir -p "$out"
      tar -xf ${src} -C "$out" --strip-components=1
      rm -f "$out"/ext/filter/tests/oom_filter_input.phpt \
            "$out"/ext/filter/tests/oom_filter_input_array.phpt \
            "$out"/ext/filter/tests/oom_filter_default_encoded.phpt
    '';

  mkPhpOverride = name: attrs: _final: prev: {
    ${name} = prev.${name}.overrideAttrs (_: {
      inherit (attrs) version;
      src = mkPatchedSrc prev attrs;
    });
  };
in
{
  # mkAfter keeps this overlay last so it wins over any other PHP override.
  nixpkgs.overlays = lib.mkAfter (map (name: mkPhpOverride name patched.${name}) (builtins.attrNames patched));
}

Import it in configuration.nix with e.g.: imports = [ ./php-overlay.nix ];

Takeaway

An overlay is the right tool when a fix is critical and just a few minutes old, but the package won't be available for days. Grabbing fresh source code from php.net and making it work with a few lines of code: With NixOS, we don't have to take down our sites until someone figures out how to patch our stuff (which, coincidentally, was the sentiment on the web).

Add new comment

The content of this field is kept private and will not be shown publicly.

Restricted HTML

  • Allowed HTML tags: <a href hreflang target> <em> <strong> <cite> <blockquote cite> <pre> <ul type> <ol start type> <li> <dl> <dt> <dd> <h4 id> <h5 id> <h6 id>
  • Lines and paragraphs break automatically.
  • Web page addresses and email addresses turn into links automatically.

Get a quote in 24 hours

Wether a huge commerce system, or a small business website, we will quote the project within 24h of you pressing the following button: Get quote