Skip to content

Fix php-wasm popen stream leaks#3679

Open
chubes4 wants to merge 3 commits into
WordPress:trunkfrom
chubes4:fix/php-wasm-popen-stream-leak
Open

Fix php-wasm popen stream leaks#3679
chubes4 wants to merge 3 commits into
WordPress:trunkfrom
chubes4:fix/php-wasm-popen-stream-leak

Conversation

@chubes4
Copy link
Copy Markdown
Contributor

@chubes4 chubes4 commented May 22, 2026

Summary

  • Close wasm popen() read-mode output with regular file-stream semantics because the shim buffers process output into /tmp/popen_output, not a real process pipe.
  • Apply the same close semantics to the php_exec() and shell_exec() paths that consume the same wasm popen() shim.
  • Add regression coverage for repeated popen(), exec(), shell_exec(), and system() calls.
  • Update the committed PHP 8.3 node JSPI/Asyncify wasm binaries used by the regression test while leaving generated JS unchanged.

Fixes #3678.

Testing

  • node packages/php-wasm/compile/build.js --PLATFORM=node --PHP_VERSION=8.3.31 --WITH_JSPI=yes
  • node packages/php-wasm/compile/build.js --PLATFORM=node --PHP_VERSION=8.3.30
  • PHP=8.3 npm exec nx -- run php-wasm-node:test-group-1-jspi --testNamePattern=\"closes read-mode process output streams\"
  • PHP=8.3 npm exec nx -- run php-wasm-node:test-group-1-asyncify --testNamePattern=\"closes read-mode process output streams\"
  • npm exec nx -- run php-wasm-node:lint
  • npm exec nx -- run php-wasm-node:typecheck

Notes

  • Generated JS files were restored after rebuilding to avoid generated JS churn; the tests pass against the updated committed wasm binaries.
  • The Asyncify node build currently points at 8_3_30, while JSPI points at 8_3_31, so the rebuilt wasm artifacts preserve those existing paths.

AI assistance

  • AI assistance: Yes
  • Tool(s): OpenCode (openai/gpt-5.5)
  • Used for: Root cause analysis, patch drafting, regression test drafting, and local verification. Chris reviewed and directed the work.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Note

Copilot was unable to run its full agentic suite in this review.

This PR fixes file descriptor/stream leaks in the php-wasm popen() read-mode shim by switching to regular file closing semantics, and adds regression tests to ensure repeated process-output APIs don’t leak /tmp/popen_output streams.

Changes:

  • Switch read-mode process-output streams from pipe-backed streams to file-backed streams (closing via fclose() semantics).
  • Apply the same behavior to exec() / shell_exec() / system() paths that consume the same shim.
  • Add a regression test that runs 100 iterations per API and asserts no increase in open /tmp/popen_output streams (for PHP 8.3 builds).

Reviewed changes

Copilot reviewed 3 out of 7 changed files in this pull request and generated 3 comments.

File Description
packages/php-wasm/node/src/test/php-part-1.spec.ts Adds regression coverage asserting no /tmp/popen_output stream leaks across repeated calls.
packages/php-wasm/compile/php/php_wasm.c Switches wasm_php_exec stream creation from pipe semantics to file semantics.
packages/php-wasm/compile/php/Dockerfile Patches upstream PHP sources so read-mode popen/exec use file semantics in wasm builds.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread packages/php-wasm/node/src/test/php-part-1.spec.ts Outdated
Comment on lines +623 to +629
const openPopenOutputCount = () =>
(php as any)[__private__dont__use].FS.streams
.filter(Boolean)
.filter(
(stream: any) =>
stream.path === '/tmp/popen_output'
).length;
Comment thread packages/php-wasm/compile/php/Dockerfile Outdated
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

php-wasm leaks popen output streams until FS.ErrnoError(33)

2 participants