libplctag wrapper crashes the process: callback on a garbage-collected delegate (callback_func_ex) #578
Reference in New Issue
Block a user
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Found 2026-10-07 during the native AB transport real-press read-only gate (Task 21, branch
feat/native-ab-transport, plandocs/plans/2026-10-07-native-ab-transport.md).What happened
At the end of a read-only differential/latency run against a production CompactLogix 5069-L320ERS2 (Z-35162), the
dotnet testhost process died with:This is a fatal CLR failure (not catchable). The packet capture shows the last session never sent a Forward Close, so the process died before or during cleanup. No writes were involved (capture verified: zero write services).
Why it matters
The libplctag .NET wrapper (
libplctag1.5.2) registers a native callback. If the managed delegate is collected while the native library can still invoke it — e.g. aTagfinalized or disposed while an async read is in flight — the whole process terminates. If the production AbCip/AbLegacy libplctag adapters (Libplctag*.cs) can reach the same state (driver shutdown/reinit with reads pending, a timeout-cancelledReadAsync), an OtOpcUa server node would crash outright.To do
Transport=Nativeremoves the libplctag native callback path entirely for that driver.Investigation in progress on the branch; this issue will be updated with the verdict.
Verdict: production-reachable — fixed on
feat/native-ab-transportin6cd68762(under review).Root cause (libplctag.NET 1.5.2): when a tag's native create fails (e.g.
NotFoundfor a mistyped tag name), the wrapper leaves the native tag and its registered callback in place, andTag.Dispose()does not release them. Once the GC collects the managed delegate, libplctag's process-exit shutdown invokes it and the CLR terminates the process.Reproduced against ab_server with the production
LibplctagTagRuntime: without the fix the process exits 134 with the exactcallback_func_exmessage; with it, exit 0.Exposure: every libplctag create path in AbCip and AbLegacy — data path, probe loop, Test Connect, symbol browse and template reads. Before the fix:
Fix: after a failed initialisation the adapters destroy the leftover native tag; tests fail with the fix removed. It relies on two private field names in the 1.5.2 wrapper; a test pins them, and if they change the code keeps the tag alive (small leak, no crash) rather than crashing.
Drivers set to
Transport=Nativedon't use the libplctag callback path at all.Fixed before this sweep (
6cd68762, merged in04b65632): adapters release the orphaned native tag after a failed create; the release helper is now shared by both drivers (#580). (merge04b65632on master, pushed 2026-10-08)