Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 13 additions & 4 deletions src/java.base/share/classes/java/lang/ClassLoader.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2013, 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2013, 2025, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2019, Azul Systems, Inc. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
Expand Down Expand Up @@ -52,6 +52,7 @@
import java.util.stream.Stream;
import java.util.stream.StreamSupport;

import jdk.internal.access.SharedSecrets;
import jdk.internal.loader.BootLoader;
import jdk.internal.loader.BuiltinClassLoader;
import jdk.internal.loader.ClassLoaders;
Expand Down Expand Up @@ -1049,14 +1050,22 @@ protected final Class<?> defineClass(String name, java.nio.ByteBuffer b,

protectionDomain = preDefineClass(name, protectionDomain);
String source = defineClassSourceLocation(protectionDomain);
Class<?> c = defineClass2(this, name, b, b.position(), len, protectionDomain, source);
postDefineClass(c, protectionDomain);
return c;

SharedSecrets.getJavaNioAccess().acquireSession(b);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now that the fields in SharedSecrets are @Stable, we do not have to make a local copy in a static final field.

try {
Class<?> c = defineClass2(this, name, b, b.position(), len, protectionDomain, source);
postDefineClass(c, protectionDomain);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we leave postDefineClass out of this acquire-release scope? I don't see any reason including this.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think it matters here because something looking to close the arena around the time that it wants to defineClass with memory allocated from that arena is broken.

return c;
} finally {
SharedSecrets.getJavaNioAccess().releaseSession(b);
}
}

static native Class<?> defineClass1(ClassLoader loader, String name, byte[] b, int off, int len,
ProtectionDomain pd, String source);

// Warning: Before calling this method, the provided ByteBuffer must be guarded
// via JavaNioAccess::(acquire|release)Session
static native Class<?> defineClass2(ClassLoader loader, String name, java.nio.ByteBuffer b,
int off, int len, ProtectionDomain pd,
String source);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
/*
* 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
* @bug 8365203
* @summary Tests guarding of ByteBuffers in ClassLoader::defineClass
* @run junit TestGuardByteBuffer
*/

import org.junit.jupiter.api.Test;

import java.lang.foreign.Arena;
import java.util.HexFormat;

import static org.junit.jupiter.api.Assertions.*;

final class TestGuardByteBuffer {

@Test
void guardCrash() {
final byte[] classBytes = getClassBytes(); // get bytes of a valid class
final var cl = new ClassLoader() {
void tryCrash() {
var arena = Arena.ofConfined();
var byteBuffer = arena.allocate(classBytes.length).asByteBuffer();
// Close the arena underneath
arena.close();
// expected to always fail because the arena
// from which the ByteBuffer was constructed
// has been closed
assertThrows(IllegalStateException.class,
() -> defineClass(null, byteBuffer, null));
}
};
for (int i = 0; i < 10_000; i++) {
cl.tryCrash();
}
}

private static byte[] getClassBytes() {
// unused. this is here just for reference
final String source = """
public class NoOp {}
""";
// (externally) compiled content of the above "source", represented as hex
final String classBytesHex = """
cafebabe00000044000d0a000200030700040c000500060100106a6176612f
6c616e672f4f626a6563740100063c696e69743e0100032829560700080100
044e6f4f70010004436f646501000f4c696e654e756d6265725461626c6501
000a536f7572636546696c650100094e6f4f702e6a61766100210007000200
0000000001000100050006000100090000001d00010001000000052ab70001
b100000001000a000000060001000000010001000b00000002000c
""";

return HexFormat.of().parseHex(classBytesHex.replaceAll("\n", ""));
}

}