VULNARCHIVE

GCVE-1988-2026-0321

PHP 8.5.7 `FILTER_SANITIZE_ENCODED` uninitialized read

Description (en)

# PHP 8.5.7 `FILTER_SANITIZE_ENCODED` uninitialized read

**Author:** Khashayar Fereidani
**Disclosure Date:** 2026-06-18
**Advisory:** https://fereidani.com/php-857-filtersanitizeencoded-uninitialized-read
**Contact:** https://fereidani.com/contact

## Description

In `ext/filter/sanitizing_filters.c`, the `php_filter_encode_url`
function leaves the `255`th byte (`0xFF`) of a transient array
uninitialized. An array of 256 bytes is populated using `memset(tmp,
1, sizeof(tmp) - 1)`, resulting in `tmp[255]` remaining uninitialized.
When `FILTER_SANITIZE_ENCODED` is applied, this array acts as a lookup
table to determine whether an input byte should be percent-encoded.
Consequently, whether the byte `0xFF` is encoded or left as-is depends
on whatever value happened to be on the stack.

## Proof of concept

```php
<?php
/*
 * FILTER_SANITIZE_ENCODED uninitialized read
(ext/filter/sanitizing_filters.c:73).
 *
 * php_filter_encode_url() does:
 *     unsigned char tmp[256];
 *     memset(tmp, 1, sizeof(tmp) - 1);   // sets tmp[0..254] = 1,
leaves tmp[255] UNINIT
 *     ...
 *     if (tmp[*s]) { percent-encode } else { keep }
 *
 * So byte 0xFF (index 255) is read UNINITIALIZED: whether it is percent-encoded
 * depends on whatever was on the stack. Every other byte is encoded
 * deterministically. Effect: inconsistent URL-encoding of 0xFF (low severity;
 * no crash / no memory corruption, just UB + nondeterministic sanitizing).
 *
 * Run:  php poc.php
 *   expect: 0xFF kept RAW (ff...) while 0xFE is correctly percent-encoded (%FE)
 */
$out = filter_var("\xff\xfeabc", FILTER_SANITIZE_ENCODED);
echo "in : ", bin2hex("\xff\xfeabc"), "\n";
echo "out: ", bin2hex($out), "\n";
echo "0xFF was kept raw and 0xFE was percent-encoded => tmp[255] read
uninitialized.\n";
```

Running the script results in:

```bash
in : fffe616263
out: ff254645616263
0xFF was kept raw and 0xFE was percent-encoded => tmp[255] read uninitialized.
```

## Impact

The impact is low. No crashes or memory corruption can occur as a
result of this bug. The sole impact is nondeterministic sanitizing of
the `0xFF` byte, which leads to inconsistent URL-encoding based on
uninitialized stack data unless it smartly gets used among other
vulnerabilities in a chain.

## Solution

Replace `sizeof(tmp) - 1` with `sizeof(tmp)` in the `memset` call in
`ext/filter/sanitizing_filters.c` to fully initialize the lookup
table.
_______________________________________________
Sent through the Full Disclosure mailing list
https://nmap.org/mailman/listinfo/fulldisclosure
Web Archives & RSS: https://seclists.org/fulldisclosure/

References

