undefect. CWE-407 — 63 sites patched across 27 ecosystems
Authors: russell@unturf.com · brackishbert@gmail.com · foxhop.net · TimeHexOn.com Patches, unit tests, benchmarks, whitepaper, and outreach briefs. Public domain — no copyright claimed. Use freely.
This commit is contained in:
commit
0a580b313d
70422 changed files with 17213626 additions and 0 deletions
416
test/jdk/java/io/FileDescriptor/Sharing.java
Normal file
416
test/jdk/java/io/FileDescriptor/Sharing.java
Normal file
|
|
@ -0,0 +1,416 @@
|
|||
/*
|
||||
* Copyright (c) 2011, 2026, 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 7105952 6322678 7082769
|
||||
* @summary Improve finalisation for FileInputStream/FileOutputStream/RandomAccessFile
|
||||
* @run main/othervm Sharing
|
||||
*/
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileDescriptor;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.io.RandomAccessFile;
|
||||
import java.io.Writer;
|
||||
import java.nio.channels.FileChannel;
|
||||
import java.nio.channels.FileLock;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
|
||||
public class Sharing {
|
||||
|
||||
static final int numFiles = 10;
|
||||
static volatile boolean fail;
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
TestFinalizer();
|
||||
TestMultipleFD();
|
||||
TestIsValid();
|
||||
MultiThreadedFD();
|
||||
TestCloseAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* Finalizer shouldn't discard a file descriptor until all streams have
|
||||
* finished with it.
|
||||
*/
|
||||
private static void TestFinalizer() throws Exception {
|
||||
FileDescriptor fd = null;
|
||||
File tempFile = new File("TestFinalizer1.txt");
|
||||
tempFile.deleteOnExit();
|
||||
try (Writer writer = new FileWriter(tempFile)) {
|
||||
for (int i=0; i<5; i++) {
|
||||
writer.write("test file content test file content");
|
||||
}
|
||||
}
|
||||
|
||||
FileInputStream fis1 = new FileInputStream(tempFile);
|
||||
fd = fis1.getFD();
|
||||
// Create a new FIS based on the existing FD (so the two FIS's share the same native fd)
|
||||
try (FileInputStream fis2 = new FileInputStream(fd)) {
|
||||
// allow fis1 to be gc'ed
|
||||
fis1 = null;
|
||||
int ret = 0;
|
||||
while(ret >= 0) {
|
||||
// encourage gc
|
||||
System.gc();
|
||||
// read from fis2 - when fis1 is gc'ed and finalizer is run, read will fail
|
||||
System.err.print(".");
|
||||
ret = fis2.read();
|
||||
}
|
||||
}
|
||||
|
||||
// variation of above. Use RandomAccessFile to obtain a filedescriptor
|
||||
File testFinalizerFile = new File("TestFinalizer");
|
||||
RandomAccessFile raf = new RandomAccessFile(testFinalizerFile, "rw");
|
||||
raf.writeBytes("test file content test file content");
|
||||
raf.seek(0L);
|
||||
fd = raf.getFD();
|
||||
try (FileInputStream fis3 = new FileInputStream(fd)) {
|
||||
// allow raf to be gc'ed
|
||||
raf = null;
|
||||
int ret = 0;
|
||||
while (ret >= 0) {
|
||||
// encourage gc
|
||||
System.gc();
|
||||
/*
|
||||
* read from fis3 - when raf is gc'ed and finalizer is run,
|
||||
* fd should still be valid.
|
||||
*/
|
||||
System.err.print(".");
|
||||
ret = fis3.read();
|
||||
}
|
||||
} finally {
|
||||
testFinalizerFile.delete();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Exercise FileDispatcher close()/preClose()
|
||||
*/
|
||||
private static void TestMultipleFD() throws Exception {
|
||||
RandomAccessFile raf = null;
|
||||
FileOutputStream fos = null;
|
||||
FileInputStream fis = null;
|
||||
FileChannel fc = null;
|
||||
FileLock fileLock = null;
|
||||
|
||||
File test1 = new File("test1");
|
||||
try {
|
||||
raf = new RandomAccessFile(test1, "rw");
|
||||
fos = new FileOutputStream(raf.getFD());
|
||||
fis = new FileInputStream(raf.getFD());
|
||||
fc = raf.getChannel();
|
||||
fileLock = fc.lock();
|
||||
raf.setLength(0L);
|
||||
fos.flush();
|
||||
fos.write("TEST".getBytes());
|
||||
} finally {
|
||||
if (fileLock != null) fileLock.release();
|
||||
if (fis != null) fis.close();
|
||||
if (fos != null) fos.close();
|
||||
if (raf != null) raf.close();
|
||||
test1.delete();
|
||||
}
|
||||
|
||||
/*
|
||||
* Close out in different order to ensure FD is not
|
||||
* closed out too early
|
||||
*/
|
||||
File test2 = new File("test2");
|
||||
try {
|
||||
raf = new RandomAccessFile(test2, "rw");
|
||||
fos = new FileOutputStream(raf.getFD());
|
||||
fis = new FileInputStream(raf.getFD());
|
||||
fc = raf.getChannel();
|
||||
fileLock = fc.lock();
|
||||
raf.setLength(0L);
|
||||
fos.flush();
|
||||
fos.write("TEST".getBytes());
|
||||
} finally {
|
||||
if (fileLock != null) fileLock.release();
|
||||
if (raf != null) raf.close();
|
||||
if (fos != null) fos.close();
|
||||
if (fis != null) fis.close();
|
||||
test2.delete();
|
||||
}
|
||||
|
||||
// one more time, fos first this time
|
||||
File test3 = new File("test3");
|
||||
try {
|
||||
raf = new RandomAccessFile(test3, "rw");
|
||||
fos = new FileOutputStream(raf.getFD());
|
||||
fis = new FileInputStream(raf.getFD());
|
||||
fc = raf.getChannel();
|
||||
fileLock = fc.lock();
|
||||
raf.setLength(0L);
|
||||
fos.flush();
|
||||
fos.write("TEST".getBytes());
|
||||
} finally {
|
||||
if (fileLock != null) fileLock.release();
|
||||
if (fos != null) fos.close();
|
||||
if (raf != null) raf.close();
|
||||
if (fis != null) fis.close();
|
||||
test3.delete();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Similar to TestMultipleFD() but this time we
|
||||
* just get and use FileDescriptor.valid() for testing.
|
||||
*/
|
||||
private static void TestIsValid() throws Exception {
|
||||
FileDescriptor fd = null;
|
||||
RandomAccessFile raf = null;
|
||||
FileOutputStream fos = null;
|
||||
FileInputStream fis = null;
|
||||
FileChannel fc = null;
|
||||
|
||||
File test1 = new File("test1");
|
||||
try {
|
||||
raf = new RandomAccessFile(test1, "rw");
|
||||
fd = raf.getFD();
|
||||
fos = new FileOutputStream(fd);
|
||||
fis = new FileInputStream(fd);
|
||||
} finally {
|
||||
try {
|
||||
if (fis != null) fis.close();
|
||||
if (fd.valid()) {
|
||||
throw new RuntimeException("[FIS close()] FileDescriptor shouldn't be valid");
|
||||
}
|
||||
if (fos != null) fos.close();
|
||||
if (raf != null) raf.close();
|
||||
} finally {
|
||||
test1.delete();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Close out in different order to ensure FD is
|
||||
* closed correctly.
|
||||
*/
|
||||
File test2 = new File("test2");
|
||||
try {
|
||||
raf = new RandomAccessFile(test2, "rw");
|
||||
fd = raf.getFD();
|
||||
fos = new FileOutputStream(fd);
|
||||
fis = new FileInputStream(fd);
|
||||
} finally {
|
||||
try {
|
||||
if (raf != null) raf.close();
|
||||
if (fd.valid()) {
|
||||
throw new RuntimeException("[RAF close()] FileDescriptor shouldn't be valid");
|
||||
}
|
||||
if (fos != null) fos.close();
|
||||
if (fis != null) fis.close();
|
||||
} finally {
|
||||
test2.delete();
|
||||
}
|
||||
}
|
||||
|
||||
// one more time, fos first this time
|
||||
File test3 = new File("test3");
|
||||
try {
|
||||
raf = new RandomAccessFile(test3, "rw");
|
||||
fd = raf.getFD();
|
||||
fos = new FileOutputStream(fd);
|
||||
fis = new FileInputStream(fd);
|
||||
} finally {
|
||||
try {
|
||||
if (fos != null) fos.close();
|
||||
if (fd.valid()) {
|
||||
throw new RuntimeException("[FOS close()] FileDescriptor shouldn't be valid");
|
||||
}
|
||||
if (raf != null) raf.close();
|
||||
if (fis != null) fis.close();
|
||||
} finally {
|
||||
test3.delete();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test concurrent access to the same FileDescriptor
|
||||
*/
|
||||
private static void MultiThreadedFD() throws Exception {
|
||||
RandomAccessFile raf = null;
|
||||
FileDescriptor fd = null;
|
||||
int numThreads = 2;
|
||||
CountDownLatch done = new CountDownLatch(numThreads);
|
||||
OpenClose[] fileOpenClose = new OpenClose[numThreads];
|
||||
File MultipleThreadedFD = new File("MultipleThreadedFD");
|
||||
try {
|
||||
raf = new RandomAccessFile(MultipleThreadedFD, "rw");
|
||||
fd = raf.getFD();
|
||||
for(int count=0;count<numThreads;count++) {
|
||||
fileOpenClose[count] = new OpenClose(fd, done);
|
||||
fileOpenClose[count].start();
|
||||
}
|
||||
done.await();
|
||||
} finally {
|
||||
try {
|
||||
if(raf != null) raf.close();
|
||||
// fd should now no longer be valid
|
||||
if(fd.valid()) {
|
||||
throw new RuntimeException("FileDescriptor should not be valid");
|
||||
}
|
||||
// OpenClose thread tests failed
|
||||
if(fail) {
|
||||
throw new RuntimeException("OpenClose thread tests failed.");
|
||||
}
|
||||
} finally {
|
||||
MultipleThreadedFD.delete();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test closeAll handling in FileDescriptor
|
||||
*/
|
||||
private static void TestCloseAll() throws Exception {
|
||||
File testFile = new File("test");
|
||||
testFile.deleteOnExit();
|
||||
RandomAccessFile raf = new RandomAccessFile(testFile, "rw");
|
||||
FileInputStream fis = new FileInputStream(raf.getFD());
|
||||
fis.close();
|
||||
if (raf.getFD().valid()) {
|
||||
throw new RuntimeException("FD should not be valid.");
|
||||
}
|
||||
|
||||
// Test the suppressed exception handling - FileInputStream
|
||||
|
||||
raf = new RandomAccessFile(testFile, "rw");
|
||||
fis = new FileInputStream(raf.getFD());
|
||||
BadFileInputStream bfis1 = new BadFileInputStream(raf.getFD());
|
||||
BadFileInputStream bfis2 = new BadFileInputStream(raf.getFD());
|
||||
BadFileInputStream bfis3 = new BadFileInputStream(raf.getFD());
|
||||
// extra test - set bfis3 to null
|
||||
bfis3 = null;
|
||||
try {
|
||||
fis.close();
|
||||
} catch (IOException ioe) {
|
||||
ioe.printStackTrace();
|
||||
if (ioe.getSuppressed().length != 2) {
|
||||
throw new RuntimeException("[FIS]Incorrect number of suppressed " +
|
||||
"exceptions received : " + ioe.getSuppressed().length);
|
||||
}
|
||||
}
|
||||
if (raf.getFD().valid()) {
|
||||
// we should still have closed the FD
|
||||
// even with the exception.
|
||||
throw new RuntimeException("[FIS]TestCloseAll : FD still valid.");
|
||||
}
|
||||
|
||||
// Now test with FileOutputStream
|
||||
|
||||
raf = new RandomAccessFile(testFile, "rw");
|
||||
FileOutputStream fos = new FileOutputStream(raf.getFD());
|
||||
BadFileOutputStream bfos1 = new BadFileOutputStream(raf.getFD());
|
||||
BadFileOutputStream bfos2 = new BadFileOutputStream(raf.getFD());
|
||||
BadFileOutputStream bfos3 = new BadFileOutputStream(raf.getFD());
|
||||
// extra test - set bfos3 to null
|
||||
bfos3 = null;
|
||||
try {
|
||||
fos.close();
|
||||
} catch (IOException ioe) {
|
||||
ioe.printStackTrace();
|
||||
if (ioe.getSuppressed().length != 2) {
|
||||
throw new RuntimeException("[FOS]Incorrect number of suppressed " +
|
||||
"exceptions received : " + ioe.getSuppressed().length);
|
||||
}
|
||||
}
|
||||
if (raf.getFD().valid()) {
|
||||
// we should still have closed the FD
|
||||
// even with the exception.
|
||||
throw new RuntimeException("[FOS]TestCloseAll : FD still valid.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A thread which will open and close a number of FileInputStreams and
|
||||
* FileOutputStreams referencing the same native file descriptor.
|
||||
*/
|
||||
private static class OpenClose extends Thread {
|
||||
private final FileDescriptor fd;
|
||||
private final CountDownLatch done;
|
||||
|
||||
OpenClose(FileDescriptor filedescriptor, CountDownLatch done) {
|
||||
this.fd = filedescriptor;
|
||||
this.done = done;
|
||||
}
|
||||
|
||||
public void run() {
|
||||
FileInputStream[] fisArray = new FileInputStream[numFiles];
|
||||
FileOutputStream[] fosArray = new FileOutputStream[numFiles];
|
||||
|
||||
try {
|
||||
for(int i=0;i<numFiles;i++) {
|
||||
fisArray[i] = new FileInputStream(fd);
|
||||
fosArray[i] = new FileOutputStream(fd);
|
||||
}
|
||||
|
||||
// Now close out
|
||||
for(int i=0;i<numFiles;i++) {
|
||||
fisArray[i].close();
|
||||
fosArray[i].close();
|
||||
}
|
||||
|
||||
} catch(IOException ioe) {
|
||||
System.err.println("OpenClose encountered IO issue :" + ioe);
|
||||
fail = true;
|
||||
} finally {
|
||||
if (fd.valid()) { // fd should not be valid after first close() call
|
||||
System.err.println("OpenClose: FileDescriptor shouldn't be valid");
|
||||
fail = true;
|
||||
}
|
||||
done.countDown();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static class BadFileInputStream extends FileInputStream {
|
||||
|
||||
BadFileInputStream(FileDescriptor fd) {
|
||||
super(fd);
|
||||
}
|
||||
|
||||
public void close() throws IOException {
|
||||
throw new IOException("Bad close operation");
|
||||
}
|
||||
}
|
||||
|
||||
private static class BadFileOutputStream extends FileOutputStream {
|
||||
|
||||
BadFileOutputStream(FileDescriptor fd) {
|
||||
super(fd);
|
||||
}
|
||||
|
||||
public void close() throws IOException {
|
||||
throw new IOException("Bad close operation");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue