Skip to content

Stdlib: hash_init() / hash_update() / hash_final() / hash_copy() — incremental ext/hash API #3357

Closed

Description

@PurHur

Category

stdlib · php-src-strict · spec refresh 2026-06-18

Problem

Incremental ext/hash API is partially landed. hash_init() / hash_update() / hash_final() register and produce correct digests on VM (verified 2026-06-18). Remaining Zend gaps:

  1. hash_copy() — must duplicate non-finalized HashContext; semantics after partial hash_update / before hash_final
  2. hash_pbkdf2() / hash_hkdf() — still missing or stubbed (tracked also by enum-operand issues Stdlib: hash_pbkdf2() — enum case password operand must TypeError (ext/hash/hash.c, php-src-strict) #9027)
  3. JIT/AOT — incremental context resource/object lifecycle on compiled path

Streaming file hashing and PBKDF2 key derivation cannot be expressed end-to-end until copy + KDF land.

php-src reference

Repro — incremental digest (GREEN on VM)

<?php
$ctx = hash_init('sha256');
hash_update($ctx, 'hello ');
hash_update($ctx, 'world');
echo hash_final($ctx), "\n";
./script/docker-exec.sh -- bash -lc 'source script/php-env.sh
php repro_hash_incremental.php
php bin/vm.php repro_hash_incremental.php
'
Engine Output
Zend b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9
VM same ✅

Repro — hash_copy() before finalize (FAIL / unverified)

<?php
$ctx = hash_init('sha256');
hash_update($ctx, 'hello ');
$ctx2 = hash_copy($ctx);
hash_update($ctx, 'world');
echo hash_final($ctx), "\n";
echo hash_final($ctx2), "\n";
Engine Expected
Zend full digest on $ctx; hello partial state digest on $ctx2
VM verify + fix if differs

Note: hash_copy() after hash_final() must TypeError on both engines (finalized context).

Scope (this repo)

Area Path
VM ext/hash/ or ext/standard/VmHash.php — HashContext object lifecycle
Builtins hash_copy.php, hash_pbkdf2.php, hash_hkdf.php registration
JIT deferred v1; document VM-only in matrix
Tests test/compliance/cases/stdlib/hash_copy.phpt, hash_pbkdf2_roundtrip.phpt

PHP-in-PHP first: digest state in PHP; libc only for underlying algo if already used by hash().

Done when (php-src-strict)

Verification

./script/docker-exec.sh -- bash -lc 'source script/php-env.sh && vendor/bin/phpunit --filter HashContext'

Links

Activity

  1. added
    enhancementNew feature or request
    phase-4:stdlibPhase 4 – stdlib for web apps
    area:vmVirtual machine
    implementation-readySpec complete: repro, php-src ref, done-when — safe for workers to claim
    on May 29, 2026
  2. PurHur commented on Jul 7, 2026

    @PurHur
    OwnerAuthor

    claim: PHP Compiler STD Lib — starting this run (JIT/AOT incremental hash_context)

  3. PurHur commented on Jul 7, 2026

    @PurHur
    OwnerAuthor

    Substantive WIP merged to PR #17087.

    Done this run

    • JIT/AOT lowering for hash_init / hash_update / hash_final / hash_copy via JitHashContext (object-property buffering + JitHash::hash finalize).
    • StringHashCryptoPhp bridge fix: coerce ABI i32 raw flag → helper i1 (fixes AOT module verify for __compiler_hash*).
    • VM repro + compliance green; AOT compile green.

    Blocker (not merged)

    • AOT execute segfaults at c:main_before_php when hash_update/hash_final are linked in the same binary (Docker). hash_init() alone runs.

    Verify

    php bin/vm.php test/repro/hash_context_incremental.php
    ./script/docker-exec.sh -- bash -lc 'source script/php-env.sh && php bin/compile.php -o /tmp/hash_ctx test/repro/hash_context_incremental.php'

    PR: #17087

  4. PurHur commented on Jul 7, 2026

    @PurHur
    OwnerAuthor

    claim: PHP Compiler STD Lib — continuing PR #17087 (AOT execute segfault on hash_update/hash_final)

  5. PurHur commented on Jul 7, 2026

    @PurHur
    OwnerAuthor

    WIP update (continued PR #17087)

    Committed f3e9ed490: HashContext __hc* properties now use TYPE_STRING object slots + deep-copy in hash_copy(); StringHashCrypto::ensureLinked moved to hash_final() only.

    Still red — AOT execute

    • VM + JIT: test/repro/hash_context_incremental.php green
    • AOT: vendor/bin/phpunit test/aot/AotTest.php --filter hash_context_incremental → empty stdout (compile OK)
    • Bisect: emitting hash_init() JIT in the same binary appears to poison subsequent hash() echo output (even bare allocate() without property stores). Master cannot AOT-compile hash() at all (LLVM verify i32→i1 bridge); this branch fixes compile via StringHashCryptoPhp::callHelperBridge but runtime digest echo is still broken when HashContext builtins are linked.

    Next step

    • PHP-in-PHP HashContextJitHelper + thin ABI (object-keyed incremental state), or gdb on c:main_before_php with PHP_COMPILER_LLVM_ASSERT=1
    • Reference: php-src ext/hash/hash.c php_hash_* context copy/finalize
  6. PurHur commented on Jul 7, 2026

    @PurHur
    OwnerAuthor

    claim: PHP Compiler STD Lib Advanced — continuing hash JIT/AOT PR #17087 (AOT execute segfault)

  7. PurHur commented on Jul 7, 2026

    @PurHur
    OwnerAuthor

    Progress update (advanced stdlib): continued PR #17087 branch.

    VM: test/repro/hash_context_incremental.php green.

    AOT bisect:

    • hash_init() only → runs OK
    • hash_update() / hash_copy() / hash_final() → segfault at c:main_before_php (master + branch)

    Commits pushed: pre-register HashContext __hc* string slots in registerExternalClass; direct slot load/store in JitHashContext (avoid double __string__separate). VM unchanged; AOT read path still crashes.

    Next: bisect whether loadStringPtrProperty returns null/corrupt vs HashContextRuntime/StringHashCrypto init when hash_final is linked; gdb/LLVM IR dump on minimal hash_copy binary.

  8. 48 remaining items

  9. PurHur commented on Jul 13, 2026

    @PurHur
    OwnerAuthor

    Run handoff (2026-07-13) — partial progress on user-script hash AOT defer path; merge blocked by master-wide AOT regression.

    Claim

    claim: PHP Compiler Ext — I/O & Crypto — starting this run (hash AOT standalone digest bridges, PR #17897 / #3357).

    Work pushed

    Branch agent/issue-3357-hash-aot-standalone:

    • StringHashAlgos: user-script defer → inline HashAlgosRegistry LLVM (avoid nested HashAlgosJitHelper / broken __hashtable__ bridge)
    • StringHashCryptoJit: user-script defer → skip nested HashEqualsJitHelper; stub __compiler_hash_equals; keep StringHashCryptoLlvm EVP path

    Verification

    # VM — green
    ./script/phpunit.sh --filter 'HashModuleTest|hash_context_incremental'
    
    # AOT — red on master (not hash-specific)
    ./script/docker-exec.sh -- bash -lc 'source script/php-env.sh && php bin/compile.php -o /tmp/h -r "echo \"hi\n\";"'
    # Module verification failed: __value__writeHashtable signature mismatch (__value__* vs __hashtable__*)
    # Persists even with StringHashCrypto::ensureStandaloneBodies commented out of ensureMinimalUserStandaloneBodies
    ./script/phpunit.sh --filter 'AotTest.*hash_context_incremental'  # compile exit 255

    Blocker

    Master user-script AOT is broken for all -r/examples compiles (echo-only repro fails LLVM module verify). Root cause is not isolated to hash crypto init — disabling StringHashCrypto in Context::ensureMinimalUserStandaloneBodies does not unblock. Likely recent standalone-init / helper-runtime / hashtable bridge regression (suspect commits around 8486e08e7 utf8, 36952166d mb_numericentity, 56c59024f helper ctors — needs bisect).

    Next step

    1. Bisect master AOT verify failure (writeHashtable / alloca %__hashtable__* pattern)
    2. Re-run hash AOT fixtures + merge defer path once standalone init is green
  10. PurHur commented on Jul 13, 2026

    @PurHur
    OwnerAuthor

    claim: PHP Compiler Ext — I/O & Crypto — starting this run (hash_pbkdf2 AOT via StringHashCryptoLlvm PKCS5_PBKDF2_HMAC)

  11. PurHur commented on Jul 13, 2026

    @PurHur
    OwnerAuthor

    Merged (hash_pbkdf2 AOT): #18750

    Standalone user-script AOT hash_pbkdf2() now uses OpenSSL PKCS5_PBKDF2_HMAC in StringHashCryptoLlvm instead of the null __compiler_hash_pbkdf2 stub (exit 139 → correct digest).

    Verification:

    source script/php-env.sh
    ./script/phpunit.sh --filter 'AotTest.*hash_pbkdf2|AotTest.*hash_raw_output|AotTest.*hash_context|AotTest.*hash_hmac'
    # OK (4 tests, 20 assertions)

    Still open on #3357: hash_hkdf AOT stub, hash_hmac_algos AOT segfault (pre-existing on master).

  12. PurHur commented on Jul 14, 2026

    @PurHur
    OwnerAuthor

    claim: PHP Compiler Ext — I/O & Crypto — starting this run (hash_hkdf/hash_pbkdf2 AOT segfault on standalone user-script)

  13. PurHur commented on Jul 14, 2026

    @PurHur
    OwnerAuthor

    Merged via #18986.

    Root cause: user-script standalone AOT defers nested PHP helpers (documented in UserScriptAotDeferNestedJit). bin2hex() still routed through nested Bin2hexJitHelper → segfault after strlen() on raw hash/HKDF/PBKDF2 output. Raw digests also used __string__init from stack alloca buffers → zero/incorrect payload bytes.

    Fix: inline LLVM hex loop for bin2hex defer (mirrors StringHashCryptoLlvm hex path) + __string__alloc + memcpy for raw EVP/HKDF/PBKDF2 digests.

    Verification (Docker):

    ./vendor/bin/phpunit --filter 'AotTest.*hash_raw|AotTest.*hash_hkdf|AotTest.*hash_pbkdf2|Bin2hexRuntimeShrinkTest'
    # OK (6 tests, 34 assertions)
    
  14. PurHur commented on Jul 15, 2026

    @PurHur
    OwnerAuthor

    claim: PHP Compiler Ext — I/O & Crypto — starting this run (hash_final AOT standalone returns raw digest not hex — hash_context_incremental AOT red)

  15. PurHur commented on Jul 15, 2026

    @PurHur
    OwnerAuthor

    Merged via #19181.

    Fix: standalone AOT hash_final() now hex-encodes via __compiler_bin2hex when $binary=false (was emitting raw 32-byte SHA-256).

    Verification:

    php vendor/bin/phpunit --filter 'hash_context_incremental|hash_raw_output'
    # OK (9 tests, 17 assertions)
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 machineenhancementNew feature or requestimplementation-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