From d3ecdc951e142a6ffbb39eb7de050c02144e9b74 Mon Sep 17 00:00:00 2001 From: Hashim Khan <64767361+Hashim1999164@users.noreply.github.com> Date: Thu, 13 Aug 2026 04:28:03 +0500 Subject: [PATCH 1/2] Cache duration Pattern in GenerationalHeapParser Compile DURATION_GROUP_PATTERN once as a static final field instead of on every CMS remark weak-reference split parse call. Fixes #567. --- .../microsoft/gctoolkit/parser/GenerationalHeapParser.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/parser/src/main/java/com/microsoft/gctoolkit/parser/GenerationalHeapParser.java b/parser/src/main/java/com/microsoft/gctoolkit/parser/GenerationalHeapParser.java index 04edccc7a..f88f76472 100644 --- a/parser/src/main/java/com/microsoft/gctoolkit/parser/GenerationalHeapParser.java +++ b/parser/src/main/java/com/microsoft/gctoolkit/parser/GenerationalHeapParser.java @@ -64,6 +64,9 @@ public class GenerationalHeapParser extends PreUnifiedGCLogParser implements Sim private static final Logger LOGGER = Logger.getLogger(GenerationalHeapParser.class.getName()); + // Cached for the CMS remark/weak-reference split-bug path; avoid recompiling on every parse call. + private static final Pattern DURATION_GROUP_PATTERN = Pattern.compile(".* " + PAUSE_TIME); + private ParNew parNewForwardReference; private GarbageCollectionTypes garbageCollectionTypeForwardReference; private GCCause gcCauseForwardReference; @@ -1917,8 +1920,7 @@ public void splitRemarkReference(GCLogTrace trace, String line) { */ public void splitRemarkReferenceWithWeakReferenceSplitBug(GCLogTrace trace, String line) { GCLogTrace remarkTrace = REMARK_CLAUSE.parse(line); - Pattern durationGroupPattern = Pattern.compile(".* " + PAUSE_TIME); - Matcher matcher = durationGroupPattern.matcher(line); + Matcher matcher = DURATION_GROUP_PATTERN.matcher(line); double duration = 0.0d; if (matcher.find()) { duration = Double.parseDouble(matcher.group(matcher.groupCount())); From 746dc235743c6474303947fb0be42487f44e03e9 Mon Sep 17 00:00:00 2001 From: Hashim1999164 <64767361+Hashim1999164@users.noreply.github.com> Date: Thu, 13 Aug 2026 04:45:00 +0500 Subject: [PATCH 2/2] Tighten pause duration regex to avoid ReDoS Replace greedy .* with a line bounded prefix so matcher.find still selects the last pause time. Guard null parse results that SonarCloud flags as reliability bugs. --- .../parser/GenerationalHeapParser.java | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/parser/src/main/java/com/microsoft/gctoolkit/parser/GenerationalHeapParser.java b/parser/src/main/java/com/microsoft/gctoolkit/parser/GenerationalHeapParser.java index f88f76472..a76307ea0 100644 --- a/parser/src/main/java/com/microsoft/gctoolkit/parser/GenerationalHeapParser.java +++ b/parser/src/main/java/com/microsoft/gctoolkit/parser/GenerationalHeapParser.java @@ -65,7 +65,8 @@ public class GenerationalHeapParser extends PreUnifiedGCLogParser implements Sim private static final Logger LOGGER = Logger.getLogger(GenerationalHeapParser.class.getName()); // Cached for the CMS remark/weak-reference split-bug path; avoid recompiling on every parse call. - private static final Pattern DURATION_GROUP_PATTERN = Pattern.compile(".* " + PAUSE_TIME); + // [^\n]* preserves last-match semantics of greedy .* without a ReDoS hotspot. + private static final Pattern DURATION_GROUP_PATTERN = Pattern.compile("[^\\n]* " + PAUSE_TIME); private ParNew parNewForwardReference; private GarbageCollectionTypes garbageCollectionTypeForwardReference; @@ -629,9 +630,11 @@ public void parNewPromotionFailedDetails(GCLogTrace trace, String line) { gcCauseForwardReference = GCCause.PROMOTION_FAILED; ArrayList blocks = new ArrayList<>(); GCLogTrace block = PARNEW_PROMOTION_FAILURE_SIZE_BLOCK.parse(line); - do { - blocks.add(block.getIntegerGroup(1)); - } while (block.hasNext()); + if (block != null) { + do { + blocks.add(block.getIntegerGroup(1)); + } while (block.hasNext()); + } promotionFailureSizesForwardReference = new int[blocks.size()]; for (int index = 0; index < blocks.size(); index++) promotionFailureSizesForwardReference[index] = blocks.get(index); @@ -1820,7 +1823,8 @@ public void concurrentPhaseYields(GCLogTrace trace, String line) { private void precleanTimedoutWithCards(GCLogTrace trace, String line) { abortPrecleanDueToTime = true; GCLogTrace concurrentPhase = new GCParseRule("X",CMS_PHASE_END).parse(line); - endOfConcurrentPhase(concurrentPhase, concurrentPhase.getDateTimeStamp(), 0); + if (concurrentPhase != null) + endOfConcurrentPhase(concurrentPhase, concurrentPhase.getDateTimeStamp(), 0); } private void shouldCollectConcurrent(GCLogTrace trace, String line) { @@ -2069,6 +2073,10 @@ private void endOfConcurrentPhase(GCLogTrace trace, String line) { private void endConcurrentPrecleanWithReferenceProcessing(GCLogTrace trace, String line) { GCLogTrace concurrentBlock = CONCURRENT_PHASE_END_BLOCK.parse(line); + if (concurrentBlock == null) { + LOGGER.warning("Unable to extract data from " + trace.toString()); + return; + } try { publish(new ConcurrentPreClean(startOfConcurrentPhase, concurrentBlock.getDoubleGroup(11), concurrentBlock.getDoubleGroup(7), concurrentBlock.getDoubleGroup(8))); } catch (Throwable t) {