JBR-4839 Report if wrong shared library is detected at run time

(cherry picked from commit e8c1913562)
This commit is contained in:
Maxim Kartashev
2022-09-26 16:25:24 +03:00
committed by jbrbot
parent 47096a9f4c
commit 50bc4b2c05
4 changed files with 276 additions and 0 deletions

View File

@@ -24,6 +24,8 @@
*/
#import <jni.h>
#import <mach-o/dyld.h>
#import <jni_util.h>
#import "InitIDs.h"
@@ -161,3 +163,26 @@ JNIEXPORT void JNICALL Java_java_awt_event_MouseEvent_initIDs
(JNIEnv *env, jclass cls)
{
}
JNIEXPORT jarray JNICALL Java_sun_font_FontManagerNativeLibrary_loadedLibraries
(JNIEnv *env, jclass cls)
{
const uint32_t count = _dyld_image_count();
jclass stringClazz = (*env)->FindClass(env, "java/lang/String");
CHECK_NULL_RETURN(stringClazz, NULL);
jarray libsArray = (*env)->NewObjectArray(env, count, stringClazz, NULL);
JNU_CHECK_EXCEPTION_RETURN(env, NULL);
for (uint32_t i = 0; i < count; i++) {
const char * name = _dyld_get_image_name(i);
if (name) {
jstring jName = (*env)->NewStringUTF(env, name);
JNU_CHECK_EXCEPTION_RETURN(env, NULL);
(*env)->SetObjectArrayElement(env, libsArray, i, jName);
JNU_CHECK_EXCEPTION_RETURN(env, NULL);
}
}
return libsArray;
}

View File

@@ -26,6 +26,10 @@
package sun.font;
import sun.awt.OSInfo;
import sun.util.logging.PlatformLogger;
import java.nio.file.Path;
import java.util.Set;
@SuppressWarnings("restricted")
public class FontManagerNativeLibrary {
@@ -53,6 +57,8 @@ public class FontManagerNativeLibrary {
System.loadLibrary("freetype");
}
System.loadLibrary("fontmanager");
checkLibraries();
}
/*
@@ -63,4 +69,72 @@ public class FontManagerNativeLibrary {
* Actual loading is performed by static initializer.
*/
public static void load() {}
private static class Holder {
private static final PlatformLogger errorLog = PlatformLogger.getLogger("sun.font.FontManagerNativeLibrary");
}
public static void checkLibraries() {
final String osName = System.getProperty("os.name");
final boolean isMacOS = osName.startsWith("Mac");
final boolean isLinux = osName.startsWith("Linux");
if (!(isMacOS || isLinux)) return;
final String[] loadedLibraries = loadedLibraries();
final String libawtName = isMacOS ? "libawt.dylib" : "libawt.so";
final String bootPath = System.getProperty("sun.boot.library.path");
final Path basePath = bootPath != null
? Path.of(bootPath)
: getBasePath(libawtName, loadedLibraries);
if (basePath == null) {
// Nothing to check if the native code failed to report where libawt is
return;
}
final String libraryPathEnvVarName = isMacOS
? "DYLD_LIBRARY_PATH"
: "LD_LIBRARY_PATH";
final Set<String> ourLibraries = isMacOS
? Set.of("libawt_lwawt.dylib", "libfontmanager.dylib", "libfreetype.dylib")
: Set.of("libawt_xawt.so", "libfontmanager.so");
boolean warnedAlready = false;
for (String pathName: loadedLibraries) {
if (pathName == null)
continue;
final Path libPath = Path.of(pathName);
final Path libFileName = libPath.getFileName();
if (ourLibraries.contains(libFileName.toString())) {
final Path libDirectory = libPath.getParent();
if (!basePath.equals(libDirectory)) {
Holder.errorLog.severe("Library '" + libFileName +
"' loaded from '" + libDirectory + "' instead of '" +
basePath + "'");
if (!warnedAlready) {
warnedAlready = true;
final String libraryPathOverride = System.getenv(libraryPathEnvVarName);
if (libraryPathOverride != null) {
Holder.errorLog.severe("Note: " + libraryPathEnvVarName +
" is set to '" + libraryPathOverride + "'");
}
}
}
}
}
}
private static Path getBasePath(String libawtName, String[] libs) {
Path basePath = null;
for (String path: libs) {
if (path.endsWith(libawtName)) {
basePath = Path.of(path).getParent().normalize();
}
}
return basePath;
}
private static native String[] loadedLibraries();
}

View File

