Skip to content

Stdlib: htmlspecialchars/htmlentities ENT_DISALLOWED leaves C0 controls — Zend replaces with U+FFFD (ext/standard/html.c) #32084

Description

@PurHur

Category

Stdlib · php-src-strict / htmlspecialchars ENT_DISALLOWED

Problem

htmlspecialchars() / htmlentities() with ENT_DISALLOWED must replace C0 controls that are illegal in the chosen document type (HTML5 / HTML 4.01 / XML1) with U+FFFD (\xEF\xBF\xBD). Zend replaces "\x01" and "\x7F". VM/JIT leave the raw control bytes.

Without ENT_DISALLOWED, both sides keep "\x01" (correct). Distinct from open #32063 (ENT_IGNORE on invalid UTF-8 — different flag, different bytes).

VmString::htmlspecialchars() copies non-<>&"' bytes in the default arm and never consults ENT_DISALLOWED.

Probed 2026-08-18 @ ad82044529 — Zend 8.2.32 vs php bin/vm.php / php bin/jit.php.

Repro Zend 8.2.32 VM/JIT
htmlspecialchars("\\x01", ENT_DISALLOWED|ENT_HTML5, 'UTF-8') "\\u{FFFD}" (efbfbd) "\\x01"
htmlentities("\\x01", ENT_DISALLOWED|ENT_HTML5, 'UTF-8') "\\u{FFFD}" "\\x01"
htmlspecialchars("a\\x01b\\x7Fc", ENT_DISALLOWED|ENT_HTML5, 'UTF-8') a + U+FFFD + b + U+FFFD + c raw 01 / 7f kept
htmlspecialchars("\\x01", ENT_HTML5, 'UTF-8') (no DISALLOWED) "\\x01" "\\x01"

php-src reference

PHP implementation target

  • ext/standard/VmString.phphtmlspecialchars() / htmlentities(): when ENT_DISALLOWED is set, replace disallowed code points with "\u{FFFD}" (do not copy C0/DEL through)
  • ext/standard/HtmlspecialcharsJitHelper.php — same flags; reuse the PHP implementation
  • ext/standard/StdlibConstants.php already defines ENT_DISALLOWED = 128
  • No new runtime/*.c

Repro

./script/docker-exec.sh -- bash -lc 'php bin/vm.php test/repro/maintainer_gap_htmlspecialchars_ent_disallowed.php'
./script/docker-exec.sh -- bash -lc 'php bin/jit.php test/repro/maintainer_gap_htmlspecialchars_ent_disallowed.php'
./script/docker-exec.sh -- bash -lc 'php test/repro/maintainer_gap_htmlspecialchars_ent_disallowed.php'  # Zend baseline
<?php
error_reporting(E_ALL);
$ctrl = "\x01";
var_dump(bin2hex(htmlspecialchars($ctrl, ENT_DISALLOWED | ENT_HTML5, 'UTF-8')));
var_dump(bin2hex(htmlentities($ctrl, ENT_DISALLOWED | ENT_HTML5, 'UTF-8')));

Done when

  • VM/JIT: ENT_DISALLOWED|ENT_HTML5 on "\x01" yields U+FFFD (efbfbd); "a\x01b\x7Fc" replaces both controls
  • htmlentities(..., ENT_DISALLOWED) matches htmlspecialchars
  • Without ENT_DISALLOWED, "\x01" is unchanged (Zend)
  • Compliance .phpt under test/compliance/cases/stdlib/
  • php-src-strict; no php-compiler-strict shortcut

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    area:vmVirtual machinebugSomething isn't workingimplementation-readySpec complete: repro, php-src ref, done-when — safe for workers to claimphase-4:stdlibPhase 4 – stdlib for web appsstdlib

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions