This is an automated email from the ASF dual-hosted git repository.
lizhimins pushed a commit to branch rocketmq-studio
in repository https://gitbox.apache.org/repos/asf/rocketmq-dashboard.git
The following commit(s) were added to refs/heads/rocketmq-studio by this push:
new 084419157 fix(diagnostics): bound and isolate jstack parsing (#2415)
084419157 is described below
commit 084419157cdae931b47a5688bafa7a352736c351
Author: yyqdbngt <[email protected]>
AuthorDate: Sat Aug 22 14:51:16 2026 +0800
fix(diagnostics): bound and isolate jstack parsing (#2415)
Co-authored-by: Yue Wang <[email protected]>
---
.../RocketMQConsumerDiagnosticsProvider.java | 18 ++++++++++---
.../RocketMQConsumerDiagnosticsProviderTest.java | 31 ++++++++++++++++++++++
2 files changed, 45 insertions(+), 4 deletions(-)
diff --git
a/server/src/main/java/org/apache/rocketmq/studio/provider/apache/RocketMQConsumerDiagnosticsProvider.java
b/server/src/main/java/org/apache/rocketmq/studio/provider/apache/RocketMQConsumerDiagnosticsProvider.java
index 19f6981d8..bd7a83d0b 100644
---
a/server/src/main/java/org/apache/rocketmq/studio/provider/apache/RocketMQConsumerDiagnosticsProvider.java
+++
b/server/src/main/java/org/apache/rocketmq/studio/provider/apache/RocketMQConsumerDiagnosticsProvider.java
@@ -50,6 +50,7 @@ public class RocketMQConsumerDiagnosticsProvider implements
ConsumerDiagnosticsP
private static final Pattern THREAD_HEADER =
Pattern.compile("^(?<name>.+?)TID:\\s+(?<id>\\d+)\\s+STATE:\\s+(?<state>\\S+)\\s*$");
+ static final int MAX_JSTACK_CHARS = 2 * 1024 * 1024;
private final RuntimeAdminClientResolver runtimeAdminClientResolver;
private final MqAdminExtFactory adminFactory;
@@ -139,6 +140,10 @@ public class RocketMQConsumerDiagnosticsProvider
implements ConsumerDiagnosticsP
if (!StringUtils.hasText(jstack)) {
return List.of();
}
+ if (jstack.length() > MAX_JSTACK_CHARS) {
+ throw new BusinessException(502,
+ "Consumer stack exceeds the supported size of " +
MAX_JSTACK_CHARS + " characters");
+ }
List<ConsumerThreadStackVO> threads = new ArrayList<>();
ThreadBuilder current = null;
@@ -150,11 +155,16 @@ public class RocketMQConsumerDiagnosticsProvider
implements ConsumerDiagnosticsP
if (header.matches()) {
if (current != null) {
threads.add(current.build());
+ current = null;
+ }
+ try {
+ current = new ThreadBuilder(
+ header.group("name").trim(),
+ Long.parseLong(header.group("id")),
+ header.group("state").trim());
+ } catch (NumberFormatException malformedThreadId) {
+ log.debug("Ignoring consumer stack row with an invalid
thread id");
}
- current = new ThreadBuilder(
- header.group("name").trim(),
- Long.parseLong(header.group("id")),
- header.group("state").trim());
continue;
}
if (current != null) {
diff --git
a/server/src/test/java/org/apache/rocketmq/studio/provider/apache/RocketMQConsumerDiagnosticsProviderTest.java
b/server/src/test/java/org/apache/rocketmq/studio/provider/apache/RocketMQConsumerDiagnosticsProviderTest.java
index e4f78bbeb..c749c806a 100644
---
a/server/src/test/java/org/apache/rocketmq/studio/provider/apache/RocketMQConsumerDiagnosticsProviderTest.java
+++
b/server/src/test/java/org/apache/rocketmq/studio/provider/apache/RocketMQConsumerDiagnosticsProviderTest.java
@@ -117,6 +117,37 @@ class RocketMQConsumerDiagnosticsProviderTest {
.containsExactly("com.example.Listener.consume(Listener.java:10)");
}
+ @Test
+ void getConsumerStackShouldIgnoreOverflowingThreadIdsTest() throws
Exception {
+ ConsumerRunningInfo runningInfo = new ConsumerRunningInfo();
+ runningInfo.setJstack("""
+ first TID: 12 STATE: RUNNABLE
+ first com.example.First.run(First.java:1)
+ malformed TID: 999999999999999999999999999999 STATE: WAITING
+ malformed ignored.frame(Line.java:2)
+ second TID: 13 STATE: WAITING
+ second com.example.Second.run(Second.java:3)
+ """);
+ when(adminExt.getConsumerRunningInfo("cg-orders", "client-1",
true)).thenReturn(runningInfo);
+
+ ConsumerStackTraceVO result = provider.getConsumerStack("instance-a",
"cg-orders", "client-1");
+
+ assertThat(result.getThreads()).extracting(thread ->
thread.getThreadName())
+ .containsExactly("first", "second");
+ }
+
+ @Test
+ void getConsumerStackShouldRejectOversizedJstackTest() throws Exception {
+ ConsumerRunningInfo runningInfo = new ConsumerRunningInfo();
+
runningInfo.setJstack("x".repeat(RocketMQConsumerDiagnosticsProvider.MAX_JSTACK_CHARS
+ 1));
+ when(adminExt.getConsumerRunningInfo("cg-orders", "client-1",
true)).thenReturn(runningInfo);
+
+ assertThatThrownBy(() -> provider.getConsumerStack("instance-a",
"cg-orders", "client-1"))
+ .isInstanceOfSatisfying(BusinessException.class,
+ error -> assertThat(error.getCode()).isEqualTo(502))
+ .hasMessageContaining("exceeds the supported size");
+ }
+
@Test
void getConsumerStackShouldUseDefaultNameServerWhenInstanceIsBlank()
throws Exception {
ConsumerRunningInfo runningInfo = new ConsumerRunningInfo();