From dae759e13499d89048596aad3908965f2c1a8c0d Mon Sep 17 00:00:00 2001 From: tstuefe Date: Tue, 23 Sep 2025 08:58:10 +0200 Subject: [PATCH 01/11] start --- src/hotspot/share/logging/logTag.hpp | 1 + src/hotspot/share/runtime/threads.cpp | 5 ++ src/hotspot/share/sanitizers/address.cpp | 66 ++++++++++++++++++++++++ src/hotspot/share/sanitizers/address.hpp | 7 +++ src/hotspot/share/utilities/vmError.cpp | 15 +++++- 5 files changed, 93 insertions(+), 1 deletion(-) create mode 100644 src/hotspot/share/sanitizers/address.cpp diff --git a/src/hotspot/share/logging/logTag.hpp b/src/hotspot/share/logging/logTag.hpp index 6d0bd117ad91d..3ad6a197d07d3 100644 --- a/src/hotspot/share/logging/logTag.hpp +++ b/src/hotspot/share/logging/logTag.hpp @@ -41,6 +41,7 @@ class outputStream; LOG_TAG(aot) \ LOG_TAG(arguments) \ LOG_TAG(array) \ + LOG_TAG(asan) \ LOG_TAG(attach) \ LOG_TAG(barrier) \ LOG_TAG(blocks) \ diff --git a/src/hotspot/share/runtime/threads.cpp b/src/hotspot/share/runtime/threads.cpp index 65eea9d5fb23b..370a419183671 100644 --- a/src/hotspot/share/runtime/threads.cpp +++ b/src/hotspot/share/runtime/threads.cpp @@ -98,6 +98,7 @@ #include "runtime/trimNativeHeap.hpp" #include "runtime/vm_version.hpp" #include "runtime/vmOperations.hpp" +#include "sanitizers/address.hpp" #include "services/attachListener.hpp" #include "services/management.hpp" #include "services/threadIdTable.hpp" @@ -695,6 +696,10 @@ jint Threads::create_vm(JavaVMInitArgs* args, bool* canTryAgain) { // No more stub generation allowed after that point. StubCodeDesc::freeze(); +#ifdef ADDRESS_SANITIZER + Asan::initialize(); +#endif + // Set flag that basic initialization has completed. Used by exceptions and various // debug stuff, that does not work until all basic classes have been initialized. set_init_completed(); diff --git a/src/hotspot/share/sanitizers/address.cpp b/src/hotspot/share/sanitizers/address.cpp new file mode 100644 index 0000000000000..b68b6615428e2 --- /dev/null +++ b/src/hotspot/share/sanitizers/address.cpp @@ -0,0 +1,66 @@ +/* + * Copyright (c) 1998, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#ifdef ADDRESS_SANITIZER + +#include "logging/log.hpp" +#include "sanitizers/address.hpp" +#include "utilities/globalDefinitions.hpp" +#include "utilities/vmError.hpp" +#include +#include + +static const char* g_asan_report = nullptr; +typedef void (*callback_setter_t) (void (*callback)(const char *)); +static callback_setter_t callback_setter = nullptr; + +extern "C" void asan_error_callback(const char* report_text) { + // Keep things very short and simple here; + // Do use as little as possible of any hotspot infrastructure. + // We will print out the report text on stderr; then, we will + // end the JVM with a fatal error, resulting in hs-err file + // and core dump. VMError will also print the error report + // to the hs-err file. + g_asan_report = report_text; + fprintf(stderr, "JVM caught ASAN Error\n"); + fprintf(stderr, "%s\n", report_text); + fatal("ASAN error caught"); +} + +const char* Asan::report() { + return g_asan_report; +} + +void Asan::initialize() { + callback_setter = (callback_setter_t) dlsym(RTLD_DEFAULT, "__asan_set_error_report_callback"); + if (callback_setter) { + callback_setter(asan_error_callback); + log_info(asan)("JVM callback for ASAN errors successfully installed"); + } else { + log_info(asan)("*** Failed to install JVM callback for ASAN. ASAN errors will not generate hs-err files. ***"); + } + //__asan_set_error_report_callback(asan_error_callback); +} + +#endif // ADDRESS_SANITIZER diff --git a/src/hotspot/share/sanitizers/address.hpp b/src/hotspot/share/sanitizers/address.hpp index 5186053f1c9e4..af5f7151ca869 100644 --- a/src/hotspot/share/sanitizers/address.hpp +++ b/src/hotspot/share/sanitizers/address.hpp @@ -26,6 +26,7 @@ #define SHARE_SANITIZERS_ADDRESS_HPP #ifdef ADDRESS_SANITIZER +#include "memory/allStatic.hpp" #include #endif @@ -74,4 +75,10 @@ } while (false) #endif +struct Asan : public AllStatic { + static void initialize(); + // Returns the ASAN report text; nullptr if no ASAN error happened. + static const char* report(); +}; + #endif // SHARE_SANITIZERS_ADDRESS_HPP diff --git a/src/hotspot/share/utilities/vmError.cpp b/src/hotspot/share/utilities/vmError.cpp index 0fbd8ed4259ac..367ce3b1f3fbb 100644 --- a/src/hotspot/share/utilities/vmError.cpp +++ b/src/hotspot/share/utilities/vmError.cpp @@ -60,6 +60,7 @@ #include "runtime/vm_version.hpp" #include "runtime/vmOperations.hpp" #include "runtime/vmThread.hpp" +#include "sanitizers/address.hpp" #include "sanitizers/ub.hpp" #include "utilities/debug.hpp" #include "utilities/decoder.hpp" @@ -910,7 +911,19 @@ void VMError::report(outputStream* st, bool _verbose) { STEP_IF("printing date and time", _verbose) os::print_date_and_time(st, buf, sizeof(buf)); - STEP_IF("printing thread", _verbose) +#ifdef ADDRESS_SANITIZER + STEP_IF("printing ASAN error information", _verbose && Asan::report() != nullptr) + st->cr(); + st->print_cr("------------------ A S A N ----------------"); + st->cr(); + st->print_cr("ASAN error information:"); + // Note: Use print_raw, not print or print_cr, to avoid truncation + // (report can be longer than 2K) + st->print_raw(Asan::report()); + st->cr(); +#endif // ADDRESS_SANITIZER + + STEP_IF("printing thread", _verbose) st->cr(); st->print_cr("--------------- T H R E A D ---------------"); st->cr(); From fd0ddea0fa8119f90ef375ba060c519ec536c82e Mon Sep 17 00:00:00 2001 From: tstuefe Date: Tue, 23 Sep 2025 09:25:43 +0200 Subject: [PATCH 02/11] fix error in non-asan build --- src/hotspot/share/sanitizers/address.hpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/hotspot/share/sanitizers/address.hpp b/src/hotspot/share/sanitizers/address.hpp index af5f7151ca869..e8c3165cd0ff4 100644 --- a/src/hotspot/share/sanitizers/address.hpp +++ b/src/hotspot/share/sanitizers/address.hpp @@ -75,10 +75,12 @@ } while (false) #endif +#ifdef ADDRESS_SANITIZER struct Asan : public AllStatic { static void initialize(); // Returns the ASAN report text; nullptr if no ASAN error happened. static const char* report(); }; +#endif #endif // SHARE_SANITIZERS_ADDRESS_HPP From bc28f6fa1b352795a8b715923782d4cdd71f005d Mon Sep 17 00:00:00 2001 From: tstuefe Date: Tue, 23 Sep 2025 10:49:09 +0200 Subject: [PATCH 03/11] add jtreg test --- src/hotspot/share/runtime/globals.hpp | 2 +- src/hotspot/share/utilities/vmError.cpp | 10 ++- .../runtime/ErrorHandling/AsanReportTest.java | 85 +++++++++++++++++++ 3 files changed, 94 insertions(+), 3 deletions(-) create mode 100644 test/hotspot/jtreg/runtime/ErrorHandling/AsanReportTest.java diff --git a/src/hotspot/share/runtime/globals.hpp b/src/hotspot/share/runtime/globals.hpp index dac01d018bf3f..24705c449cbee 100644 --- a/src/hotspot/share/runtime/globals.hpp +++ b/src/hotspot/share/runtime/globals.hpp @@ -502,7 +502,7 @@ const int ObjectAlignmentInBytes = 8; "If > 0, provokes an error after VM initialization; the value " \ "determines which error to provoke. See controlled_crash() " \ "in vmError.cpp.") \ - range(0, 17) \ + range(0, 18) \ \ develop(uint, TestCrashInErrorHandler, 0, \ "If > 0, provokes an error inside VM error handler (a secondary " \ diff --git a/src/hotspot/share/utilities/vmError.cpp b/src/hotspot/share/utilities/vmError.cpp index 367ce3b1f3fbb..0d5b5154999e8 100644 --- a/src/hotspot/share/utilities/vmError.cpp +++ b/src/hotspot/share/utilities/vmError.cpp @@ -916,7 +916,6 @@ void VMError::report(outputStream* st, bool _verbose) { st->cr(); st->print_cr("------------------ A S A N ----------------"); st->cr(); - st->print_cr("ASAN error information:"); // Note: Use print_raw, not print or print_cr, to avoid truncation // (report can be longer than 2K) st->print_raw(Asan::report()); @@ -964,7 +963,6 @@ void VMError::report(outputStream* st, bool _verbose) { address stack_bottom = stack_top - stack_size; st->print("[" PTR_FORMAT "," PTR_FORMAT "]", p2i(stack_bottom), p2i(stack_top)); - frame fr = _context ? os::fetch_frame_from_context(_context) : os::current_frame(); @@ -2199,6 +2197,14 @@ void VMError::controlled_crash(int how) { fatal("Force crash with a nested ThreadsListHandle."); } } + case 18: { + // Trigger an error that should cause ASAN to report a double free or use-after-free. + // Please note that this is not 100% bullet-proof since it assumes that this block + // is not immediately repurposed by some other thread after free. + void* const p = os::malloc(4096, mtTest); + os::free(p); + os::free(p); + } default: // If another number is given, give a generic crash. fatal("Crashing with number %d", how); diff --git a/test/hotspot/jtreg/runtime/ErrorHandling/AsanReportTest.java b/test/hotspot/jtreg/runtime/ErrorHandling/AsanReportTest.java new file mode 100644 index 0000000000000..3638f670663d6 --- /dev/null +++ b/test/hotspot/jtreg/runtime/ErrorHandling/AsanReportTest.java @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2025, IBM Corporation. All rights reserved. + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @summary Test that we get ASAN-reports and hs-err files on ASAN error + * @library /test/lib + * @requires vm.asan + * @requires vm.flagless + * @requires vm.debug == true & os.family == "linux" + * @modules java.base/jdk.internal.misc + * java.management + * @run driver AsanReportTest + */ + +// Note: this test can only run on debug since it relies on VMError::controlled_crash() which +// only exists in debug builds. +import java.io.File; +import java.util.Arrays; +import java.util.regex.Pattern; + +import jdk.test.lib.process.OutputAnalyzer; +import jdk.test.lib.Platform; +import jdk.test.lib.process.ProcessTools; + +public class AsanReportTest { + + private static void do_test() throws Exception { + + ProcessBuilder pb = ProcessTools.createLimitedTestJavaProcessBuilder( + "-Xmx100M", + "-XX:-CreateCoredumpOnCrash", + // Switch off NMT since it can alter the error ASAN sees; we want the pure double free error + "-XX:NativeMemoryTracking=off", + // Causes double-free in controlled_crash + "-XX:ErrorHandlerTest=18", + "-version"); + + OutputAnalyzer output = new OutputAnalyzer(pb.start()); + + output.shouldNotHaveExitValue(0); + + // ASAN error should appear on stderr + output.shouldContain("JVM caught ASAN Error"); + output.shouldContain("# A fatal error has been detected by the Java Runtime Environment"); + output.shouldMatch(".*AddressSanitizer.*double-free.*"); + output.shouldContain("# fatal error: ASAN error"); + + File hs_err_file = HsErrFileUtils.openHsErrFileFromOutput(output); + Pattern[] pat = new Pattern[] { + Pattern.compile(".*A S A N.*"), + Pattern.compile(".*AddressSanitizer.*double-free.*"), + Pattern.compile(".*(crash_with_segfault|controlled_crash).*") + }; + HsErrFileUtils.checkHsErrFileContent(hs_err_file, pat, false); + + } + + public static void main(String[] args) throws Exception { + do_test(); + } + +} + From e90a25a9633956444f19078f0981da2fe3efa388 Mon Sep 17 00:00:00 2001 From: tstuefe Date: Tue, 23 Sep 2025 10:54:51 +0200 Subject: [PATCH 04/11] stupid sort includes test --- src/hotspot/share/sanitizers/address.cpp | 2 ++ src/hotspot/share/sanitizers/address.hpp | 1 + 2 files changed, 3 insertions(+) diff --git a/src/hotspot/share/sanitizers/address.cpp b/src/hotspot/share/sanitizers/address.cpp index b68b6615428e2..086d1559ca9de 100644 --- a/src/hotspot/share/sanitizers/address.cpp +++ b/src/hotspot/share/sanitizers/address.cpp @@ -28,9 +28,11 @@ #include "sanitizers/address.hpp" #include "utilities/globalDefinitions.hpp" #include "utilities/vmError.hpp" + #include #include + static const char* g_asan_report = nullptr; typedef void (*callback_setter_t) (void (*callback)(const char *)); static callback_setter_t callback_setter = nullptr; diff --git a/src/hotspot/share/sanitizers/address.hpp b/src/hotspot/share/sanitizers/address.hpp index e8c3165cd0ff4..5d20974399a31 100644 --- a/src/hotspot/share/sanitizers/address.hpp +++ b/src/hotspot/share/sanitizers/address.hpp @@ -27,6 +27,7 @@ #ifdef ADDRESS_SANITIZER #include "memory/allStatic.hpp" + #include #endif From 1b4476b3078a77e4bf32b570a2aa8b2fe387cc4d Mon Sep 17 00:00:00 2001 From: tstuefe Date: Tue, 23 Sep 2025 10:57:24 +0200 Subject: [PATCH 05/11] remove stray modification --- src/hotspot/share/utilities/vmError.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/hotspot/share/utilities/vmError.cpp b/src/hotspot/share/utilities/vmError.cpp index 0d5b5154999e8..0a7352d2af7f9 100644 --- a/src/hotspot/share/utilities/vmError.cpp +++ b/src/hotspot/share/utilities/vmError.cpp @@ -963,6 +963,7 @@ void VMError::report(outputStream* st, bool _verbose) { address stack_bottom = stack_top - stack_size; st->print("[" PTR_FORMAT "," PTR_FORMAT "]", p2i(stack_bottom), p2i(stack_top)); + frame fr = _context ? os::fetch_frame_from_context(_context) : os::current_frame(); From 69d5cf5d096594b5d8448a75a54782cbd1c5fc6a Mon Sep 17 00:00:00 2001 From: Thomas Stuefe Date: Tue, 23 Sep 2025 12:36:49 +0200 Subject: [PATCH 06/11] Update address.cpp --- src/hotspot/share/sanitizers/address.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/hotspot/share/sanitizers/address.cpp b/src/hotspot/share/sanitizers/address.cpp index 086d1559ca9de..6addccf982fda 100644 --- a/src/hotspot/share/sanitizers/address.cpp +++ b/src/hotspot/share/sanitizers/address.cpp @@ -62,7 +62,6 @@ void Asan::initialize() { } else { log_info(asan)("*** Failed to install JVM callback for ASAN. ASAN errors will not generate hs-err files. ***"); } - //__asan_set_error_report_callback(asan_error_callback); } #endif // ADDRESS_SANITIZER From f1f5617ff70796ee62246731dd3098e69cd53503 Mon Sep 17 00:00:00 2001 From: tstuefe Date: Wed, 24 Sep 2025 15:47:49 +0200 Subject: [PATCH 07/11] rethink core file behavior --- src/hotspot/share/sanitizers/address.cpp | 90 ++++++++++++++----- src/hotspot/share/sanitizers/address.hpp | 6 +- src/hotspot/share/utilities/vmError.cpp | 6 +- .../runtime/ErrorHandling/AsanReportTest.java | 18 ++-- 4 files changed, 86 insertions(+), 34 deletions(-) diff --git a/src/hotspot/share/sanitizers/address.cpp b/src/hotspot/share/sanitizers/address.cpp index 6addccf982fda..263e58e886dc5 100644 --- a/src/hotspot/share/sanitizers/address.cpp +++ b/src/hotspot/share/sanitizers/address.cpp @@ -26,41 +26,91 @@ #include "logging/log.hpp" #include "sanitizers/address.hpp" +#include "runtime/globals_extension.hpp" #include "utilities/globalDefinitions.hpp" #include "utilities/vmError.hpp" #include #include - -static const char* g_asan_report = nullptr; typedef void (*callback_setter_t) (void (*callback)(const char *)); -static callback_setter_t callback_setter = nullptr; +static callback_setter_t g_callback_setter = nullptr; +static const char* g_report = nullptr; extern "C" void asan_error_callback(const char* report_text) { - // Keep things very short and simple here; - // Do use as little as possible of any hotspot infrastructure. - // We will print out the report text on stderr; then, we will - // end the JVM with a fatal error, resulting in hs-err file - // and core dump. VMError will also print the error report - // to the hs-err file. - g_asan_report = report_text; + // Please keep things very short and simple here and use as little + // as possible of any hotspot infrastructure. However shaky the JVM, + // we should always at least get the ASAN report on stderr. + + // Note: this is threadsafe since ASAN synchronizes error reports + g_report = report_text; + + // First, print off the bare error to stderr fprintf(stderr, "JVM caught ASAN Error\n"); fprintf(stderr, "%s\n", report_text); - fatal("ASAN error caught"); -} -const char* Asan::report() { - return g_asan_report; + // Then, let normal JVM error handling run its due course. + fatal("ASAN Error"); } void Asan::initialize() { - callback_setter = (callback_setter_t) dlsym(RTLD_DEFAULT, "__asan_set_error_report_callback"); - if (callback_setter) { - callback_setter(asan_error_callback); - log_info(asan)("JVM callback for ASAN errors successfully installed"); - } else { - log_info(asan)("*** Failed to install JVM callback for ASAN. ASAN errors will not generate hs-err files. ***"); + + g_callback_setter = (callback_setter_t) dlsym(RTLD_DEFAULT, "__asan_set_error_report_callback"); + if (g_callback_setter == nullptr) { + log_warning(asan)("*** Failed to install JVM callback for ASAN. ASAN errors will not generate hs-err files. ***"); + return; + } + + g_callback_setter(asan_error_callback); + log_info(asan)("JVM callback for ASAN errors successfully installed"); + + // Controlling core dump behavior: + // + // In hotspot, CreateCoredumpOnCrash decides whether to create a core dump (on Posix, whether to + // end the process with abort(3) or exit(3)). + // + // Core generation in the default ASAN reporter is controlled by two options: + // - "abort_on_error=0" (default) - end with exit(3), "abort_on_error=1" end with abort(3) + // - "disable_coredump=1" (default) disables cores by imposing a near-zero core soft limit. + // By default both options are set to prevent cores. That default makes sense since ASAN cores + // can get very large (due to the shadow map) and very numerous (ASAN is typically ran for + // large-scale integration tests, not targeted micro-tests). + // + // In hotspot ASAN builds, we replace the default ASAN reporter. The soft limit imposed by + // "disable_coredump=1" is still in effect. But "abort_on_error" is not honored. Since we'd + // like to exhibit exactly the same behavior as the standard ASAN error reporter, we honor + // "abort_on_error=1" by ending the JVM with exit(3) (we just switch off CreateCoredumpOnCrash). + // + // Thus: + // abort_on_error disable_coredump core file? + // 0 0 No (enforced by ergo-setting CreateCoredumpOnCrash=0) + // (*) 0 1 No (enforced by ASAN-imposed soft limit) + // 1 0 Yes, unless -XX:-CreateCoredumpOnCrash set on command line + // 1 1 No (enforced by ASAN-imposed soft limit) + // (*) is the default if no ASAN options are specified. + + const char* const asan_options = getenv("ASAN_OPTIONS"); + const bool abort_on_error = (asan_options != nullptr) && ::strstr(asan_options, "abort_on_error=1"); + if (!abort_on_error) { + if (CreateCoredumpOnCrash && FLAG_IS_CMDLINE(CreateCoredumpOnCrash)) { + log_warning(asan)("CreateCoredumpOnCrash overruled by%s abort_on_error, core generation will be disabled.", + asan_options != nullptr ? "" : " default setting for"); + log_warning(asan)("Use 'ASAN_OPTIONS=abort_on_error=1:disable_coredump=0:unmap_shadow_on_exit=1' to enable core generation."); + } + FLAG_SET_ERGO(CreateCoredumpOnCrash, false); + } +} + +bool Asan::had_error() { + return g_report != nullptr; +} + +void Asan::report(outputStream* st) { + if (had_error()) { + // Use raw print here to avoid truncation. + st->print_raw(g_report); + st->cr(); + st->cr(); } } diff --git a/src/hotspot/share/sanitizers/address.hpp b/src/hotspot/share/sanitizers/address.hpp index 5d20974399a31..109aa59dac07b 100644 --- a/src/hotspot/share/sanitizers/address.hpp +++ b/src/hotspot/share/sanitizers/address.hpp @@ -76,11 +76,13 @@ } while (false) #endif +class outputStream; + #ifdef ADDRESS_SANITIZER struct Asan : public AllStatic { static void initialize(); - // Returns the ASAN report text; nullptr if no ASAN error happened. - static const char* report(); + static bool had_error(); + static void report(outputStream* st); }; #endif diff --git a/src/hotspot/share/utilities/vmError.cpp b/src/hotspot/share/utilities/vmError.cpp index 0a7352d2af7f9..e0cbb60c74422 100644 --- a/src/hotspot/share/utilities/vmError.cpp +++ b/src/hotspot/share/utilities/vmError.cpp @@ -912,13 +912,11 @@ void VMError::report(outputStream* st, bool _verbose) { os::print_date_and_time(st, buf, sizeof(buf)); #ifdef ADDRESS_SANITIZER - STEP_IF("printing ASAN error information", _verbose && Asan::report() != nullptr) + STEP_IF("printing ASAN error information", _verbose && Asan::had_error()) st->cr(); st->print_cr("------------------ A S A N ----------------"); st->cr(); - // Note: Use print_raw, not print or print_cr, to avoid truncation - // (report can be longer than 2K) - st->print_raw(Asan::report()); + Asan::report(st); st->cr(); #endif // ADDRESS_SANITIZER diff --git a/test/hotspot/jtreg/runtime/ErrorHandling/AsanReportTest.java b/test/hotspot/jtreg/runtime/ErrorHandling/AsanReportTest.java index 3638f670663d6..211df563e6ce5 100644 --- a/test/hotspot/jtreg/runtime/ErrorHandling/AsanReportTest.java +++ b/test/hotspot/jtreg/runtime/ErrorHandling/AsanReportTest.java @@ -37,11 +37,9 @@ // Note: this test can only run on debug since it relies on VMError::controlled_crash() which // only exists in debug builds. import java.io.File; -import java.util.Arrays; import java.util.regex.Pattern; import jdk.test.lib.process.OutputAnalyzer; -import jdk.test.lib.Platform; import jdk.test.lib.process.ProcessTools; public class AsanReportTest { @@ -49,8 +47,11 @@ public class AsanReportTest { private static void do_test() throws Exception { ProcessBuilder pb = ProcessTools.createLimitedTestJavaProcessBuilder( - "-Xmx100M", - "-XX:-CreateCoredumpOnCrash", + "-Xmx64M", "-XX:CompressedClassSpaceSize=64M", + // Default ASAN options should prevent core file generation, which should overrule +CreateCoredumpOnCrash. + // We test below. + "-XX:+CreateCoredumpOnCrash", + "-Xlog:asan", // Switch off NMT since it can alter the error ASAN sees; we want the pure double free error "-XX:NativeMemoryTracking=off", // Causes double-free in controlled_crash @@ -62,10 +63,12 @@ private static void do_test() throws Exception { output.shouldNotHaveExitValue(0); // ASAN error should appear on stderr + output.shouldContain("CreateCoredumpOnCrash overruled"); output.shouldContain("JVM caught ASAN Error"); - output.shouldContain("# A fatal error has been detected by the Java Runtime Environment"); - output.shouldMatch(".*AddressSanitizer.*double-free.*"); - output.shouldContain("# fatal error: ASAN error"); + output.shouldMatch("AddressSanitizer.*double-free"); + output.shouldMatch("# +A fatal error has been detected by the Java Runtime Environment"); + output.shouldMatch("# +fatal error: ASAN"); + output.shouldNotContain("Aborted (core dumped)"); File hs_err_file = HsErrFileUtils.openHsErrFileFromOutput(output); Pattern[] pat = new Pattern[] { @@ -74,7 +77,6 @@ private static void do_test() throws Exception { Pattern.compile(".*(crash_with_segfault|controlled_crash).*") }; HsErrFileUtils.checkHsErrFileContent(hs_err_file, pat, false); - } public static void main(String[] args) throws Exception { From 4adc9689aeb0c76912909f1043ebd0d581ddcd33 Mon Sep 17 00:00:00 2001 From: tstuefe Date: Wed, 24 Sep 2025 16:19:23 +0200 Subject: [PATCH 08/11] fix --- src/hotspot/share/sanitizers/address.cpp | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/hotspot/share/sanitizers/address.cpp b/src/hotspot/share/sanitizers/address.cpp index 263e58e886dc5..a3e4bb5a6e3d2 100644 --- a/src/hotspot/share/sanitizers/address.cpp +++ b/src/hotspot/share/sanitizers/address.cpp @@ -26,7 +26,6 @@ #include "logging/log.hpp" #include "sanitizers/address.hpp" -#include "runtime/globals_extension.hpp" #include "utilities/globalDefinitions.hpp" #include "utilities/vmError.hpp" @@ -78,8 +77,8 @@ void Asan::initialize() { // // In hotspot ASAN builds, we replace the default ASAN reporter. The soft limit imposed by // "disable_coredump=1" is still in effect. But "abort_on_error" is not honored. Since we'd - // like to exhibit exactly the same behavior as the standard ASAN error reporter, we honor - // "abort_on_error=1" by ending the JVM with exit(3) (we just switch off CreateCoredumpOnCrash). + // like to exhibit exactly the same behavior as the standard ASAN error reporter, we disable + // core files if ASAN would inhibit them (we just switch off CreateCoredumpOnCrash). // // Thus: // abort_on_error disable_coredump core file? @@ -90,12 +89,15 @@ void Asan::initialize() { // (*) is the default if no ASAN options are specified. const char* const asan_options = getenv("ASAN_OPTIONS"); - const bool abort_on_error = (asan_options != nullptr) && ::strstr(asan_options, "abort_on_error=1"); - if (!abort_on_error) { - if (CreateCoredumpOnCrash && FLAG_IS_CMDLINE(CreateCoredumpOnCrash)) { - log_warning(asan)("CreateCoredumpOnCrash overruled by%s abort_on_error, core generation will be disabled.", + const bool asan_inhibits_cores = (asan_options == nullptr) || + (::strstr(asan_options, "abort_on_error=1") == nullptr) || + (::strstr(asan_options, "disable_coredump=0") == nullptr); + if (asan_inhibits_cores) { + if (CreateCoredumpOnCrash) { + log_info(asan)("CreateCoredumpOnCrash overruled by%s asan options. Core generation disabled.", asan_options != nullptr ? "" : " default setting for"); - log_warning(asan)("Use 'ASAN_OPTIONS=abort_on_error=1:disable_coredump=0:unmap_shadow_on_exit=1' to enable core generation."); + log_info(asan)("Use 'ASAN_OPTIONS=abort_on_error=1:disable_coredump=0:unmap_shadow_on_exit=1' " + "to enable core generation."); } FLAG_SET_ERGO(CreateCoredumpOnCrash, false); } From 549edb5d43293b8d7ae5e6214e1ee17c68ad02f8 Mon Sep 17 00:00:00 2001 From: Thomas Stuefe Date: Sat, 27 Sep 2025 08:42:39 +0200 Subject: [PATCH 09/11] Comment reference to asan_interface.h --- src/hotspot/share/sanitizers/address.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/hotspot/share/sanitizers/address.cpp b/src/hotspot/share/sanitizers/address.cpp index a3e4bb5a6e3d2..3648462791ebd 100644 --- a/src/hotspot/share/sanitizers/address.cpp +++ b/src/hotspot/share/sanitizers/address.cpp @@ -54,6 +54,7 @@ extern "C" void asan_error_callback(const char* report_text) { void Asan::initialize() { + // For documentation of __asan_set_error_report_callback() see asan_interfact.h g_callback_setter = (callback_setter_t) dlsym(RTLD_DEFAULT, "__asan_set_error_report_callback"); if (g_callback_setter == nullptr) { log_warning(asan)("*** Failed to install JVM callback for ASAN. ASAN errors will not generate hs-err files. ***"); From 0a1f5361d02d619bb21d8b40126cf12ba99bd62e Mon Sep 17 00:00:00 2001 From: Thomas Stuefe Date: Sat, 27 Sep 2025 08:44:07 +0200 Subject: [PATCH 10/11] Update address.cpp --- src/hotspot/share/sanitizers/address.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hotspot/share/sanitizers/address.cpp b/src/hotspot/share/sanitizers/address.cpp index 3648462791ebd..2f95f782851d8 100644 --- a/src/hotspot/share/sanitizers/address.cpp +++ b/src/hotspot/share/sanitizers/address.cpp @@ -54,7 +54,7 @@ extern "C" void asan_error_callback(const char* report_text) { void Asan::initialize() { - // For documentation of __asan_set_error_report_callback() see asan_interfact.h + // For documentation of __asan_set_error_report_callback() see asan_interface.h . g_callback_setter = (callback_setter_t) dlsym(RTLD_DEFAULT, "__asan_set_error_report_callback"); if (g_callback_setter == nullptr) { log_warning(asan)("*** Failed to install JVM callback for ASAN. ASAN errors will not generate hs-err files. ***"); From e250395484d68abc365e33633d080b862dbad99c Mon Sep 17 00:00:00 2001 From: Thomas Stuefe Date: Mon, 29 Sep 2025 08:07:01 +0200 Subject: [PATCH 11/11] Update address.cpp --- src/hotspot/share/sanitizers/address.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hotspot/share/sanitizers/address.cpp b/src/hotspot/share/sanitizers/address.cpp index 2f95f782851d8..b050039b1b821 100644 --- a/src/hotspot/share/sanitizers/address.cpp +++ b/src/hotspot/share/sanitizers/address.cpp @@ -57,7 +57,7 @@ void Asan::initialize() { // For documentation of __asan_set_error_report_callback() see asan_interface.h . g_callback_setter = (callback_setter_t) dlsym(RTLD_DEFAULT, "__asan_set_error_report_callback"); if (g_callback_setter == nullptr) { - log_warning(asan)("*** Failed to install JVM callback for ASAN. ASAN errors will not generate hs-err files. ***"); + log_info(asan)("*** Failed to install JVM callback for ASAN. ASAN errors will not generate hs-err files. ***"); return; }