@@ -33,6 +33,10 @@
#include "jni_util.h"
#include <stdlib.h>
#include <string.h>
#include <link.h>
/*
* This file contains stubs for JNI field and method id initializers
* which are used in the win32 awt.
@@ -91,3 +95,72 @@ Java_java_awt_event_MouseEvent_initIDs
(JNIEnv *env, jclass clazz)
{
}
struct shared_libs {
int count;
int index;
char ** names;
};
static int
dl_iterate_callback
(struct dl_phdr_info *info, size_t size, void *data)
{
struct shared_libs *libs = (struct shared_libs*)data;
if (libs->names == NULL) {
libs->count++;
} else {
// The number of libraries may have grown since the last time we asked.
if (libs->index < libs->count) {
libs->names[libs->index++] = strdup(info->dlpi_name);
}
}
return 0;
}
static jarray
convert_to_java_array
(JNIEnv *env, struct shared_libs* libs)
{
jclass stringClazz = (*env)->FindClass(env, "java/lang/String");
CHECK_NULL_RETURN(stringClazz, NULL);
jarray libsArray = (*env)->NewObjectArray(env, libs->count, stringClazz, NULL);
JNU_CHECK_EXCEPTION_RETURN(env, NULL);
for (uint32_t i = 0; i < libs->count; i++) {
const char * name = libs->names[i];
if (name) {
jstring jName = (*env)->NewStringUTF(env, name);
JNU_CHECK_EXCEPTION_RETURN(env, NULL);
(*env)->SetObjectArrayElement(env, libsArray, i, jName);
JNU_CHECK_EXCEPTION_RETURN(env, NULL);
}
}
return libsArray;
}
JNIEXPORT jarray JNICALL
Java_sun_font_FontManagerNativeLibrary_loadedLibraries
(JNIEnv *env, jclass cls)
{
struct shared_libs libs = {0, 0, NULL};
dl_iterate_phdr(&dl_iterate_callback, &libs);
if (libs.count <= 0) {
return NULL;
}
libs.names = (char **) calloc(libs.count, sizeof(libs.names[0]));
CHECK_NULL_RETURN(libs.names, NULL);
dl_iterate_phdr(&dl_iterate_callback, &libs);
jarray libsArray = convert_to_java_array(env, &libs);
for (uint32_t i = 0; i < libs.count; i++) {
free(libs.names[i]);
}
free(libs.names);
return libsArray;
}

View File

@@ -0,0 +1,104 @@
/*
* Copyright (c) 2022, JetBrains s.r.o.. 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 Verifies that a warning is issued when certain bundled libraries
* are loaded NOT from 'test.jdk'.
* @requires os.family == "mac"
* @library /test/lib
* @run main LibrariesCheck
*/
import java.awt.Robot;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.JLabel;
import java.nio.file.Path;
import java.nio.file.Files;
import java.util.Map;
import jdk.test.lib.process.ProcessTools;
import jdk.test.lib.process.OutputAnalyzer;
import jdk.test.lib.Platform;
public class LibrariesCheck {
public static void main(String[] args) throws Exception {
if (args.length > 0 && args[0].equals("runtest")) {
LibrariesCheck test = new LibrariesCheck();
} else {
// Run the test with DYLD_LIBRARY_PATH pointing to a directory
// with a substitute libfontmanager. JBR is supposed to detect this
// and complain with a log message.
String dynamicLibPathEnvVarName = "DYLD_LIBRARY_PATH";
Path tempDir = Files.createTempDirectory(LibrariesCheck.class.getName());
String testLibName = System.mapLibraryName("fontmanager");
Path testLibPath = tempDir.resolve(testLibName);
try {
Path jdkLibPath = Platform.libDir();
Files.copy(jdkLibPath.resolve(testLibName), testLibPath);
ProcessBuilder pb = ProcessTools.createTestJvm(
LibrariesCheck.class.getName(),
"runtest");
Map<String, String> env = pb.environment();
env.put(dynamicLibPathEnvVarName, tempDir.toString());
OutputAnalyzer oa = ProcessTools.executeProcess(pb);
oa.shouldContain("SEVERE").shouldContain(testLibName)
.shouldContain(dynamicLibPathEnvVarName);
} finally {
Files.delete(testLibPath);
Files.delete(tempDir);
}
}
}
JFrame frame;
LibrariesCheck() throws Exception {
try {
SwingUtilities.invokeAndWait(() -> {
frame = new JFrame("LibrariesCheck");
JPanel panel = new JPanel();
panel.add(new JLabel("some text"));
frame.getContentPane().add(panel);
frame.pack();
frame.setVisible(true);
});
Robot robot = new Robot();
robot.waitForIdle();
SwingUtilities.invokeAndWait(() -> {
frame.dispose();
});
} finally {
if (frame != null) {
frame.dispose();
}
}
}
}