Raw JSON
{
  "containers": {
    "cna": {
      "affected": [
        {
          "product": "PHP",
          "vendor": "Php",
          "versions": [
            {
              "status": "affected",
              "version": "unknown"
            }
          ]
        }
      ],
      "credits": [
        {
          "lang": "en",
          "type": "finder",
          "value": "Khashayar Fereidani"
        }
      ],
      "descriptions": [
        {
          "lang": "en",
          "value": "# PHP 8.5.7 `FILTER_SANITIZE_ENCODED` uninitialized read\n\n**Author:** Khashayar Fereidani\n**Disclosure Date:** 2026-06-18\n**Advisory:** https://fereidani.com/php-857-filtersanitizeencoded-uninitialized-read\n**Contact:** https://fereidani.com/contact\n\n## Description\n\nIn `ext/filter/sanitizing_filters.c`, the `php_filter_encode_url`\nfunction leaves the `255`th byte (`0xFF`) of a transient array\nuninitialized. An array of 256 bytes is populated using `memset(tmp,\n1, sizeof(tmp) - 1)`, resulting in `tmp[255]` remaining uninitialized.\nWhen `FILTER_SANITIZE_ENCODED` is applied, this array acts as a lookup\ntable to determine whether an input byte should be percent-encoded.\nConsequently, whether the byte `0xFF` is encoded or left as-is depends\non whatever value happened to be on the stack.\n\n## Proof of concept\n\n```php\n<?php\n/*\n * FILTER_SANITIZE_ENCODED uninitialized read\n(ext/filter/sanitizing_filters.c:73).\n *\n * php_filter_encode_url() does:\n *     unsigned char tmp[256];\n *     memset(tmp, 1, sizeof(tmp) - 1);   // sets tmp[0..254] = 1,\nleaves tmp[255] UNINIT\n *     ...\n *     if (tmp[*s]) { percent-encode } else { keep }\n *\n * So byte 0xFF (index 255) is read UNINITIALIZED: whether it is percent-encoded\n * depends on whatever was on the stack. Every other byte is encoded\n * deterministically. Effect: inconsistent URL-encoding of 0xFF (low severity;\n * no crash / no memory corruption, just UB + nondeterministic sanitizing).\n *\n * Run:  php poc.php\n *   expect: 0xFF kept RAW (ff...) while 0xFE is correctly percent-encoded (%FE)\n */\n$out = filter_var(\"\\xff\\xfeabc\", FILTER_SANITIZE_ENCODED);\necho \"in : \", bin2hex(\"\\xff\\xfeabc\"), \"\\n\";\necho \"out: \", bin2hex($out), \"\\n\";\necho \"0xFF was kept raw and 0xFE was percent-encoded => tmp[255] read\nuninitialized.\\n\";\n```\n\nRunning the script results in:\n\n```bash\nin : fffe616263\nout: ff254645616263\n0xFF was kept raw and 0xFE was percent-encoded => tmp[255] read uninitialized.\n```\n\n## Impact\n\nThe impact is low. No crashes or memory corruption can occur as a\nresult of this bug. The sole impact is nondeterministic sanitizing of\nthe `0xFF` byte, which leads to inconsistent URL-encoding based on\nuninitialized stack data unless it smartly gets used among other\nvulnerabilities in a chain.\n\n## Solution\n\nReplace `sizeof(tmp) - 1` with `sizeof(tmp)` in the `memset` call in\n`ext/filter/sanitizing_filters.c` to fully initialize the lookup\ntable.\n_______________________________________________\nSent through the Full Disclosure mailing list\nhttps://nmap.org/mailman/listinfo/fulldisclosure\nWeb Archives & RSS: https://seclists.org/fulldisclosure/"
        }
      ],
      "providerMetadata": {
        "dateUpdated": "2026-09-11T07:55:54Z",
        "orgId": "4e2abfbf-4a2a-4b76-a4e0-d77c18ba156c",
        "shortName": "VULNARCHIVE"
      },
      "references": [
        {
          "tags": [
            "technical-description",
            "exploit"
          ],
          "url": "https://vuln.freearchive.org/archive/full-disclosure/2026/Jun/11"
        },
        {
          "tags": [
            "technical-description"
          ],
          "url": "https://seclists.org/fulldisclosure/2026/Jun/11"
        },
        {
          "url": "https://fereidani.com/contact"
        },
        {
          "url": "https://fereidani.com/php-857-filtersanitizeencoded-uninitialized-read"
        },
        {
          "url": "https://nmap.org/mailman/listinfo/fulldisclosure"
        },
        {
          "url": "https://seclists.org/fulldisclosure/"
        }
      ],
      "source": {
        "defect": [
          "https://seclists.org/fulldisclosure/2026/Jun/11"
        ],
        "discovery": "EXTERNAL"
      },
      "title": "PHP 8.5.7 `FILTER_SANITIZE_ENCODED` uninitialized read",
      "x_gcve": [
        {
          "recordType": "advisory",
          "relationships": [],
          "vulnId": "GCVE-1988-2026-0321",
          "x_vulnarchive": {
            "archiveUrl": "https://vuln.freearchive.org/archive/full-disclosure/2026/Jun/11",
            "automated": true,
            "contentSha256": "cd252d9e44c3c678217a7e75865750ba301ec028f67e430852bfb74f7f5c179d",
            "evidenceScore": 9,
            "messageId": "",
            "originalUrl": "https://seclists.org/fulldisclosure/2026/Jun/11",
            "policy": "vulnarchive-1",
            "sourceFormat": "text/html",
            "sourcePublishedAt": "2026-06-19T06:22:32Z"
          }
        }
      ]
    }
  },
  "cveMetadata": {
    "assignerOrgId": "4e2abfbf-4a2a-4b76-a4e0-d77c18ba156c",
    "assignerShortName": "VULNARCHIVE",
    "datePublished": "2026-09-11T07:55:54Z",
    "dateUpdated": "2026-09-11T07:55:54Z",
    "state": "PUBLISHED",
    "vulnId": "GCVE-1988-2026-0321"
  },
  "dataType": "CVE_RECORD",
  "dataVersion": "5.2"
}