Separate disposal from list removal so bulk cleanup updates the live list head explicitly before invoking callbacks. Cover recursive cleanup across two subscriptions and use the existing synchronized server pause helpers in the notification regression.
Adapt the 1.5 fixes to master's discovery, alarms and conditions, session, and nodeset-loader architecture. Preserve master behavior and combine PubSub replay protection with authenticated key rollover. Enable allocation-failure regression coverage and retain both branches' relevant tests.
Include stdlib.h explicitly in examples using EXIT_SUCCESS or EXIT_FAILURE. Allocator hooks disable the transitive inclusion through config.h in the alarms coverage build.
Use the existing test-client helper for callback lifetime regressions so client and server share the fake clock and the client has the Valgrind-safe timeout.
Notification, inactivity and deletion callbacks may remove their subscription or clear the client. Mark deleted objects as removed before invoking user callbacks, revalidate retained subscriptions after callbacks and stop processing when they are gone.
When a PublishResponse comes back with BadSessionClosed or BadSessionIdInvalid, processMSGResponse takes the request off the list and cleans up the Session before calling the response callback. cleanupSession cancels the other PublishRequests, whose callbacks decrement the counter, and then resets it to zero. The callback of the response that triggered the cleanup runs afterwards and decrements it once more, so the UInt16 wraps to 65535. From then on backgroundPublish never sends a PublishRequest, also not for the next Session, until the client disconnects.
Every PublishRequest already decrements the counter exactly once, when its callback runs for the response or for the cancellation, so the reset is not needed. Remove it.
The synchronous CreateMonitoredItems and ModifyMonitoredItems keep the Subscription and MonitoredItem pointers across the service call. If the Session is closed meanwhile (BadSessionClosed on a queued Publish, or BadSessionIdInvalid on the request itself), the client removes its Subscriptions before the call returns and these pointers are used after free. For CreateMonitoredItems the MonitoredItem is freed twice and its delete callback runs twice. Look them up again by id and client handle after the call, as the async handlers already do.
Port keyless certificate validation into the GDS receiver driver instead
of restoring the removed server implementation. Preserve master's timer
batching, PubSub lifecycle and client HTTP configuration handling.
Adapt Publish cancellation to response tokens and the HEL/fuzz harnesses
to the explicit TCP driver. Keep SKS policy history and custom datatype
ownership intact, including failed replacement configuration cleanup.
Retain TPM error cleanup while enforcing key-handle and nonce lengths.
Validation:
- Full mbedTLS unit-test build with full namespace, GDS, SKS and Query.
- 21 affected suites passed with ASan/UBSan, plus 9 isolated SKS ownership
and lifecycle checks.
- OpenSSL certificate update, GDS information model and client encryption
suites passed with sanitizers.
- OPN, MSG and binary-message fuzzers each completed 100 sanitizer runs.
- Both TPM PubSub policy sources passed compilation checks.
The full SKS client suite timed out after multicast UDP sends reported
"No route to host" on this machine; its affected ownership and lifecycle
cases passed separately. No live TPM hardware validation was performed.
Adapt the incoming fixes and regressions to the 1.5 encoding options,
nodestore, connection-manager, PubSub and session APIs. Preserve the
strict HEL EndpointUrl limit of less than 4096 bytes.
Cover cancellation of interleaved queued Publish requests, including
response handles, cancellation counts and a successful CancelResponse.
Extend the nodestore mask regression to 1.5's derived attributes and
invalid attribute identifiers.
Validation: full OpenSSL test build; 252 checks passed across the builtin
types, server, attribute services, client subscriptions and mbedTLS PubSub
decryption suites, with sanitizers enabled.
Use pointer-sized atomic run flags in threaded fixtures, including dynamically
allocated discovery and browse flags. Pointer sizing supports the MSVC atomic
implementation as well as C11 and GCC atomics.
Serialize the client error-injection test with the server network callbacks
before accessing the internal session and shared send buffer.
iterateUntilNotification polls the client with a zero timeout, so its
maxIterations budget is not a duration: the loop spins as fast as the CPU
allows and can return long before a PublishResponse becomes readable on
the socket. Whether the test passes therefore depends on the ratio between
the machine's loopback delivery latency and the cost of one spin. That
ratio is not controlled by the test, which is why the loop can be green on
one host and red on another.
On a WSL2 host the 100 spins take about 0.08ms while the response needs
about 0.6-1.0ms to be delivered, so Client_subscription_modifyMonitoredItem
fails on the initial-value assertion (notificationReceived == 0) on every
run, on unmodified 1.5 and with the CI build options.
Advancing the simulated clock does not help: UA_fakeSleep drives the OPC UA
timers, not the kernel. Measured on the failing host, +2000ms of simulated
time still fails, while giving the same loop real time succeeds after
0.6-1.0ms.
Use a 1ms client timeout so each iteration actually waits, which is what
Client_subscription_createDataChanges in the same file already does. The
assertions are unchanged.
findMonitoredItemForNotification falls back to a full linear scan of the
MonitoredItem ziptree when a notification clientHandle is not found, to
promote a pending re-key from a ModifyMonitoredItems. A malicious or buggy
server can send a PublishResponse full of notifications with unknown
clientHandles: each triggers the full O(n) scan, i.e. O(N*n) CPU burn on
the single client event-loop thread, stalling reconnection and every other
subscription.
Track the number of pending re-keys per subscription (pendingRekeys) and
skip the scan when it is zero, which is the common case since a re-key is
pending only briefly after a ModifyMonitoredItems. The counter is
maintained where pendingParameters is set (modify), cleared (rollback,
delete) and promoted (on the authoritative notification).
Extend Client_subscription_modifyMonitoredItem_doubleBuffer to assert the
counter (0 -> 1 -> 0) and that a notification with an unknown handle is
dropped without a scan while nothing is pending.