Skip to content

Commit c54521b

Browse files
Andrew LuRealLucy
Andrew Lu
authored andcommittedNov 9, 2023
8314263: Signed jars triggering Logger finder recursion and StackOverflowError
8315696: SignedLoggerFinderTest.java test failed 8316087: Test SignedLoggerFinderTest.java is still failing Reviewed-by: lucy Backport-of: 7daae1fb4267f92b38f0152611d69b7b89691087
1 parent 416c48e commit c54521b

File tree

17 files changed

+1074
-40
lines changed

17 files changed

+1074
-40
lines changed
 

‎src/java.base/share/classes/java/lang/System.java

+7-3
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@
6060
import java.util.concurrent.ConcurrentHashMap;
6161
import java.util.stream.Stream;
6262

63+
import jdk.internal.logger.LoggerFinderLoader.TemporaryLoggerFinder;
6364
import jdk.internal.util.StaticProperty;
6465
import jdk.internal.module.ModuleBootstrap;
6566
import jdk.internal.module.ServicesCatalog;
@@ -1619,13 +1620,16 @@ static LoggerFinder accessProvider() {
16191620
// We do not need to synchronize: LoggerFinderLoader will
16201621
// always return the same instance, so if we don't have it,
16211622
// just fetch it again.
1622-
if (service == null) {
1623+
LoggerFinder finder = service;
1624+
if (finder == null) {
16231625
PrivilegedAction<LoggerFinder> pa =
16241626
() -> LoggerFinderLoader.getLoggerFinder();
1625-
service = AccessController.doPrivileged(pa, null,
1627+
finder = AccessController.doPrivileged(pa, null,
16261628
LOGGERFINDER_PERMISSION);
1629+
if (finder instanceof TemporaryLoggerFinder) return finder;
1630+
service = finder;
16271631
}
1628-
return service;
1632+
return finder;
16291633
}
16301634

16311635
}

‎src/java.base/share/classes/jdk/internal/logger/BootstrapLogger.java

+38-15
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2015, 2023, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -38,7 +38,6 @@
3838
import java.util.function.Supplier;
3939
import java.lang.System.LoggerFinder;
4040
import java.lang.System.Logger;
41-
import java.lang.System.Logger.Level;
4241
import java.lang.ref.WeakReference;
4342
import java.util.Objects;
4443
import java.util.concurrent.ExecutionException;
@@ -228,9 +227,19 @@ static void flush() {
228227

229228
// The accessor in which this logger is temporarily set.
230229
final LazyLoggerAccessor holder;
230+
// tests whether the logger is invoked by the loading thread before
231+
// the LoggerFinder is loaded; can be null;
232+
final BooleanSupplier isLoadingThread;
233+
234+
// returns true if the logger is invoked by the loading thread before the
235+
// LoggerFinder service is loaded
236+
boolean isLoadingThread() {
237+
return isLoadingThread != null && isLoadingThread.getAsBoolean();
238+
}
231239

232-
BootstrapLogger(LazyLoggerAccessor holder) {
240+
BootstrapLogger(LazyLoggerAccessor holder, BooleanSupplier isLoadingThread) {
233241
this.holder = holder;
242+
this.isLoadingThread = isLoadingThread;
234243
}
235244

236245
// Temporary data object storing log events
@@ -499,14 +508,15 @@ static LogEvent valueOf(BootstrapLogger bootstrap, PlatformLogger.Level level,
499508
static void log(LogEvent log, PlatformLogger.Bridge logger) {
500509
final SecurityManager sm = System.getSecurityManager();
501510
if (sm == null || log.acc == null) {
502-
log.log(logger);
511+
BootstrapExecutors.submit(() -> log.log(logger));
503512
} else {
504513
// not sure we can actually use lambda here. We may need to create
505514
// an anonymous class. Although if we reach here, then it means
506515
// the VM is booted.
507-
AccessController.doPrivileged((PrivilegedAction<Void>) () -> {
508-
log.log(logger); return null;
509-
}, log.acc);
516+
BootstrapExecutors.submit(() ->
517+
AccessController.doPrivileged((PrivilegedAction<Void>) () -> {
518+
log.log(logger); return null;
519+
}, log.acc));
510520
}
511521
}
512522

@@ -553,8 +563,9 @@ public String getName() {
553563
* @return true if the VM is still bootstrapping.
554564
*/
555565
boolean checkBootstrapping() {
556-
if (isBooted()) {
566+
if (isBooted() && !isLoadingThread()) {
557567
BootstrapExecutors.flush();
568+
holder.getConcreteLogger(this);
558569
return false;
559570
}
560571
return true;
@@ -928,20 +939,26 @@ private static boolean useSurrogateLoggers() {
928939
// - the logging backend is a custom backend
929940
// - the logging backend is JUL, there is no custom config,
930941
// and the LogManager has not been initialized yet.
931-
public static synchronized boolean useLazyLoggers() {
932-
return !BootstrapLogger.isBooted()
933-
|| DetectBackend.detectedBackend == LoggingBackend.CUSTOM
934-
|| useSurrogateLoggers();
942+
public static boolean useLazyLoggers() {
943+
// Note: avoid triggering the initialization of the DetectBackend class
944+
// while holding the BootstrapLogger class monitor
945+
if (!BootstrapLogger.isBooted() ||
946+
DetectBackend.detectedBackend == LoggingBackend.CUSTOM) {
947+
return true;
948+
}
949+
synchronized (BootstrapLogger.class) {
950+
return useSurrogateLoggers();
951+
}
935952
}
936953

937954
// Called by LazyLoggerAccessor. This method will determine whether
938955
// to create a BootstrapLogger (if the VM is not yet booted),
939956
// a SurrogateLogger (if JUL is the default backend and there
940957
// is no custom JUL configuration and LogManager is not yet initialized),
941958
// or a logger returned by the loaded LoggerFinder (all other cases).
942-
static Logger getLogger(LazyLoggerAccessor accessor) {
943-
if (!BootstrapLogger.isBooted()) {
944-
return new BootstrapLogger(accessor);
959+
static Logger getLogger(LazyLoggerAccessor accessor, BooleanSupplier isLoading) {
960+
if (!BootstrapLogger.isBooted() || isLoading != null && isLoading.getAsBoolean()) {
961+
return new BootstrapLogger(accessor, isLoading);
945962
} else {
946963
if (useSurrogateLoggers()) {
947964
// JUL is the default backend, there is no custom configuration,
@@ -957,6 +974,12 @@ static Logger getLogger(LazyLoggerAccessor accessor) {
957974
}
958975
}
959976

977+
// trigger class initialization outside of holding lock
978+
static void ensureBackendDetected() {
979+
assert VM.isBooted() : "VM is not booted";
980+
// triggers detection of the backend
981+
var backend = DetectBackend.detectedBackend;
982+
}
960983

961984
// If the backend is JUL, and there is no custom configuration, and
962985
// nobody has attempted to call LogManager.getLogManager() yet, then

‎src/java.base/share/classes/jdk/internal/logger/LazyLoggers.java

+26-7
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2015, 2023, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -32,6 +32,9 @@
3232
import java.lang.System.Logger;
3333
import java.lang.ref.WeakReference;
3434
import java.util.Objects;
35+
import java.util.function.BooleanSupplier;
36+
37+
import jdk.internal.logger.LoggerFinderLoader.TemporaryLoggerFinder;
3538
import jdk.internal.misc.VM;
3639
import sun.util.logging.PlatformLogger;
3740

@@ -110,6 +113,9 @@ static final class LazyLoggerAccessor implements LoggerAccessor {
110113
// We need to pass the actual caller module when creating the logger.
111114
private final WeakReference<Module> moduleRef;
112115

116+
// whether this is the loading thread, can be null
117+
private final BooleanSupplier isLoadingThread;
118+
113119
// The name of the logger that will be created lazyly
114120
final String name;
115121
// The plain logger SPI object - null until it is accessed for the
@@ -122,16 +128,24 @@ static final class LazyLoggerAccessor implements LoggerAccessor {
122128
private LazyLoggerAccessor(String name,
123129
LazyLoggerFactories<? extends Logger> factories,
124130
Module module) {
131+
this(name, factories, module, null);
132+
}
133+
134+
private LazyLoggerAccessor(String name,
135+
LazyLoggerFactories<? extends Logger> factories,
136+
Module module, BooleanSupplier isLoading) {
137+
125138
this(Objects.requireNonNull(name), Objects.requireNonNull(factories),
126-
Objects.requireNonNull(module), null);
139+
Objects.requireNonNull(module), isLoading, null);
127140
}
128141

129142
private LazyLoggerAccessor(String name,
130143
LazyLoggerFactories<? extends Logger> factories,
131-
Module module, Void unused) {
144+
Module module, BooleanSupplier isLoading, Void unused) {
132145
this.name = name;
133146
this.factories = factories;
134147
this.moduleRef = new WeakReference<>(module);
148+
this.isLoadingThread = isLoading;
135149
}
136150

137151
/**
@@ -162,7 +176,7 @@ public Logger wrapped() {
162176
// BootstrapLogger has the logic to decide whether to invoke the
163177
// SPI or use a temporary (BootstrapLogger or SimpleConsoleLogger)
164178
// logger.
165-
wrapped = BootstrapLogger.getLogger(this);
179+
wrapped = BootstrapLogger.getLogger(this, isLoadingThread);
166180
synchronized(this) {
167181
// if w has already been in between, simply drop 'wrapped'.
168182
setWrappedIfNotSet(wrapped);
@@ -194,7 +208,7 @@ public PlatformLogger.Bridge platform() {
194208
// BootstrapLogger has the logic to decide whether to invoke the
195209
// SPI or use a temporary (BootstrapLogger or SimpleConsoleLogger)
196210
// logger.
197-
final Logger wrapped = BootstrapLogger.getLogger(this);
211+
final Logger wrapped = BootstrapLogger.getLogger(this, isLoadingThread);
198212
synchronized(this) {
199213
// if w has already been set, simply drop 'wrapped'.
200214
setWrappedIfNotSet(wrapped);
@@ -282,10 +296,10 @@ Logger createLogger() {
282296
* Creates a new lazy logger accessor for the named logger. The given
283297
* factories will be use when it becomes necessary to actually create
284298
* the logger.
285-
* @param <T> An interface that extends {@link Logger}.
286299
* @param name The logger name.
287300
* @param factories The factories that should be used to create the
288301
* wrapped logger.
302+
* @param module The module for which the logger is being created
289303
* @return A new LazyLoggerAccessor.
290304
*/
291305
public static LazyLoggerAccessor makeAccessor(String name,
@@ -339,6 +353,7 @@ private static LoggerFinder accessLoggerFinder() {
339353
prov = sm == null ? LoggerFinder.getLoggerFinder() :
340354
AccessController.doPrivileged(
341355
(PrivilegedAction<LoggerFinder>)LoggerFinder::getLoggerFinder);
356+
if (prov instanceof TemporaryLoggerFinder) return prov;
342357
provider = prov;
343358
}
344359
return prov;
@@ -358,7 +373,6 @@ public Logger apply(String name, Module module) {
358373
new LazyLoggerFactories<>(loggerSupplier);
359374

360375

361-
362376
// A concrete implementation of Logger that delegates to a System.Logger,
363377
// but only creates the System.Logger instance lazily when it's used for
364378
// the first time.
@@ -376,6 +390,11 @@ private JdkLazyLogger(LazyLoggerAccessor holder, Void unused) {
376390
}
377391
}
378392

393+
static Logger makeLazyLogger(String name, Module module, BooleanSupplier isLoading) {
394+
final LazyLoggerAccessor holder = new LazyLoggerAccessor(name, factories, module, isLoading);
395+
return new JdkLazyLogger(holder, null);
396+
}
397+
379398
/**
380399
* Gets a logger from the LoggerFinder. Creates the actual concrete
381400
* logger.

‎src/java.base/share/classes/jdk/internal/logger/LoggerFinderLoader.java

+57-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2015, 2023, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -25,13 +25,18 @@
2525
package jdk.internal.logger;
2626

2727
import java.io.FilePermission;
28+
import java.lang.System.Logger;
29+
import java.lang.System.LoggerFinder;
2830
import java.security.AccessController;
2931
import java.security.Permission;
3032
import java.security.PrivilegedAction;
3133
import java.util.Iterator;
3234
import java.util.Locale;
3335
import java.util.ServiceConfigurationError;
3436
import java.util.ServiceLoader;
37+
import java.util.function.BooleanSupplier;
38+
39+
import jdk.internal.vm.annotation.Stable;
3540
import sun.security.util.SecurityConstants;
3641
import sun.security.action.GetPropertyAction;
3742

@@ -64,20 +69,41 @@ private LoggerFinderLoader() {
6469
throw new InternalError("LoggerFinderLoader cannot be instantiated");
6570
}
6671

67-
72+
// record the loadingThread while loading the backend
73+
static volatile Thread loadingThread;
6874
// Return the loaded LoggerFinder, or load it if not already loaded.
6975
private static System.LoggerFinder service() {
7076
if (service != null) return service;
77+
// ensure backend is detected before attempting to load the finder
78+
BootstrapLogger.ensureBackendDetected();
7179
synchronized(lock) {
7280
if (service != null) return service;
73-
service = loadLoggerFinder();
81+
Thread currentThread = Thread.currentThread();
82+
if (loadingThread == currentThread) {
83+
// recursive attempt to load the backend while loading the backend
84+
// use a temporary logger finder that returns special BootstrapLogger
85+
// which will wait until loading is finished
86+
return TemporaryLoggerFinder.INSTANCE;
87+
}
88+
loadingThread = currentThread;
89+
try {
90+
service = loadLoggerFinder();
91+
} finally {
92+
loadingThread = null;
93+
}
7494
}
7595
// Since the LoggerFinder is already loaded - we can stop using
7696
// temporary loggers.
7797
BootstrapLogger.redirectTemporaryLoggers();
7898
return service;
7999
}
80100

101+
// returns true if called by the thread that loads the LoggerFinder, while
102+
// loading the LoggerFinder.
103+
static boolean isLoadingThread() {
104+
return loadingThread != null && loadingThread == Thread.currentThread();
105+
}
106+
81107
// Get configuration error policy
82108
private static ErrorPolicy configurationErrorPolicy() {
83109
String errorPolicy =
@@ -115,6 +141,34 @@ private static Iterator<System.LoggerFinder> findLoggerFinderProviders() {
115141
return iterator;
116142
}
117143

144+
public static final class TemporaryLoggerFinder extends LoggerFinder {
145+
private TemporaryLoggerFinder() {}
146+
@Stable
147+
private LoggerFinder loadedService;
148+
149+
private static final BooleanSupplier isLoadingThread = new BooleanSupplier() {
150+
@Override
151+
public boolean getAsBoolean() {
152+
return LoggerFinderLoader.isLoadingThread();
153+
}
154+
};
155+
private static final TemporaryLoggerFinder INSTANCE = new TemporaryLoggerFinder();
156+
157+
@Override
158+
public Logger getLogger(String name, Module module) {
159+
if (loadedService == null) {
160+
loadedService = service;
161+
if (loadedService == null) {
162+
return LazyLoggers.makeLazyLogger(name, module, isLoadingThread);
163+
}
164+
}
165+
assert loadedService != null;
166+
assert !LoggerFinderLoader.isLoadingThread();
167+
assert loadedService != this;
168+
return LazyLoggers.getLogger(name, module);
169+
}
170+
}
171+
118172
// Loads the LoggerFinder using ServiceLoader. If no LoggerFinder
119173
// is found returns the default (possibly JUL based) implementation
120174
private static System.LoggerFinder loadLoggerFinder() {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
loggerfinder.SimpleLoggerFinder

1 commit comments

Comments
 (1)

openjdk-notifier[bot] commented on Nov 9, 2023

@openjdk-notifier[bot]
Please sign in to comment.