Skip to content

fix: remove constructor.Reset() from S7Client/S7Server destructors#107

Open
JordiB-Inserma wants to merge 2 commits into
mathiask88:masterfrom
JordiB-Inserma:fix/s7-driver-linux-segfault
Open

fix: remove constructor.Reset() from S7Client/S7Server destructors#107
JordiB-Inserma wants to merge 2 commits into
mathiask88:masterfrom
JordiB-Inserma:fix/s7-driver-linux-segfault

Conversation

@JordiB-Inserma

Copy link
Copy Markdown

Summary

S7Client and S7Server destructors were calling constructor.Reset() on a static Nan::Persistent<FunctionTemplate>, which destroys the V8 constructor template shared across all instances. This causes segfaults on Linux when applications create and destroy S7Client instances repeatedly (e.g. SCADA/HMI reconnection loops like FUXA).

Root cause

constructor is a static class member initialized once during module load (Init). It stores the FunctionTemplate that V8 uses to create new instances. Both ~S7Client() and ~S7Server() were calling constructor.Reset(), which releases the persistent handle to the static FunctionTemplate.

Since this is a static resource shared across ALL instances:

  1. new snap7.S7Client() — works, FunctionTemplate alive
  2. Instance destroyed (GC/reference lost) → constructor.Reset() destroys the static template
  3. Next new snap7.S7Client() → V8's internal constructor resolution fails → SEGFAULT

The crash is non-deterministic and depends on V8 GC timing. On Linux, the GC is more aggressive, making this crash frequent in production SCADA systems.

Fix

Remove constructor.Reset() from both ~S7Client() (node_snap7_client.cpp:722) and ~S7Server() (node_snap7_server.cpp:682). The static FunctionTemplate should live for the lifetime of the module, not per-instance.

Changes

File Change
src/node_snap7_client.cpp Removed constructor.Reset() from ~S7Client()
src/node_snap7_server.cpp Removed constructor.Reset() from ~S7Server()

Stack trace before fix (Linux)

Thread 1 "FUXA" received signal SIGSEGV, Segmentation fault.
#0  v8::internal::ApiNatives::InstantiateFunction(...)
#1  v8::FunctionTemplate::GetFunction(v8::Local<v8::Context>)
#2  node_snap7::S7Client::New(Nan::FunctionCallbackInfo<v8::Value> const&)

Related

  • Upstream pthread fix: davenardella/snap7#29 — prerequisite for full Linux stability (fixes undefined behavior when joining detached threads)
  • Upstream issues: davenardella/snap7#19, #23
  • Environment: Linux, Node.js 18+, node-snap7 1.0.9, FUXA SCADA v1.3.3

S7Client::~S7Client() and S7Server::~S7Server() were calling
constructor.Reset() on a static Nan::Persistent<FunctionTemplate>.
This releases the persistent handle to the FunctionTemplate that
V8 uses to create new instances of the object.

Since constructor is a static class member shared across ALL
instances, destroying ANY single instance invalidates the
constructor template for every existing and future instance.
When V8's GC collects the invalidated template and a new
instance is created, S7Client::New tries to resolve the
constructor via FunctionTemplate::GetFunction() which returns
a stale/collected handle, causing a segfault.

This crash is non-deterministic and depends on V8 GC timing.
On Linux, the GC is more aggressive, making the crash frequent
in applications with repeated create/destroy cycles (e.g.
SCADA/HMI reconnection loops).

The fix is simply removing constructor.Reset() from both
destructors. The static FunctionTemplate should live for the
lifetime of the module, not per-instance.

References:
- davenardella/snap7#29 (upstream pthread fix, prerequisite
  for full Linux stability)
- davenardella/snap7#19 (server destructor race)
Add AGENTS.md, .opencode/, .claude/, .cursorrules, and
.github/copilot-instructions.md to both .gitignore and
.npmignore to keep AI assistant configuration files out of
version control and npm package distribution.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant