Repository: nodejs/node
Pull request: child_process: treat ipc length header as unsigned uint32

Fixes: https://github.com/nodejs/node/issues/61312

<!--
Before submitting a pull request, please read:

- the CONTRIBUTING guide at https://github.com/nodejs/node/blob/HEAD/CONTRIBUTING.md
- the commit message formatting guidelines at
  https://github.com/nodejs/node/blob/HEAD/doc/contributing/pull-requests.md#commit-message-guidelines

For code changes:
1. Include tests for any bug fixes or new features.
2. Update documentation if relevant.
3. Ensure that `make -j4 test` (UNIX), or `vcbuild test` (Windows) passes.

If you believe this PR should be highlighted in the Node.js CHANGELOG
please add the `notable-change` label.

Developer's Certificate of Origin 1.1

By making a contribution to this project, I certify that:

(a) The contribution was created in whole or in part by me and I
    have the right to submit it under the open source license
    indicated in the file; or

(b) The contribution is based upon previous work that, to the best
    of my knowledge, is covered under an appropriate open source
    license and I have the right under that license to submit that
    work with modifications, whether created in whole or in part
    by me, under the same open source license (unless I am
    permitted to submit under a different license), as indicated
    in the file; or

(c) The contribution was provided directly to me by some other
    person who certified (a), (b) or (c) and I have not modified
    it.

(d) I understand and agree that this project and the contribution
    are public and that a record of the contribution (including all
    personal information I submit with it, including my sign-off) is
    maintained indefinitely and may be redistributed consistent with
    this project or the open source license(s) involved.
-->


Changed files:
filename: lib/internal/child_process/serialization.js
patch: @@ -74,12 +74,12 @@ const advanced = {
     while (messageBufferHead.length >= 4) {
       // We call `readUInt32BE` manually here, because this is faster than first converting
       // it to a buffer and using `readUInt32BE` on that.
-      const fullMessageSize = (
+      const fullMessageSize = ((
         messageBufferHead[0] << 24 |
         messageBufferHead[1] << 16 |
         messageBufferHead[2] << 8 |
         messageBufferHead[3]
-      ) + 4;
+      ) >>> 0) + 4;
 
       if (channel[kMessageBufferSize] < fullMessageSize) break;
 

filename: test/parallel/test-child-process-fork-advanced-header-serialization.js
patch: @@ -0,0 +1,42 @@
+'use strict';
+const common = require('../common');
+const assert = require('assert');
+const { fork } = require('child_process');
+const fs = require('fs');
+
+if (process.argv[2] === 'child-buffer') {
+  const v = process.argv[3];
+  const payload = Buffer.from([
+    (v >> 24) & 0xFF,
+    (v >> 16) & 0xFF,
+    (v >> 8) & 0xFF,
+    v & 0xFF,
+  ]);
+  const fd = process.channel?.fd;
+  if (fd === undefined) {
+    // skip test
+    process.exit(0);
+  }
+  fs.writeSync(fd, payload);
+  return;
+}
+
+const testCases = [
+  0x00000001,
+  0x7fffffff,
+  0x80000000,
+  0x80000001,
+  0xffffffff,
+];
+
+for (const size of testCases) {
+  const child = fork(__filename, ['child-buffer', size], {
+    serialization: 'advanced',
+    stdio: ['inherit', 'inherit', 'inherit', 'ipc'],
+  });
+
+  child.on('exit', common.mustCall((code, signal) => {
+    assert.strictEqual(code, 0);
+    assert.strictEqual(signal, null);
+  }));
+}