8343839: Detect patched modules and abort run-time image link early

Reviewed-by: mchung
This commit is contained in:
Severin Gehwolf 2024-12-03 20:41:48 +00:00
parent 0664b51765
commit 05ee562a38
9 changed files with 30 additions and 121 deletions

View File

@ -32,6 +32,7 @@ import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.UncheckedIOException;
import java.nio.ByteOrder;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
@ -56,7 +57,6 @@ import jdk.tools.jlink.internal.runtimelink.JimageDiffGenerator;
import jdk.tools.jlink.internal.runtimelink.JimageDiffGenerator.ImageResource;
import jdk.tools.jlink.internal.runtimelink.ResourceDiff;
import jdk.tools.jlink.internal.runtimelink.ResourcePoolReader;
import jdk.tools.jlink.internal.runtimelink.RuntimeImageLinkException;
import jdk.tools.jlink.plugin.PluginException;
import jdk.tools.jlink.plugin.ResourcePool;
import jdk.tools.jlink.plugin.ResourcePoolBuilder;
@ -91,14 +91,11 @@ public final class ImageFileCreator {
private final Map<String, List<Entry>> entriesForModule = new HashMap<>();
private final ImagePluginStack plugins;
private final boolean generateRuntimeImage;
private final TaskHelper helper;
private ImageFileCreator(ImagePluginStack plugins,
boolean generateRuntimeImage,
TaskHelper taskHelper) {
boolean generateRuntimeImage) {
this.plugins = Objects.requireNonNull(plugins);
this.generateRuntimeImage = generateRuntimeImage;
this.helper = taskHelper;
}
/**
@ -118,24 +115,20 @@ public final class ImageFileCreator {
public static ExecutableImage create(Set<Archive> archives,
ByteOrder byteOrder,
ImagePluginStack plugins,
boolean generateRuntimeImage,
TaskHelper taskHelper)
boolean generateRuntimeImage)
throws IOException
{
ImageFileCreator image = new ImageFileCreator(plugins,
generateRuntimeImage,
taskHelper);
generateRuntimeImage);
try {
image.readAllEntries(archives);
// write to modular image
image.writeImage(archives, byteOrder);
} catch (RuntimeImageLinkException e) {
// readAllEntries() might throw this exception.
// Propagate as IOException with appropriate message for
// jlink runs from the run-time image. This handles better
// error messages for the case of modified files in the run-time
// image.
throw image.newIOException(e);
} catch (UncheckedIOException e) {
// When linking from the run-time image, readAllEntries() might
// throw this exception for a modified runtime. Unpack and
// re-throw as IOException.
throw e.getCause();
} finally {
// Close all archives
for (Archive a : archives) {
@ -200,11 +193,6 @@ public final class ImageFileCreator {
ResourcePool result = null;
try (DataOutputStream out = plugins.getJImageFileOutputStream()) {
result = generateJImage(allContent, writer, plugins, out, generateRuntimeImage);
} catch (RuntimeImageLinkException e) {
// Propagate as IOException with appropriate message for
// jlink runs from the run-time image. This handles better
// error messages for the case of --patch-module.
throw newIOException(e);
}
//Handle files.
@ -218,18 +206,6 @@ public final class ImageFileCreator {
}
}
private IOException newIOException(RuntimeImageLinkException e) throws IOException {
if (JlinkTask.DEBUG) {
e.printStackTrace();
}
String message = switch (e.getReason()) {
case PATCH_MODULE -> helper.getMessage("err.runtime.link.patched.module", e.getFile());
case MODIFIED_FILE -> helper.getMessage("err.runtime.link.modified.file", e.getFile());
default -> throw new AssertionError("Unexpected value: " + e.getReason());
};
throw new IOException(message);
}
/**
* Create a jimage based on content of the given ResourcePoolManager,
* optionally creating a runtime that can be used for linking from the

View File

@ -26,8 +26,6 @@
package jdk.tools.jlink.internal;
import static jdk.tools.jlink.internal.LinkableRuntimeImage.RESPATH_PATTERN;
import static jdk.tools.jlink.internal.runtimelink.RuntimeImageLinkException.Reason.MODIFIED_FILE;
import static jdk.tools.jlink.internal.runtimelink.RuntimeImageLinkException.Reason.PATCH_MODULE;
import java.io.ByteArrayInputStream;
import java.io.IOException;
@ -37,7 +35,6 @@ import java.lang.module.ModuleFinder;
import java.lang.module.ModuleReference;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.NoSuchFileException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.security.MessageDigest;
@ -56,7 +53,6 @@ import java.util.stream.Stream;
import jdk.internal.util.OperatingSystem;
import jdk.tools.jlink.internal.Archive.Entry.EntryType;
import jdk.tools.jlink.internal.runtimelink.ResourceDiff;
import jdk.tools.jlink.internal.runtimelink.RuntimeImageLinkException;
import jdk.tools.jlink.plugin.ResourcePoolEntry;
import jdk.tools.jlink.plugin.ResourcePoolEntry.Type;
@ -223,7 +219,9 @@ public class JRTArchive implements Archive {
Path path = BASE.resolve(m.resPath);
if (shaSumMismatch(path, m.hashOrTarget, m.symlink)) {
if (errorOnModifiedFile) {
throw new RuntimeImageLinkException(path.toString(), MODIFIED_FILE);
String msg = taskHelper.getMessage("err.runtime.link.modified.file", path.toString());
IOException cause = new IOException(msg);
throw new UncheckedIOException(cause);
} else {
taskHelper.warning("err.runtime.link.modified.file", path.toString());
}
@ -460,16 +458,7 @@ public class JRTArchive implements Archive {
// the underlying base path is a JrtPath with the
// JrtFileSystem underneath which is able to handle
// this size query.
try {
return Files.size(archive.getPath().resolve(resPath));
} catch (NoSuchFileException file) {
// This indicates that we don't find the class in the
// modules image using the JRT FS provider. Yet, we find
// the class using the system module finder. Therefore,
// we have a patched module. Mention that module patching
// is not supported.
throw new RuntimeImageLinkException(file.getFile(), PATCH_MODULE);
}
return Files.size(archive.getPath().resolve(resPath));
}
} catch (IOException e) {
throw new UncheckedIOException(e);

View File

@ -63,6 +63,7 @@ import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import jdk.internal.module.ModuleBootstrap;
import jdk.internal.module.ModulePath;
import jdk.internal.module.ModuleReferenceImpl;
import jdk.internal.module.ModuleResolution;
@ -73,7 +74,6 @@ import jdk.tools.jlink.internal.Jlink.PluginsConfiguration;
import jdk.tools.jlink.internal.TaskHelper.BadArgs;
import jdk.tools.jlink.internal.TaskHelper.Option;
import jdk.tools.jlink.internal.TaskHelper.OptionsHelper;
import jdk.tools.jlink.internal.runtimelink.RuntimeImageLinkException;
import jdk.tools.jlink.plugin.PluginException;
/**
@ -309,7 +309,7 @@ public class JlinkTask {
}
cleanupOutput(outputPath);
return EXIT_ERROR;
} catch (IllegalArgumentException | ResolutionException | RuntimeImageLinkException e) {
} catch (IllegalArgumentException | ResolutionException e) {
log.println(taskHelper.getMessage("error.prefix") + " " + e.getMessage());
if (DEBUG) {
e.printStackTrace(log);
@ -620,6 +620,12 @@ public class JlinkTask {
String msg = taskHelper.getMessage("err.runtime.link.jdk.jlink.prohibited");
throw new IllegalArgumentException(msg);
}
// Do not permit linking from run-time image when the current image
// is being patched.
if (ModuleBootstrap.patcher().hasPatches()) {
String msg = taskHelper.getMessage("err.runtime.link.patched.module");
throw new IllegalArgumentException(msg);
}
// Print info message indicating linking from the run-time image
if (verbose && log != null) {
@ -1039,7 +1045,7 @@ public class JlinkTask {
@Override
public ExecutableImage retrieve(ImagePluginStack stack) throws IOException {
ExecutableImage image = ImageFileCreator.create(archives,
targetPlatform.arch().byteOrder(), stack, generateRuntimeImage, taskHelper);
targetPlatform.arch().byteOrder(), stack, generateRuntimeImage);
if (packagedModulesPath != null) {
// copy the packaged modules to the given path
Files.createDirectories(packagedModulesPath);

View File

@ -1,62 +0,0 @@
/*
* Copyright (c) 2024, Red Hat, Inc.
* 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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.
*/
package jdk.tools.jlink.internal.runtimelink;
import java.util.Objects;
/**
* Exception thrown when linking from the run-time image
*/
public class RuntimeImageLinkException extends RuntimeException {
private static final long serialVersionUID = -1848914673073119403L;
public static enum Reason {
PATCH_MODULE, /* link exception due to patched module */
MODIFIED_FILE, /* link exception due to modified file */
}
private final String file;
private final Reason reason;
public RuntimeImageLinkException(String file, Reason reason) {
this.file = Objects.requireNonNull(file);
this.reason = Objects.requireNonNull(reason);
}
public String getFile() {
return file;
}
public Reason getReason() {
return reason;
}
@Override
public String getMessage() {
return reason + ", file: " + file;
}
}

View File

@ -125,8 +125,8 @@ err.runtime.link.jdk.jlink.prohibited=This JDK does not contain packaged modules
err.runtime.link.packaged.mods=This JDK has no packaged modules.\
\ --keep-packaged-modules is not supported
err.runtime.link.modified.file={0} has been modified
err.runtime.link.patched.module=File {0} not found in the modules image.\
\ --patch-module is not supported when linking from the run-time image
err.runtime.link.patched.module=jlink does not support linking from the run-time image\
\ when running on a patched runtime with --patch-module
err.empty.module.path=empty module path
err.jlink.version.mismatch=jlink version {0}.{1} does not match target java.base version {2}.{3}
err.automatic.module:automatic module cannot be used with jlink: {0} from {1}

View File

@ -224,6 +224,6 @@ public class ImageFileCreatorTest {
ImagePluginStack stack = new ImagePluginStack(noopBuilder, Collections.emptyList(),
null, false);
ImageFileCreator.create(archives, ByteOrder.nativeOrder(), stack, false, null);
ImageFileCreator.create(archives, ByteOrder.nativeOrder(), stack, false);
}
}

