Add explicit message when noexec
prevents library loading.
Motivation: Docker's `--tmpfs` flag mounts the temp volume with `noexec` by default, resulting in an UnsatisfiedLinkError. While this is good security practice, it is a surprising failure from a seemingly innocuous flag. Modifications: Add a best-effort attempt in `NativeLibraryLoader` to detect when temp files beng loaded cannot be executed even when execution permissions are set, often because the `noexec` flag is set on the volume. Requires numerous additional exclusions to the Animal Sniffer config for Java7 POSIX permissions manipulation. Result: Fixes [#6678].
This commit is contained in:
parent
1f0d47dee7
commit
ed5fcbb773
@ -18,8 +18,8 @@ package io.netty.util.internal;
|
||||
import io.netty.util.internal.logging.InternalLogger;
|
||||
import io.netty.util.internal.logging.InternalLoggerFactory;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.Closeable;
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
@ -27,10 +27,14 @@ import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.lang.reflect.Method;
|
||||
import java.net.URL;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.attribute.PosixFilePermission;
|
||||
import java.security.AccessController;
|
||||
import java.security.PrivilegedAction;
|
||||
import java.util.Arrays;
|
||||
import java.util.EnumSet;
|
||||
import java.util.Locale;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Helper class to load JNI resources.
|
||||
@ -227,6 +231,20 @@ public final class NativeLibraryLoader {
|
||||
out = null;
|
||||
|
||||
loadLibrary(loader, tmpFile.getPath(), true);
|
||||
} catch (UnsatisfiedLinkError e) {
|
||||
try {
|
||||
if (tmpFile != null && tmpFile.isFile() && tmpFile.canRead() &&
|
||||
!NoexecVolumeDetector.canExecuteExecutable(tmpFile)) {
|
||||
logger.info("{} exists but cannot be executed even when execute permissions set; " +
|
||||
"check volume for \"noexec\" flag; use -Dio.netty.native.workdir=[path] " +
|
||||
"to set native working directory separately.",
|
||||
tmpFile.getPath());
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
logger.debug("Error checking if {} is on a file store mounted with noexec", tmpFile, t);
|
||||
}
|
||||
// Re-throw to fail the load
|
||||
throw e;
|
||||
} catch (Exception e) {
|
||||
throw (UnsatisfiedLinkError) new UnsatisfiedLinkError(
|
||||
"could not load a native library: " + name).initCause(e);
|
||||
@ -371,4 +389,42 @@ public final class NativeLibraryLoader {
|
||||
private NativeLibraryLoader() {
|
||||
// Utility
|
||||
}
|
||||
|
||||
private static final class NoexecVolumeDetector {
|
||||
|
||||
private static boolean canExecuteExecutable(File file) throws IOException {
|
||||
if (PlatformDependent.javaVersion() < 7) {
|
||||
// Pre-JDK7, the Java API did not directly support POSIX permissions; instead of implementing a custom
|
||||
// work-around, assume true, which disables the check.
|
||||
return true;
|
||||
}
|
||||
|
||||
// If we can already execute, there is nothing to do.
|
||||
if (file.canExecute()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// On volumes, with noexec set, even files with the executable POSIX permissions will fail to execute.
|
||||
// The File#canExecute() method honors this behavior, probaby via parsing the noexec flag when initializing
|
||||
// the UnixFileStore, though the flag is not exposed via a public API. To find out if library is being
|
||||
// loaded off a volume with noexec, confirm or add executalbe permissions, then check File#canExecute().
|
||||
|
||||
Set<PosixFilePermission> existingFilePermissions = Files.getPosixFilePermissions(file.toPath());
|
||||
Set<PosixFilePermission> executePermissions = EnumSet.of(PosixFilePermission.OWNER_EXECUTE,
|
||||
PosixFilePermission.GROUP_EXECUTE,
|
||||
PosixFilePermission.OTHERS_EXECUTE);
|
||||
if (existingFilePermissions.containsAll(executePermissions)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Set<PosixFilePermission> newPermissions = EnumSet.copyOf(existingFilePermissions);
|
||||
newPermissions.addAll(executePermissions);
|
||||
Files.setPosixFilePermissions(file.toPath(), newPermissions);
|
||||
return file.canExecute();
|
||||
}
|
||||
|
||||
private NoexecVolumeDetector() {
|
||||
// Utility
|
||||
}
|
||||
}
|
||||
}
|
||||
|
7
pom.xml
7
pom.xml
@ -709,6 +709,13 @@
|
||||
|
||||
<!-- Resolver -->
|
||||
<ignore>java.net.InetAddress</ignore>
|
||||
|
||||
<!-- NoexecVolumeDetector -->
|
||||
<ignore>java.nio.file.attribute.PosixFilePermission</ignore>
|
||||
<ignore>java.nio.file.Files</ignore>
|
||||
<ignore>java.nio.file.LinkOption</ignore>
|
||||
<ignore>java.nio.file.Path</ignore>
|
||||
<ignore>java.io.File</ignore>
|
||||
</ignores>
|
||||
</configuration>
|
||||
<executions>
|
||||
|
Loading…
x
Reference in New Issue
Block a user