mirror of
https://github.com/openjdk/jdk.git
synced 2026-01-28 12:09:14 +00:00
8368365: ASAN errors should produce hs-err files and core dumps
Reviewed-by: mbaesken, asmehra
This commit is contained in:
parent
b597b6556d
commit
aaa9fbf6b5
@ -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) \
|
||||
|
||||
@ -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 " \
|
||||
|
||||
@ -97,6 +97,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"
|
||||
@ -702,6 +703,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();
|
||||
|
||||
120
src/hotspot/share/sanitizers/address.cpp
Normal file
120
src/hotspot/share/sanitizers/address.cpp
Normal file
@ -0,0 +1,120 @@
|
||||
/*
|
||||
* 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 <dlfcn.h>
|
||||
#include <stdio.h>
|
||||
|
||||
typedef void (*callback_setter_t) (void (*callback)(const char *));
|
||||
static callback_setter_t g_callback_setter = nullptr;
|
||||
static const char* g_report = nullptr;
|
||||
|
||||
extern "C" void asan_error_callback(const char* 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);
|
||||
|
||||
// Then, let normal JVM error handling run its due course.
|
||||
fatal("ASAN Error");
|
||||
}
|
||||
|
||||
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_info(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 disable
|
||||
// core files if ASAN would inhibit them (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 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_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);
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
#endif // ADDRESS_SANITIZER
|
||||
@ -26,6 +26,8 @@
|
||||
#define SHARE_SANITIZERS_ADDRESS_HPP
|
||||
|
||||
#ifdef ADDRESS_SANITIZER
|
||||
#include "memory/allStatic.hpp"
|
||||
|
||||
#include <sanitizer/asan_interface.h>
|
||||
#endif
|
||||
|
||||
@ -74,4 +76,14 @@
|
||||
} while (false)
|
||||
#endif
|
||||
|
||||
class outputStream;
|
||||
|
||||
#ifdef ADDRESS_SANITIZER
|
||||
struct Asan : public AllStatic {
|
||||
static void initialize();
|
||||
static bool had_error();
|
||||
static void report(outputStream* st);
|
||||
};
|
||||
#endif
|
||||
|
||||
#endif // SHARE_SANITIZERS_ADDRESS_HPP
|
||||
|
||||
@ -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,16 @@ 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::had_error())
|
||||
st->cr();
|
||||
st->print_cr("------------------ A S A N ----------------");
|
||||
st->cr();
|
||||
Asan::report(st);
|
||||
st->cr();
|
||||
#endif // ADDRESS_SANITIZER
|
||||
|
||||
STEP_IF("printing thread", _verbose)
|
||||
st->cr();
|
||||
st->print_cr("--------------- T H R E A D ---------------");
|
||||
st->cr();
|
||||
@ -2186,6 +2196,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);
|
||||
|
||||
87
test/hotspot/jtreg/runtime/ErrorHandling/AsanReportTest.java
Normal file
87
test/hotspot/jtreg/runtime/ErrorHandling/AsanReportTest.java
Normal file
@ -0,0 +1,87 @@
|
||||
/*
|
||||
* 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.regex.Pattern;
|
||||
|
||||
import jdk.test.lib.process.OutputAnalyzer;
|
||||
import jdk.test.lib.process.ProcessTools;
|
||||
|
||||
public class AsanReportTest {
|
||||
|
||||
private static void do_test() throws Exception {
|
||||
|
||||
ProcessBuilder pb = ProcessTools.createLimitedTestJavaProcessBuilder(
|
||||
"-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
|
||||
"-XX:ErrorHandlerTest=18",
|
||||
"-version");
|
||||
|
||||
OutputAnalyzer output = new OutputAnalyzer(pb.start());
|
||||
|
||||
output.shouldNotHaveExitValue(0);
|
||||
|
||||
// ASAN error should appear on stderr
|
||||
output.shouldContain("CreateCoredumpOnCrash overruled");
|
||||
output.shouldContain("JVM caught 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[] {
|
||||
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();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user