View File

@ -78,7 +78,7 @@ public class ModifiedFilesExitTest extends ModifiedFilesTest {
}
analyzer.stdoutShouldContain(modifiedFile.toString() + " has been modified");
// Verify the error message is reasonable
analyzer.stdoutShouldNotContain("jdk.tools.jlink.internal.RunImageLinkException");
analyzer.stdoutShouldNotContain("IOException");
analyzer.stdoutShouldNotContain("java.lang.IllegalArgumentException");
}

View File

@ -70,6 +70,6 @@ public class ModifiedFilesWarningTest extends ModifiedFilesTest {
// verify we get the warning message
out.stdoutShouldMatch("Warning: .* has been modified");
out.stdoutShouldNotContain("java.lang.IllegalArgumentException");
out.stdoutShouldNotContain("jdk.tools.jlink.internal.RunImageLinkException");
out.stdoutShouldNotContain("IOException");
}
}

View File

@ -97,10 +97,10 @@ public class PatchedJDKModuleJlinkTest extends AbstractLinkableRuntimeTest {
if (analyzer.getExitValue() == 0) {
throw new AssertionError("Expected jlink to fail due to patched module!");
}
analyzer.stdoutShouldContain("MyJlinkPatchInteger.class not found in the modules image.");
analyzer.stdoutShouldContain("--patch-module is not supported");
analyzer.stdoutShouldContain("jlink does not support linking from the run-time image");
analyzer.stdoutShouldContain(" when running on a patched runtime with --patch-module");
// Verify the error message is reasonable
analyzer.stdoutShouldNotContain("jdk.tools.jlink.internal.RunImageLinkException");
analyzer.stdoutShouldNotContain("IOException");
analyzer.stdoutShouldNotContain("java.lang.IllegalArgumentException");
}