This is an automated email from the ASF dual-hosted git repository.
RongtongJin pushed a commit to branch develop
in repository https://gitbox.apache.org/repos/asf/rocketmq.git
The following commit(s) were added to refs/heads/develop by this push:
new 88846a0a5b [ISSUE #10288] Hold MappedFile reference in ConsumeQueue
write path to prevent SIGSEGV (#10566)
88846a0a5b is described below
commit 88846a0a5b550116d183f62c1a468e75ca3f61d4
Author: Jiahua Wang <[email protected]>
AuthorDate: Thu Sep 3 15:55:22 2026 +0800
[ISSUE #10288] Hold MappedFile reference in ConsumeQueue write path to
prevent SIGSEGV (#10566)
ConsumeQueue.putMessagePositionInfo() accesses MappedFile without
holding a reference count. A concurrent cleanExpiredConsumeQueue
can destroy() the MappedFile (unmapping its buffer) while the
writer is still using it, causing SIGSEGV in JIT-compiled
Unsafe.copyMemory.
Fix: Call mappedFile.hold() before accessing the buffer and
release() in a finally block, following the same pattern used
in DefaultMappedFile.getData(). Also protect the fillPreBlank
call in initializeWithOffset.
Reported crash signatures:
- Case 1 (v4.9.4): SEGV_MAPERR in ReputMessageService thread
at ConsumeQueue.putMessagePositionInfo
- Case 2 (v5.3.3): SEGV in AdminBrokerThread at
ConsumeQueueStore.cleanExpired
Co-authored-by: wangjiahua.wjh <[email protected]>
---
.../org/apache/rocketmq/store/ConsumeQueue.java | 86 +++++++++++++---------
1 file changed, 53 insertions(+), 33 deletions(-)
diff --git a/store/src/main/java/org/apache/rocketmq/store/ConsumeQueue.java
b/store/src/main/java/org/apache/rocketmq/store/ConsumeQueue.java
index 0d698dacfe..3503d8fcfc 100644
--- a/store/src/main/java/org/apache/rocketmq/store/ConsumeQueue.java
+++ b/store/src/main/java/org/apache/rocketmq/store/ConsumeQueue.java
@@ -856,44 +856,52 @@ public class ConsumeQueue implements
ConsumeQueueInterface {
MappedFile mappedFile =
this.mappedFileQueue.getLastMappedFile(expectLogicOffset);
if (mappedFile != null) {
-
- if (mappedFile.isFirstCreateInQueue() && cqOffset != 0 &&
mappedFile.getWrotePosition() == 0) {
- this.minLogicOffset = expectLogicOffset;
- this.mappedFileQueue.setFlushedWhere(expectLogicOffset);
- this.mappedFileQueue.setCommittedWhere(expectLogicOffset);
- this.fillPreBlank(mappedFile, expectLogicOffset);
- log.info("fill pre blank space " + mappedFile.getFileName() +
" " + expectLogicOffset + " "
- + mappedFile.getWrotePosition());
+ if (!mappedFile.hold()) {
+ log.warn("Failed to hold mapped file for ConsumeQueue write,
topic={} queueId={}",
+ this.topic, this.queueId);
+ return false;
}
+ try {
+ if (mappedFile.isFirstCreateInQueue() && cqOffset != 0 &&
mappedFile.getWrotePosition() == 0) {
+ this.minLogicOffset = expectLogicOffset;
+ this.mappedFileQueue.setFlushedWhere(expectLogicOffset);
+ this.mappedFileQueue.setCommittedWhere(expectLogicOffset);
+ this.fillPreBlank(mappedFile, expectLogicOffset);
+ log.info("fill pre blank space " +
mappedFile.getFileName() + " " + expectLogicOffset + " "
+ + mappedFile.getWrotePosition());
+ }
- if (cqOffset != 0) {
- long currentLogicOffset = mappedFile.getWrotePosition() +
mappedFile.getFileFromOffset();
+ if (cqOffset != 0) {
+ long currentLogicOffset = mappedFile.getWrotePosition() +
mappedFile.getFileFromOffset();
- if (expectLogicOffset < currentLogicOffset) {
- log.warn("Build consume queue repeatedly,
expectLogicOffset: {} currentLogicOffset: {} Topic: {} QID: {} Diff: {}",
- expectLogicOffset, currentLogicOffset, this.topic,
this.queueId, expectLogicOffset - currentLogicOffset);
- return true;
- }
+ if (expectLogicOffset < currentLogicOffset) {
+ log.warn("Build consume queue repeatedly,
expectLogicOffset: {} currentLogicOffset: {} Topic: {} QID: {} Diff: {}",
+ expectLogicOffset, currentLogicOffset, this.topic,
this.queueId, expectLogicOffset - currentLogicOffset);
+ return true;
+ }
- if (expectLogicOffset != currentLogicOffset) {
- LOG_ERROR.warn(
- "[BUG]logic queue order maybe wrong,
expectLogicOffset: {} currentLogicOffset: {} Topic: {} QID: {} Diff: {}",
- expectLogicOffset,
- currentLogicOffset,
- this.topic,
- this.queueId,
- expectLogicOffset - currentLogicOffset
- );
+ if (expectLogicOffset != currentLogicOffset) {
+ LOG_ERROR.warn(
+ "[BUG]logic queue order maybe wrong,
expectLogicOffset: {} currentLogicOffset: {} Topic: {} QID: {} Diff: {}",
+ expectLogicOffset,
+ currentLogicOffset,
+ this.topic,
+ this.queueId,
+ expectLogicOffset - currentLogicOffset
+ );
+ }
}
+ this.setMaxPhysicOffset(offset + size);
+ boolean appendResult;
+ if
(messageStore.getMessageStoreConfig().isPutConsumeQueueDataByFileChannel()) {
+ appendResult =
mappedFile.appendMessageUsingFileChannel(this.byteBufferIndex.array());
+ } else {
+ appendResult =
mappedFile.appendMessage(this.byteBufferIndex.array());
+ }
+ return appendResult;
+ } finally {
+ mappedFile.release();
}
- this.setMaxPhysicOffset(offset + size);
- boolean appendResult;
- if
(messageStore.getMessageStoreConfig().isPutConsumeQueueDataByFileChannel()) {
- appendResult =
mappedFile.appendMessageUsingFileChannel(this.byteBufferIndex.array());
- } else {
- appendResult =
mappedFile.appendMessage(this.byteBufferIndex.array());
- }
- return appendResult;
}
return false;
}
@@ -1271,7 +1279,19 @@ public class ConsumeQueue implements
ConsumeQueueInterface {
// transientStorePool is null, only need set wrote position here
MappedFile mappedFile = mappedFileQueue.getLastMappedFile(offset *
ConsumeQueue.CQ_STORE_UNIT_SIZE, true);
- fillPreBlank(mappedFile, offset * ConsumeQueue.CQ_STORE_UNIT_SIZE);
+ if (mappedFile == null) {
+ log.error("initializeWithOffset failed: mappedFile is null for
offset {}", offset);
+ return;
+ }
+ if (!mappedFile.hold()) {
+ log.error("initializeWithOffset failed: mappedFile hold() failed
for offset {}", offset);
+ return;
+ }
+ try {
+ fillPreBlank(mappedFile, offset * ConsumeQueue.CQ_STORE_UNIT_SIZE);
+ } finally {
+ mappedFile.release();
+ }
flush(0);
}