Skip to content

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

Open
michele-tramonti wants to merge 1 commit intoapache: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 intoapache: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

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