Skip to content

fix(plc4j/drivers/s7): release direct ByteBufs allocated for alarm-query reassembly#2543

Open
michele-tramonti wants to merge 1 commit into
apache:developfrom
michele-tramonti:fix/s7-alarmquery-direct-buffer-leak
Open

fix(plc4j/drivers/s7): release direct ByteBufs allocated for alarm-query reassembly#2543
michele-tramonti wants to merge 1 commit into
apache:developfrom
michele-tramonti:fix/s7-alarmquery-direct-buffer-leak

Conversation

@michele-tramonti
Copy link
Copy Markdown

Summary

Second direct-memory leak I noticed while auditing #2248 (and its fix #2542). Same family of bug, different code path.

S7ProtocolLogic.decodeEventSubscriptionResponse() allocates two direct ByteBufs when handling S7PayloadUserDataItemCpuFunctionAlarmQueryResponse:

```java
ByteBuf buffer = Unpooled.directBuffer(items.getItems().length * 2);
ByteBuf rxBuffer = Unpooled.directBuffer(items.getItems().length * 2);
```

They are populated, then drained via `ByteBufUtil.getBytes(rxBuffer)` (which copies the bytes into a Java array) and the method returns. Neither buffer is ever `release()`d. `ByteBufUtil.getBytes` does not own the buffer and does not release it.

On a PLC that emits alarms regularly, every alarm event leaks two direct buffers. On long-running applications this slowly exhausts `MaxDirectMemorySize` and triggers `OutOfMemoryError: Cannot reserve N bytes of direct buffer memory` — the same symptom as #2248, different root cause.

Fix

Wrap the existing logic in a try/finally so both buffers are released on every path, including when the inner parsing block (or any of the loopFuture.get() calls) propagates an exception.

No change in behaviour or wire format — the buffers are local to the method and only used for reassembly before the bytes are copied out via ByteBufUtil.getBytes.

Test plan

  • mvn -pl :plc4j-driver-s7 -P with-java test passes

…ry reassembly

S7ProtocolLogic.decodeEventSubscriptionResponse() allocates two direct ByteBufs
(`buffer` and `rxBuffer`) when handling S7PayloadUserDataItemCpuFunctionAlarmQueryResponse,
but never releases them. ByteBufUtil.getBytes(rxBuffer) copies the bytes into a
Java array but does not release the underlying Netty buffer.

On a PLC that emits alarms frequently, this leaks two direct buffers per event,
slowly exhausting MaxDirectMemorySize on long-running applications. It is the
same family of leak as apache#2248 but in a different code path (alarm subscriptions
rather than the multiplex outbound queue).

Wrap the existing logic in a try/finally so both buffers are always released,
including when an exception propagates out of the parsing block.
@chrisdutz
Copy link
Copy Markdown
Contributor

Hi Michele,

thanks for the PR ... however I should mention that in the SPI3 PR I'm currently completely rewriting all of our drivers to completely work without Netty and everything that was making our life miserable.

Chris

spnettec added a commit to spnettec/plc4x that referenced this pull request May 31, 2026
…lc4x

- fix(s7): S7HMuxImpl direct buffer leak per-message (PR apache#2542)
- fix(s7): alarm-query directBuffer not released in S7ProtocolLogic and S7NonHProtocolLogic (PR apache#2543)
- fix(s7,spi): decodeLargeReadResponse heap buffer not released in S7ProtocolLogic and S7NonHProtocolLogic
- feat(spi): SharedExecutor — JVM-scoped thread pools replace per-connection ThreadPoolExecutors to prevent thread accumulation on reconnect (PR apache#2281)
- fix(spi): RequestTransactionManager.shutdown() cancels in-flight requests without shutting down shared executor
- feat(spi): channelInactive lifecycle hook on Plc4xProtocolBase and Plc4xNettyWrapper; all drivers release resources on TCP disconnect (PR apache#2241)
- fix(eip): NPE in decodeSingleReadResponse when PLC returns error status and getData() is null (PR apache#2471)
- fix(umas): align pom version to 0.14.0.B-SNAPSHOT and fix RequestTransactionManager constructor call

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
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.

2 participants