nodejs / help

:sparkles: Need help with Node.js? File an Issue here. :rocket:
1.44k stars 276 forks source link

Spawning worker threads (with native node add on) result in "Memory Access Error" (special capabilities are not inherited) #4395

Open mStirner opened 1 month ago

mStirner commented 1 month ago

Node.js Version

v20.11.0

NPM Version

v10.5.1

Operating System

Linux workstation 6.5.0-28-generic #29-Ubuntu SMP PREEMPT_DYNAMIC Thu Mar 28 23:46:48 UTC 2024 x86_64 x86_64 x86_64 GNU/Linux

Subsystem

worker_threads

Description

Main thread, do not pass capabilities to worker threads. Setted capabilities on the node.js executable: E.g.:

sudo getcap /usr/bin/node
/usr/bin/node cap_net_bind_service,cap_net_admin,cap_net_raw=eip

Results in the worker who needs them in a "memory access error".

Minimal Reproduction

const {
    isMainThread,
    Worker
} = require("worker_threads");

const raw = require("raw-socket");

if (isMainThread && process.argv[2] !== "--direct") {

    let worker = new Worker(__filename, {
        env: process.env
    });

    worker.once("online", () => {
        console.log("[main] Worker online");
    });

    worker.once("error", (err) => {
        console.log("[main] Worker error", err);
    });

    worker.once("exit", (code) => {
        console.log("[main] Worker exited", code);
    });

} else {
    try {

        console.log("[worker] send ping");

        let socket = raw.createSocket({
            protocol: raw.Protocol.ICMP
        });

        // without this, no "Speicherzugriffsfehler"/"Memory access error" error is printed
        // worker exit with code 0, no error/hint printed
        console.log("socket created");

        socket.on("message", (buffer, source) => {
            console.log(`[worker] response recieved answer from ${source}`, buffer);
        });

        // ICMP echo (ping) request
        var buffer = Buffer.from([
            0x08, 0x00, 0x00, 0x00, 0x00, 0x01, 0x0a, 0x09,
            0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68,
            0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70,
            0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x61,
            0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69
        ]);

        raw.writeChecksum(buffer, 2, raw.createChecksum(buffer));

        socket.send(buffer, 0, buffer.length, "8.8.8.8", (error, bytes) => {
            if (error) {
                console.log("[worker] ping error", error.toString());
            } else {
                console.log(`[worker] ping send to 8.8.8.8`);
            }
        });

    } catch (err) {

        console.log(err);

    }
}

For the example above a the module "raw-socket" is used to create raw network sockets to send icmp ping messages. When used used with the argument --direct the script creates directly a raw socket. Without it, it spawns a worker thread and trys to create the raw socket in the worker thread, which results in the error described above.

Output

sudo /usr/bin/node test.js

[main] Worker online
[worker] send ping
socket created
Speicherzugriffsfehler

sudo /usr/bin/node test.js --direct

[worker] send ping
socket created
[worker] ping send to 8.8.8.8
[worker] response recieved answer from 8.8.8.8 <Buffer 45 80 00 3c 00 00 00 00 71 01 75 eb 08 08 08 08 c0 a8 02 9e 00 00 4b 52 00 01 0a 09 61 62 63 64 65 66 67 68 69 6a 6b 6c 6d 6e 6f 70 71 72 73 74 75 76 ... 10 more bytes>

Before You Submit

mStirner commented 1 month ago

Forget to mention, i used a patched version of "raw-socket", since it currently does not work in worker threads: https://github.com/nospaceships/node-raw-socket/issues/89

diff --git a/src/raw.cc b/src/raw.cc
index b8e4850..b8f50e5 100644
--- a/src/raw.cc
+++ b/src/raw.cc
@@ -52,7 +52,9 @@ void InitAll (Local<Object> exports) {
    SocketWrap::Init (exports);
 }

-NODE_MODULE(raw, InitAll)
+// see #89
+//NODE_MODULE(raw, InitAll)
+NAN_MODULE_WORKER_ENABLED(raw, InitAll)

 NAN_METHOD(CreateChecksum) {
    Nan::HandleScope scope;

Patched version: raw.node.tar.gz

preveen-stack commented 1 month ago

Speicherzugriffsfehler

This means segmentation fault, right.

Where is the error you mentioned in the title?

mStirner commented 1 month ago

Speicherzugriffsfehler

This means segmentation fault, right.

Not sure what the exact translation is, but "Speicherzugriffsfehler" is loose translated to "Memory access error". There is no other error message than "Speicherzugriffsfehler". Thats all that is printed on the console.

I created a native add-on (to debug this issue) that prints the capabilities of the executable: https://stackoverflow.com/q/78496672/5781499 Event when there are some set on the /usr/bin/node, they are not correctly inherited to child processes.