Skip to content

Commit ab51afc

Browse files
committed
fix(crashtracking): Redaction on macOS aarch64 register-to-memory mapping
1 parent b8ae4df commit ab51afc

4 files changed

Lines changed: 89 additions & 7 deletions

File tree

dd-java-agent/agent-crashtracking/src/main/java/datadog/crashtracking/parsers/HotspotCrashLogParser.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -650,7 +650,7 @@ private static State nextThreadSectionState(String line, boolean previousLineBla
650650
if (line.contains("P R O C E S S")) {
651651
return State.PROCESS;
652652
}
653-
if (previousLineBlank && !line.contains("=") && SUBSECTION_TITLE.matcher(line).matches()) {
653+
if (previousLineBlank && SUBSECTION_TITLE.matcher(line).matches()) {
654654
return State.STACKTRACE;
655655
}
656656
return null;

dd-java-agent/agent-crashtracking/src/main/java/datadog/crashtracking/parsers/RedactUtils.java

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -38,9 +38,11 @@ public final class RedactUtils {
3838
// 'in 'class'' clause in {method} descriptor entries
3939
private static final Pattern METHOD_IN_CLASS = Pattern.compile("( in ')([^']+)(')");
4040

41-
// Library path after <offset 0x...> in /path/to/lib.so
41+
// Library path in two formats produced by os::print_location():
42+
// <offset 0x...> in /path/to/lib.so at 0x... (no dladdr symbol)
43+
// symbol+offset in /path/to/lib.so at 0x... (dladdr resolved a symbol name)
4244
private static final Pattern LIBRARY_PATH =
43-
Pattern.compile("(<offset 0x[0-9a-fA-F]+> in )(/\\S+)");
45+
Pattern.compile("((?:<[^>]+>|\\S+\\+\\S+)\\s+in\\s+)(/\\S+)");
4446

4547
// Dotted class name followed by an OOP reference: "com.company.Type"{0x...}
4648
// This specifically identifies the inline string value of a java.lang.Class 'name' field
@@ -115,9 +117,9 @@ static String redactMethodClass(String line) {
115117
}
116118

117119
/**
118-
* Redacts all but the parent directory and filename from a library path in the line: <code>
119-
* &lt;offset 0x...&gt; in /path/to/dir/lib.so</code> to <code>
120-
* &lt;offset 0x...&gt; in /redacted/redacted/dir/lib.so</code>
120+
* Redacts all but the parent directory and filename from a library path. Handles both
121+
* <code>&lt;offset 0x...&gt; in /path/to/dir/lib.so</code> and <code>symbol+0 in
122+
* /path/to/dir/lib.so</code> to <code>... in /redacted/redacted/dir/lib.so</code>
121123
*/
122124
static String redactLibraryPath(String line) {
123125
return replaceAll(LIBRARY_PATH, line, m -> m.group(1) + redactPath(m.group(2)));

dd-java-agent/agent-crashtracking/src/test/java/datadog/crashtracking/parsers/HotspotCrashLogParserTest.java

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
import java.io.InputStream;
1414
import java.io.InputStreamReader;
1515
import java.nio.charset.StandardCharsets;
16+
import java.util.Map;
1617
import java.util.Objects;
1718
import java.util.UUID;
1819
import java.util.stream.Collectors;
@@ -68,6 +69,12 @@ public void testRegisterParsingMacosAarch64() throws Exception {
6869
assertEquals("0x000000010f8ac794", crashLog.experimental.ucontext.get("pc"));
6970
assertEquals("0x0000000060001000", crashLog.experimental.ucontext.get("cpsr"));
7071

72+
// "Top of Stack: (sp=0x...)" contains "=" — verify the parser stops at it and doesn't
73+
// absorb its hex-dump content into the last register mapping entry (sp).
74+
assertThat(crashLog.experimental.registerToMemoryMapping)
75+
.extractingByKey("sp", STRING)
76+
.doesNotContain("Top of Stack:");
77+
7178
assertNotNull(crashLog.experimental.runtimeArgs);
7279
assertTrue(crashLog.experimental.runtimeArgs.contains("--enable-native-access=ALL-UNNAMED"));
7380
assertTrue(crashLog.experimental.runtimeArgs.contains("--add-modules=ALL-DEFAULT"));
@@ -76,6 +83,50 @@ public void testRegisterParsingMacosAarch64() throws Exception {
7683
.anyMatch(arg -> arg.contains("SourceLauncher") || arg.endsWith("CrashTest.java")));
7784
}
7885

86+
/**
87+
* Verifies the register-to-memory mapping section for the macOS aarch64 sample:
88+
* representative values, library path redaction, and that "Top of Stack:" / "Instructions:"
89+
* subsections are not absorbed into register values.
90+
*/
91+
@Test
92+
public void testRegisterToMemoryMappingMacosAarch64() throws Exception {
93+
CrashLog crashLog =
94+
new HotspotCrashLogParser()
95+
.parse(
96+
UUID.randomUUID().toString(), readFileAsString("sample-crash-macos-aarch64.txt"));
97+
98+
Map<String, String> mapping = crashLog.experimental.registerToMemoryMapping;
99+
100+
// Representative single-line entries
101+
assertThat(mapping)
102+
.containsEntry("x0", "0x0000000000000c55 is an unknown value")
103+
.containsEntry("x2", "0x0 is null")
104+
.containsEntry("x28", "0x0000000100a153f0 is a thread");
105+
106+
// Library path (symbol+offset format) — path must be redacted, keeping only last 2 segments
107+
// /usr/lib/system/libsystem_pthread.dylib → 2 redacted ("usr","lib") + "system/lib..."
108+
assertThat(mapping)
109+
.extractingByKey("x16", STRING)
110+
.isEqualTo(
111+
"0x0000000182d709d0: pthread_jit_write_protect_np+0 in /redacted/redacted/system/libsystem_pthread.dylib at 0x0000000182d69000");
112+
// /Users/USER/.local/share/mise/installs/java/25.0.2/lib/server/libjvm.dylib → 9 redacted + "server/libjvm.dylib"
113+
assertThat(mapping)
114+
.extractingByKey("x21", STRING)
115+
.isEqualTo(
116+
"0x0000000106c1ccc0: _ZN19TemplateInterpreter13_active_tableE+0 in /redacted/redacted/redacted/redacted/redacted/redacted/redacted/redacted/redacted/server/libjvm.dylib at 0x0000000105efc000");
117+
118+
// "Top of Stack: (sp=0x...)" and "Instructions: (pc=0x...)" must not leak into register values
119+
assertThat(mapping).doesNotContainKey("Top of Stack");
120+
assertThat(mapping)
121+
.allSatisfy(
122+
(k, v) -> assertThat(v).doesNotContain("Top of Stack:", "Instructions:"));
123+
124+
// sp is the last register before "Top of Stack:" — its value must be clean
125+
assertThat(mapping)
126+
.extractingByKey("sp", STRING)
127+
.isEqualTo("0x000000016feee0f0 is pointing into the stack for thread: 0x0000000100a153f0");
128+
}
129+
79130
/** Linux aarch64 uses uppercase register names: R0-R30 */
80131
@Test
81132
public void testRegisterParsingLinuxAarch64() throws Exception {

dd-java-agent/agent-crashtracking/src/test/java/datadog/crashtracking/parsers/RedactUtilsTest.java

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,14 +124,43 @@ void testRedactMethodClass_keepsKnownPackage() {
124124
}
125125

126126
@Test
127-
void testRedactLibraryPath_redactsIntermediateSegments() {
127+
void testRedactLibraryPath_offsetFormat() {
128128
assertThat(
129129
RedactUtils.redactLibraryPath(
130130
"0x0000ffff9efa1650: <offset 0x0000000000e01650> in /opt/company/lib/server/app.so at 0x0000ffff9e1a0000"))
131131
.isEqualTo(
132132
"0x0000ffff9efa1650: <offset 0x0000000000e01650> in /redacted/redacted/redacted/server/app.so at 0x0000ffff9e1a0000");
133133
}
134134

135+
@Test
136+
void testRedactLibraryPath_symbolOffsetFormat() {
137+
// macOS/Linux: dladdr resolved a C++ mangled symbol — "symbol+offset in /path at 0x..."
138+
assertThat(
139+
RedactUtils.redactLibraryPath(
140+
"0x0000000106c1ccc0: _ZN19TemplateInterpreter13_active_tableE+0 in /Users/USER/.local/share/mise/installs/java/25.0.2/lib/server/libjvm.dylib at 0x0000000105efc000"))
141+
.isEqualTo(
142+
"0x0000000106c1ccc0: _ZN19TemplateInterpreter13_active_tableE+0 in /redacted/redacted/redacted/redacted/redacted/redacted/redacted/redacted/redacted/server/libjvm.dylib at 0x0000000105efc000");
143+
}
144+
145+
@Test
146+
void testRedactLibraryPath_cSymbolFormat() {
147+
// macOS: C symbol "symbol+0 in /usr/lib/system/lib.dylib at 0x..."
148+
assertThat(
149+
RedactUtils.redactLibraryPath(
150+
"0x0000000182d709d0: pthread_jit_write_protect_np+0 in /usr/lib/system/libsystem_pthread.dylib at 0x0000000182d69000"))
151+
.isEqualTo(
152+
"0x0000000182d709d0: pthread_jit_write_protect_np+0 in /redacted/redacted/system/libsystem_pthread.dylib at 0x0000000182d69000");
153+
}
154+
155+
@Test
156+
void testRedactLibraryPath_doesNotMatchInterpreterCodelet() {
157+
// "code_begin+1776 in an Interpreter codelet" — "an" doesn't start with "/" so no match
158+
assertThat(
159+
RedactUtils.redactLibraryPath(
160+
"0x0000000116d0c970 is at code_begin+1776 in an Interpreter codelet"))
161+
.isEqualTo("0x0000000116d0c970 is at code_begin+1776 in an Interpreter codelet");
162+
}
163+
135164
@Test
136165
void testRedactLibraryPath_leavesUnrelatedLinesUnchanged() {
137166
assertThat(RedactUtils.redactLibraryPath("0x00007f37a16e2590 is an unknown value"))

0 commit comments

Comments
 (0)