Visit NES for PHP Home Page

NES for PHP 8 Release Notes: PHP 8.0

Comprehensive release notes and changelog for NES for PHP 8 Release Notes: PHP 8.0, including security patches, bug fixes, and feature updates across all supported versions.

57 Patched Vulnerabilities
VEX Statements

PHP 8.0

8.0.1 (NES) - October 30, 2026

Packages: https://registry.nes.herodevs.com/bin/nes/php/v8.0.30-nes-8.0.1/ (Ubuntu 22.04, amd64; see Installation)

Package versions: 1:8.0.30+nes8.0.1-1~ubuntu22.04 (php-common: 2:101.nes8.0.1~ubuntu22.04)

Based on PHP 8.0.30.

This release patches the following CVEs:

  • CVE-2024-2756 Incomplete CVE-2022-31629 fix: cookie names with an unmatched [, such as .[Host-test or _[Secure-test, were mangled into __Host-test / __Secure-test after the check that rejects them, so a network or same-site attacker could set an ordinary cookie that the application takes for a __Host- or __Secure- cookie. - (Medium)
  • CVE-2024-3096 A bcrypt password starting with a NUL byte was hashed as the empty string, so password_verify() accepted an empty password for that hash (the fix commit names the draft id GHSA-q6x7-frmf-grcw). - (Medium)
  • CVE-2024-5458 FILTER_VALIDATE_URL returned success as soon as the host was a bracketed IPv6 address and skipped the userinfo check, so URLs with invalid userinfo were accepted and could be parsed differently later. - (Medium)
  • CVE-2024-8925 A multipart boundary longer than the read buffer was not recognised when it was split across reads, so part of a body could be merged into another field or dropped, letting an attacker who controls part of the data hide other fields. - (Low)
  • CVE-2024-8927 cgi.force_redirect accepted HTTP_REDIRECT_STATUS, which a client sets with a Redirect-Status: request header, so a request straight to php-cgi passed the check; in some configurations this allows arbitrary file inclusion. - (High)
  • CVE-2024-11234 With an HTTP proxy in the stream context and request_fulluri, a request URI containing CR/LF from user input could add headers or whole requests sent through the proxy (request smuggling, requests to resources the proxy can reach). - (Medium)
  • CVE-2024-8932 On 32-bit systems the length of the escaped string could overflow for an input of about 1.4 GB, so ldap_escape() wrote past its buffer. - (Critical)
  • CVE-2024-11233 A chunk ending in =\r made the convert.quoted-printable-decode filter read one byte past its input buffer, which can crash or disclose a byte of memory. - (Medium)
  • GHSA-4w77-75f9-2c8w Heap use-after-free: in the CLI built-in web server, a request without Content-Type kept the previous request's content type pointer, freed with that request, and sapi_read_post_data() read it. - (Low)
  • CVE-2025-14177 getimagesize read JPEG APPn segments that span several reads into a buffer it had not filled completely, so uninitialised heap memory could be returned to the script in the APP data (information leak). - (Medium)
  • CVE-2025-14178 array_merge() summed the sizes of its arguments in 32 bits and sized the result array from the sum, so a very large merge overflowed the allocation and wrote past the end of the heap block. - (Medium)
  • GHSA-www2-q4fc-65wf gethostbyname(), gethostbynamel(), gethostbyaddr(), dns_get_record(), checkdnsrr(), getmxrr(), getservbyname(), getprotobyname(), inet_pton() and ip2long() took the part of their argument before a NUL byte, so a name with a NUL byte resolved as a shorter one.
  • CVE-2026-7259 mb_ereg_search_init with an invalid encoding left the search state so that php_mb_check_encoding dereferenced a NULL pointer: a crash (denial of service). - (Low)
  • CVE-2026-7568 metaphone() indexed its input with a signed int offset while the string length is a size_t, so a very long string overflowed the signed integer and read out of bounds. - (Medium)
  • CVE-2026-92842 The convert.base64-encode and convert.quoted-printable-* filters copied a line-break-chars option with pestrdup(), which stops at a NUL, but kept its full length, so the filter read past its copy and put heap bytes into its output (information disclosure). - (Medium)
  • GHSA-ch8v-r6jh-4vvr php_filter_encode_url() initialised 255 of the 256 entries of its "must encode" table, so whether byte 0xFF was percent-encoded by FILTER_SANITIZE_ENCODED depended on stack contents. - (Low)
  • CVE-2026-14355 openssl_encrypt() sized its output buffer for a block cipher, too small for the AES key wrap with padding modes (AES-*-WRAP-PAD), so a call wrote past the end of the heap block. - (Medium)
  • CVE-2026-91769 Peer verification fell back to the certificate's Common Name whenever no subjectAltName matched, so a certificate whose SAN names another host but whose CN holds the expected name was accepted (RFC 6125 forbids the fallback once a DNS, SRV or URI identity is present). - (Medium)
  • CVE-2026-91767 php_openssl_matches_wildcard_name() checked only the suffix's length, so a certificate wildcard name whose literal parts together were longer than the peer name made the length passed to memchr() wrap and read far past the name (heap out-of-bounds read). - (Medium)
  • CVE-2025-1217 The HTTP stream wrapper (fopen(), file_get_contents() on http://) mis-parsed a response header folded onto a continuation line, so a malicious server could make the client take part of a header as another. - (Medium)
  • CVE-2025-1734 The HTTP stream wrapper accepted response header lines without a colon (and lines with other malformed shapes) as headers, so a malicious server could smuggle a header past the wrapper's checks. - (Medium)
  • CVE-2025-1861 The HTTP stream wrapper kept a redirect's Location header in a buffer of 1024 bytes and truncated a longer one, so a malicious server could make the client follow a different URL than the one it sent. - (Medium)
  • CVE-2025-1736 check_has_header() decided whether the header stream context option already holds a standard header (Authorization, Host, User-Agent...) and accepted a match that followed a bare LF, although requests separate headers with CRLF. - (Medium)
  • CVE-2026-91766 The HTTP stream wrapper kept the Authorization header (and credentials in the URL, a cookie) when it followed a redirect to another origin, handing them to a server the script never meant to trust. - (Medium)
  • CVE-2026-93682 The HTTP stream wrapper read before the start of a redirect's Location header value when that value was empty, an over-read of the response buffer. - (Medium)
  • CVE-2025-1220 fsockopen(), stream_socket_client() and the stream transports took the part of a host name before a NUL byte as the host and the rest as ignored, so a name such as localhost\0.example.com connected to localhost after a check against the longer name. - (Low)
  • CVE-2025-6491 SOAP gave libxml2 an over-long XML name for a SoapVar or SoapParam node (a very large namespace prefix or element name); libxml2 then did not set the node's name and the encoder compared a NULL name with strcmp() in serialize_zval() (a SoapClient serialising the SoapVar), a NULL pointer dereference; the server-side serialize_response_call2() has the same comparison. - (Medium)
  • CVE-2026-6722 The SOAP apache:Map decoder stored values in its reference map (SOAP_GLOBAL(ref_map)) without holding a reference to them, so a later lookup in the same message used a value freed in between (use-after-free). - (Critical)
  • CVE-2026-7261 SoapServer::handle() freed the session-persisted server object when a SOAP header raised a fault or an exception with SOAP_PERSISTENCE_SESSION on, while the session still held it, and a later request used the freed object (use-after-free). - (Medium)
  • CVE-2026-7262 The SOAP apache:Map decoder checked the wrong node for NULL, so a map item with a missing <value> element dereferenced a NULL pointer and crashed the process. - (Low)
  • CVE-2026-91765 The SOAP XML parsing and decoding code (cleanup_xml_node(), master_to_zval_int()) recursed with the nesting depth of the document, or of its href references, so a deeply nested message overflowed the stack and crashed the process. - (High)
  • CVE-2025-14181 get_http_body() of SoapClient kept the body size of a chunked HTTP response in an int and checked the sum of two chunk sizes for overflow with signed arithmetic, which the compiler may remove: a malicious SOAP server could make the client reallocate a buffer too small for the data it then read into it (heap buffer overflow). - (Medium)
  • CVE-2024-9026 php-fpm's handling of log lines from its children (catch_workers_output) could be made to drop or alter up to four characters at the start or end of a log message, so a worker's output could spoof or hide log content. - (Low)
  • CVE-2026-6735 The full PHP-FPM status page (?full&html, ?full&xml) printed each worker's request URI unescaped in its HTML and XML output, and escaped the query string without quote flags, so a crafted request gave cross-site scripting against whoever opens the status page. - (High)
  • CVE-2026-91768 The FastCGI client check compared IPv6 addresses on 12 of 16 bytes, so an allowed address matched a whole /96 prefix and other hosts in it could talk FastCGI to the pool (run scripts with arbitrary parameters). - (Medium)
  • CVE-2026-7260 phar_get_link_source() followed the symbolic links of a tar archive recursively without a bound, so a phar with circular symlinks overflowed the stack and crashed the process. - (Medium)
  • CVE-2026-6103 phar_parse_tarfile() read a tar entry's octal size into 32 bits without checking it, and skipped the data blocks of regular files only, so an oversized size or the data of another entry type was then parsed as further tar headers: a crafted archive could make PHP see entries that other tools do not (archive entry injection, e.g. past a scanner). - (Medium)
  • CVE-2025-1219 When libxml-based extensions loaded a document over HTTP and the server redirected, PHP passed the content-type of the first response, not the final one, to the charset detection, so a document could be parsed with the wrong character set. - (Medium)
  • GHSA-wg4p-4hqh-c3g9 With XML_OPTION_SKIP_TAGSTART set, xml_parse_into_struct() passed tag_name + skip to the info table before checking that the tag name was longer than the skip, an out-of-bounds read of the tag name.
  • CVE-2024-8929 mysqlnd trusted lengths in packets from the MySQL server: a malicious server could make the client read past a packet (the OK packet, the result set header, field metadata, LOCAL INFILE and authentication plugin messages, binary and text rows) and return the heap bytes it read to the script. - (Medium)
  • CVE-2025-1218 mysqlnd read several fields of server packets (OK, EOF, error, result set field, row, prepare response) without checking that the packet holds them, so a hostile or compromised MySQL server, or a man in the middle on an unencrypted connection, could make PHP read past the packet buffer and leak heap memory or crash. - (Low)
  • CVE-2024-11236 On 32-bit systems the quoted length of a very long string overflowed in the dblib and Firebird quoters, so PDO::quote() wrote past its buffer. - (Critical)
  • CVE-2025-1735 pg_escape_string(), pg_insert(), pg_update(), pg_convert() and the PDO_PgSQL quoter ignored the error PQescapeStringConn reports (for example for bytes invalid in the connection's encoding) and used the half-escaped string in a query, which can leave a value or name improperly escaped. - (Medium)
  • CVE-2025-14179 PDO:quote on a pdo_firebird connection stopped doubling quotes at the first NUL byte, so the rest of the string was copied into the SQL text unescaped: SQL injection. - (High)
  • CVE-2026-17543 pg_convert(), and through it pg_insert(), pg_update(), pg_select() and pg_delete(), quoted escaped strings as E'...'; with standard_conforming_strings on (PostgreSQL's default) PQescapeStringConn() leaves backslashes alone, so a value such as x\' OR 1=1 -- closed the literal (SQL injection through values a script believes are escaped). - (High)
  • CVE-2026-7258 Many functions (urldecode() among them) passed a signed char to the ctype.h functions; for bytes >= 0x80 the argument was negative, and C libraries whose tables do not cover negative values read out of bounds. - (Medium)
  • CVE-2026-9672 The GIF LZW decoder of the bundled libgd (LWZReadByte_(), ReadImage()) read uninitialised stack memory and went out of bounds on a crafted GIF with a repeated clear code.
  • CVE-2021-38115 The bundled libgd's TGA reader ignored a short read of the image identification field, leaving the buffer uninitialised (CVE-2021-38115), and its BMP and WebP writers ignored gdPutBuf results, the BMP writer's error path freeing the caller's context (CVE-2021-40812).
  • CVE-2022-1586 The bundled PCRE2 10.35 of PHP 8.0.30 misses later JIT fixes: CVE-2022-1586 (out-of-bounds read in compile_xclass_matchingpath for Unicode property classes, fixed in 10.40), CVE-2022-1587 (out-of-bounds read in get_recurse_data_length for recursions, 10.40) and CVE-2026-89156 (pcre2_match runs the JIT without validating the whole UTF subject, 10.48).
  • CVE-2026-103111 A PCRE2 JIT stack allocation larger than the growth step wrote below the stack (CVE-2026-103111, advisory GHSA-r9hj-j2rw-4q3m, fixed in PCRE2 10.49). ext/pcre creates a growable JIT stack, so the code is reachable. - (High)
  • CVE-2024-2408 The Marvin attack: RSA decryption with PKCS#1 v1.5 padding leaks through timing whether the padding was valid, so an attacker who can submit many ciphertexts and time the answers can decrypt a ciphertext or forge a signature. - (Medium)

Upgrade notes

  • This is the first release of NES for PHP 8.0. It is for Ubuntu 22.04 on amd64 (x86_64) only. There are no RPMs, no container images and no Windows zip for PHP 8.0.
  • The release is one signed apt repository in one archive, php-v8.0.30-nes-8.0.1.ubuntu2204.amd64.repo.tar.xz. It holds the release signing key and a repo/ directory with the 39 packages and their signed index. Download it with the key and the checksums, then verify them:
    base=https://registry.nes.herodevs.com/bin/nes/php/v8.0.30-nes-8.0.1
    auth="<your-contact-email>:<your-nes-access-token>"
    for f in RPM-GPG-KEY-php-nes SHASUMS256.txt SHASUMS256.txt.asc php-v8.0.30-nes-8.0.1.ubuntu2204.amd64.repo.tar.xz; do
      curl -fSLO -u "$auth" "$base/$f"
    done
    gpg --show-keys RPM-GPG-KEY-php-nes    # must show CA44E935A3995EB733AF292B18DD1E9212F3C6A2
    gpg --import RPM-GPG-KEY-php-nes
    gpg --verify SHASUMS256.txt.asc SHASUMS256.txt
    sha256sum -c --ignore-missing SHASUMS256.txt
    
  • To install on a host without PHP 8.0, extract the archive to /opt/nes-php and add it as an apt source. apt reads an armored key only when its file name ends in .asc, so the key goes to /usr/share/keyrings/nes-php.asc. Then install php-common and the packages you need:
    sudo mkdir -p /opt/nes-php
    sudo tar -xJf php-v8.0.30-nes-8.0.1.ubuntu2204.amd64.repo.tar.xz -C /opt/nes-php --strip-components=1
    sudo cp /opt/nes-php/RPM-GPG-KEY-php-nes /usr/share/keyrings/nes-php.asc
    echo 'deb [arch=amd64 signed-by=/usr/share/keyrings/nes-php.asc] file:/opt/nes-php/repo ./' | sudo tee /etc/apt/sources.list.d/nes-php.list
    sudo apt-get update
    sudo apt-get install --no-remove --no-install-recommends php-common php8.0-cli php8.0-fpm
    php8.0 -v
    
  • The 39 packages are php-common, php8.0, php8.0-common, php8.0-cli, php8.0-fpm, php8.0-cgi, php8.0-phpdbg, libapache2-mod-php8.0, libphp8.0-embed, php8.0-dev and 27 extension packages: php8.0-bcmath, php8.0-bz2, php8.0-curl, php8.0-dba, php8.0-enchant, php8.0-gd, php8.0-gmp, php8.0-imap, php8.0-interbase, php8.0-intl, php8.0-ldap, php8.0-litespeed, php8.0-mbstring, php8.0-mysql, php8.0-odbc, php8.0-opcache, php8.0-pgsql, php8.0-pspell, php8.0-readline, php8.0-snmp, php8.0-soap, php8.0-sqlite3, php8.0-sybase, php8.0-tidy, php8.0-xml, php8.0-xsl and php8.0-zip, and the GD library packages libgd3 and libgd-dev. They keep the usual Ubuntu paths: /usr/bin/php8.0, /etc/php/8.0/<sapi>/php.ini and /usr/lib/php/20200930/ for extensions. Turn an extension on or off with phpenmod -v 8.0 and phpdismod -v 8.0.
  • libgd3 and libgd-dev are version 2.3.0-2ubuntu2.3+nes1: Ubuntu's libgd 2.3.0 with a GIF decoder fix. php8.0-gd needs exactly this version, so apt installs it with php8.0-gd in place of Ubuntu's libgd3. The library ABI and soname (libgd.so.3) are Ubuntu's, so other programs on the host that use libgd keep working. A PHP rollback keeps this libgd3.
  • Apache runs PHP through libapache2-mod-php8.0 with the prefork MPM:
    sudo a2dismod mpm_event
    sudo a2enmod mpm_prefork php8.0
    sudo apache2ctl -t
    sudo service apache2 restart
    
  • php-fpm is the php8.0-fpm service (sudo service php8.0-fpm start). It listens on /run/php/php8.0-fpm.sock, owned by www-data.
  • Other PHP versions on the host, such as Ubuntu's own PHP 8.1, keep working, and the php command keeps pointing where it did. Run php8.0, or point php at it with sudo update-alternatives --set php /usr/bin/php8.0.
  • Ubuntu 22.04 has no PHP 8.0 of its own. A host that has PHP 8.0.30 packages from another repository can upgrade to these. The NES versions sort above the other builds of 8.0.30: every package is 1:8.0.30+nes8.0.1-1~ubuntu22.04, and php-common is 2:101.nes8.0.1~ubuntu22.04. The tested upgrade replaces the 36 versioned PHP 8.0 packages in one apt command and keeps the host's php-common. First keep the packages you have, so you can roll back. Their versions must still be in their repository:
    mkdir -p ~/php-8.0-rollback && cd ~/php-8.0-rollback
    apt-get download $(dpkg-query -W -f '${db:Status-Abbrev} ${Package}=${Version}\n' | awk '$1 ~ /^i/ && $2 ~ /^(php8\.0|libapache2-mod-php8\.0|libphp8\.0-embed)/ { print $2 }')
    

    Then add the NES repository as above, without the install line, and upgrade:
    sudo apt-get install --no-remove --no-install-recommends $(dpkg-query -W -f '${db:Status-Abbrev} ${Package}\n' | awk '$1 ~ /^i/ && $2 ~ /^(php8\.0|libapache2-mod-php8\.0|libphp8\.0-embed)/ { print $2 }')
    sudo service php8.0-fpm restart
    sudo service apache2 restart
    

    With --no-remove, apt stops instead of removing a package. Extension packages from the other repository that NES does not ship are not part of the tested upgrade: remove them first, and build the extension against php8.0-dev instead (see below).
  • To roll back, install the packages you kept. Only this step allows apt to downgrade. Then take the NES source out, so that apt does not upgrade again:
    cd ~/php-8.0-rollback
    sudo apt-get install --allow-downgrades --no-remove --no-install-recommends ./*.deb
    sudo rm /etc/apt/sources.list.d/nes-php.list
    sudo apt-get update
    sudo service php8.0-fpm restart
    sudo service apache2 restart
    

    The upgrade and the rollback keep your edits to /etc/php/8.0/cli/php.ini, and the extensions you turned off stay off.
  • php8.0 -v and PHP_VERSION report 8.0.30-nes, so version_compare(PHP_VERSION, '8.0.30', '>=') is false. PHP_VERSION_ID stays 80030.
  • The extension API is PHP 8.0.30's: PHP API 20200930 and Zend extension API 420200930. An extension built for PHP 8.0 elsewhere loads as before. To build one that NES does not ship, install php8.0-dev and use phpize8.0 and php-config8.0. With pecl (Ubuntu's php-pear), first run pecl config-set php_bin /usr/bin/php8.0 and pecl config-set php_suffix 8.0. APCu builds and runs this way.
  • Report a suspected vulnerability in NES for PHP privately to HeroDevs at support@herodevs.com. Include the output of php8.0 -v, the package versions (dpkg -l 'php8.0*') and the steps that reproduce it.