8370216: Fix of new File().exists() causes undefined behavior in libraries

This commit is contained in:
Brian Burkhalter 2025-11-04 10:36:52 -08:00
parent 4c6af03f81
commit 0544b64ba4
4 changed files with 151 additions and 50 deletions

View File

@ -32,8 +32,15 @@ import java.lang.annotation.Native;
*/
abstract class FileSystem {
/* -- Legacy empty path behavior -- */
/* -- Current Working Directory --*/
private static final String FAIL_IF_EMPTY_PATH_PROPERTY =
"jdk.io.File.failIfEmptyPath";
protected static final boolean FAIL_IF_EMPTY_PATH =
Boolean.getBoolean(FAIL_IF_EMPTY_PATH_PROPERTY);
/* -- Current Working Directory -- */
/* lazy initialization of CWD object */
private static class CurrentWorkingDirectoryHolder {

View File

@ -34,10 +34,16 @@ final class UnixFileSystem extends FileSystem {
private final String userDir;
private String getPathForSysCalls(String path) {
if (FAIL_IF_EMPTY_PATH)
return path;
return path.isEmpty() ? getCWD().getPath() : path;
}
private File getFileForSysCalls(File file) {
if (FAIL_IF_EMPTY_PATH)
return file;
return file.getPath().isEmpty() ? getCWD() : file;
}

View File

@ -83,10 +83,16 @@ final class WinNTFileSystem extends FileSystem {
}
private String getPathForWin32Calls(String path) {
if (FAIL_IF_EMPTY_PATH)
return path;
return (path != null && path.isEmpty()) ? getCWD().getPath() : path;
}
private File getFileForWin32Calls(File file) {
if (FAIL_IF_EMPTY_PATH)
return file;
return file.getPath().isEmpty() ? getCWD() : file;
}

View File

@ -22,9 +22,10 @@
*/
/* @test
* @bug 4842706 8024695 8361587 8362429
* @bug 4842706 8024695 8361587 8362429 8370216
* @summary Test some file operations with empty path
* @run junit EmptyPath
* @run junit/othervm -Djdk.io.File.failIfEmptyPath=true EmptyPath
*/
import java.io.File;
@ -47,6 +48,7 @@ import java.util.stream.Collectors;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.condition.DisabledIf;
import org.junit.jupiter.api.condition.DisabledOnOs;
import org.junit.jupiter.api.condition.OS;
@ -55,28 +57,35 @@ import static org.junit.jupiter.api.Assertions.*;
public class EmptyPath {
private static final String EMPTY_STRING = "";
static boolean failIfEmptyPath;
static File f;
static Path p;
@BeforeAll
public static void init() {
failIfEmptyPath = Boolean.getBoolean("jdk.io.File.failIfEmptyPath");
f = new File(EMPTY_STRING);
p = Path.of(EMPTY_STRING);
}
private boolean failIfEmptyPath() {
return failIfEmptyPath;
}
@Test
public void canExecute() {
assertTrue(f.canExecute());
assertNotEquals(failIfEmptyPath, f.canExecute());
}
@Test
public void canRead() {
assertTrue(f.canRead());
assertNotEquals(failIfEmptyPath, f.canRead());
}
@Test
public void canWrite() {
assertTrue(f.canWrite());
assertNotEquals(failIfEmptyPath, f.canWrite());
}
@Test
@ -107,7 +116,7 @@ public class EmptyPath {
@Test
public void exists() {
assertTrue(f.exists());
assertNotEquals(failIfEmptyPath, f.exists());
}
@Test
@ -142,8 +151,12 @@ public class EmptyPath {
@Test
public void getFreeSpace() throws IOException {
FileStore fs = Files.getFileStore(f.toPath());
checkSpace(fs.getUnallocatedSpace(), f.getFreeSpace());
if (failIfEmptyPath)
assertEquals(0, f.getFreeSpace());
else {
FileStore fs = Files.getFileStore(f.toPath());
checkSpace(fs.getUnallocatedSpace(), f.getFreeSpace());
}
}
@Test
@ -168,14 +181,22 @@ public class EmptyPath {
@Test
public void getTotalSpace() throws IOException {
FileStore fs = Files.getFileStore(f.toPath());
checkSpace(fs.getTotalSpace(), f.getTotalSpace());
if (failIfEmptyPath)
assertEquals(0, f.getTotalSpace());
else {
FileStore fs = Files.getFileStore(f.toPath());
checkSpace(fs.getTotalSpace(), f.getTotalSpace());
}
}
@Test
public void getUsableSpace() throws IOException {
FileStore fs = Files.getFileStore(f.toPath());
checkSpace(fs.getUsableSpace(), f.getUsableSpace());
if (failIfEmptyPath)
assertEquals(0, f.getTotalSpace());
else {
FileStore fs = Files.getFileStore(f.toPath());
checkSpace(fs.getUsableSpace(), f.getUsableSpace());
}
}
@Test
@ -190,7 +211,7 @@ public class EmptyPath {
@Test
public void isDirectory() {
assertTrue(f.isDirectory());
assertNotEquals(failIfEmptyPath, f.isDirectory());
}
@Test
@ -205,22 +226,34 @@ public class EmptyPath {
@Test
public void lastModified() {
assertTrue(f.lastModified() > 0);
if (failIfEmptyPath)
assertEquals(0, f.lastModified());
else
assertTrue(f.lastModified() > 0);
}
@Test
public void length() throws IOException {
assertEquals(Files.size(f.toPath()), f.length());
if (failIfEmptyPath)
assertEquals(0, f.length());
else
assertEquals(Files.size(f.toPath()), f.length());
}
@Test
public void list() throws IOException {
list(f.list());
if (failIfEmptyPath)
assertNull(f.list());
else
list(f.list());
}
@Test
public void listFilenameFilter() throws IOException {
list(f.list((FilenameFilter)null));
if (failIfEmptyPath)
assertNull(f.list((FilenameFilter)null));
else
list(f.list((FilenameFilter)null));
}
private void list(String[] files) throws IOException {
@ -233,7 +266,10 @@ public class EmptyPath {
@Test
public void listFiles() throws IOException {
listFiles(x -> x.listFiles());
if (failIfEmptyPath)
assertNull(f.listFiles());
else
listFiles(x -> x.listFiles());
}
@Test
@ -241,12 +277,18 @@ public class EmptyPath {
FileFilter ff = new FileFilter() {
public boolean accept(File pathname) { return true; }
};
listFiles(x -> x.listFiles(ff));
if (failIfEmptyPath)
assertNull(f.listFiles(ff));
else
listFiles(x -> x.listFiles(ff));
}
@Test
public void listFilesNullFileFilter() throws IOException {
listFiles(x -> x.listFiles((FileFilter)null));
if (failIfEmptyPath)
assertNull(f.listFiles((FileFilter)null));
else
listFiles(x -> x.listFiles((FileFilter)null));
}
@Test
@ -254,12 +296,18 @@ public class EmptyPath {
FilenameFilter fnf = new FilenameFilter() {
public boolean accept(File dir, String name) { return true; }
};
listFiles(x -> x.listFiles(fnf));
if (failIfEmptyPath)
assertNull(f.listFiles(fnf));
else
listFiles(x -> x.listFiles(fnf));
}
@Test
public void listFilesNullFilenameFilter() throws IOException {
listFiles(x -> x.listFiles((FilenameFilter)null));
if (failIfEmptyPath)
assertNull(f.listFiles((FilenameFilter)null));
else
listFiles(x -> x.listFiles((FilenameFilter)null));
}
private void listFiles(Function<File,File[]> func) throws IOException {
@ -317,13 +365,19 @@ public class EmptyPath {
public void setLastModified() {
long t0 = f.lastModified();
long t = System.currentTimeMillis();
try {
assertTrue(f.setLastModified(t));
assertEquals(t, f.lastModified());
assertTrue(f.setLastModified(t0));
assertEquals(t0, f.lastModified());
} finally {
f.setLastModified(t0);
if (failIfEmptyPath) {
assertEquals(0, t0);
assertFalse(f.setLastModified(t));
assertEquals(0, f.lastModified());
} else {
try {
assertTrue(f.setLastModified(t));
assertEquals(t, f.lastModified());
assertTrue(f.setLastModified(t0));
assertEquals(t0, f.lastModified());
} finally {
f.setLastModified(t0);
}
}
}
@ -334,45 +388,72 @@ public class EmptyPath {
@Test
@DisabledOnOs({OS.WINDOWS})
public void setReadable() {
assertTrue(f.canRead());
try {
assertTrue(f.setReadable(false));
if (failIfEmptyPath) {
assertFalse(f.canRead());
assertTrue(f.setReadable(true));
assertFalse(f.setReadable(false));
assertFalse(f.canRead());
assertFalse(f.setReadable(true));
assertFalse(f.canRead());
} else {
assertTrue(f.canRead());
} finally {
f.setReadable(true);
try {
assertTrue(f.setReadable(false));
assertFalse(f.canRead());
assertTrue(f.setReadable(true));
assertTrue(f.canRead());
} finally {
f.setReadable(true);
}
}
}
@Test
@DisabledOnOs({OS.WINDOWS})
public void setReadOnly() {
assertTrue(f.canExecute());
assertTrue(f.canRead());
assertTrue(f.canWrite());
try {
assertTrue(f.setReadOnly());
assertTrue(f.canRead());
if (failIfEmptyPath) {
assertFalse(f.canExecute());
assertFalse(f.canRead());
assertFalse(f.canWrite());
assertTrue(f.setWritable(true, true));
assertFalse(f.setReadOnly());
assertFalse(f.canRead());
assertFalse(f.canWrite());
assertFalse(f.setWritable(true, true));
assertFalse(f.canWrite());
} else {
assertTrue(f.canExecute());
assertTrue(f.canRead());
assertTrue(f.canWrite());
} finally {
f.setWritable(true, true);
try {
assertTrue(f.setReadOnly());
assertTrue(f.canRead());
assertFalse(f.canWrite());
assertTrue(f.setWritable(true, true));
assertTrue(f.canWrite());
} finally {
f.setWritable(true, true);
}
}
}
@Test
@DisabledOnOs({OS.WINDOWS})
public void setWritable() {
assertTrue(f.canWrite());
try {
assertTrue(f.setWritable(false, true));
if (failIfEmptyPath) {
assertFalse(f.canWrite());
assertTrue(f.setWritable(true, true));
assertFalse(f.setWritable(false, true));
assertFalse(f.canWrite());
assertFalse(f.setWritable(true, true));
assertFalse(f.canWrite());
} else {
assertTrue(f.canWrite());
} finally {
f.setWritable(true, true);
try {
assertTrue(f.setWritable(false, true));
assertFalse(f.canWrite());
assertTrue(f.setWritable(true, true));
assertTrue(f.canWrite());
} finally {
f.setWritable(true, true);
}
}
}
@ -391,6 +472,7 @@ public class EmptyPath {
assertEquals(f.toPath().toUri(), f.toURI());
}
@DisabledIf("failIfEmptyPath")
@Test
public void toURL() throws MalformedURLException {
assertEquals(f.toPath().toUri().toURL(), f.toURL());