16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 24 /** 25 * @test 26 * @bug 8024521 27 * @summary Closing ProcessPipeInputStream at the time the process exits is racy 28 * and leads to data corruption. Run this test manually (as 29 * an ordinary java program) with -Xmx8M to repro bug 8024521. 30 * @run main/othervm -Xmx8M -Dtest.duration=2 CloseRace 31 */ 32 33 import java.io.*; 34 import java.util.ArrayList; 35 import java.util.List; 36 37 public class CloseRace { 38 private static final String BIG_FILE = "bigfile"; 39 40 private static final int[] procFDs = new int[6]; 41 42 /** default value sufficient to repro bug 8024521. */ 43 private static final int testDurationSeconds 44 = Integer.getInteger("test.duration", 600); 45 46 static boolean fdInUse(int i) { 47 return new File("/proc/self/fd/" + i).exists(); 48 } 49 50 static boolean[] procFDsInUse() { 51 boolean[] inUse = new boolean[procFDs.length]; 52 for (int i = 0; i < procFDs.length; i++) 53 inUse[i] = fdInUse(procFDs[i]); 54 return inUse; 55 } 56 57 static int count(boolean[] bits) { 58 int count = 0; 59 for (int i = 0; i < bits.length; i++) 60 count += bits[i] ? 1 : 0; 61 return count; 62 } 63 64 public static void main(String args[]) throws Exception { 65 if (!(new File("/proc/self/fd").isDirectory())) 66 return; 67 68 // Catch Errors from process reaper 69 Thread.setDefaultUncaughtExceptionHandler 70 ((t, e) -> { e.printStackTrace(); System.exit(1); }); 71 72 try (RandomAccessFile f = new RandomAccessFile(BIG_FILE, "rw")) { 73 f.setLength(Runtime.getRuntime().maxMemory()); // provoke OOME 74 } 75 76 for (int i = 0, j = 0; j < procFDs.length; i++) 77 if (!fdInUse(i)) 78 procFDs[j++] = i; 79 80 Thread[] threads = { 81 new Thread(new OpenLoop()), 82 new Thread(new ExecLoop()), 83 }; 84 for (Thread thread : threads) 85 thread.start(); 86 87 Thread.sleep(testDurationSeconds * 1000); 88 89 for (Thread thread : threads) 90 thread.interrupt(); 91 for (Thread thread : threads) 92 thread.join(); 93 } 94 95 static class OpenLoop implements Runnable { 96 public void run() { 97 while (!Thread.interrupted()) { 98 try { 99 // wait for ExecLoop to finish creating process 100 do {} while (count(procFDsInUse()) != 3); 101 List<InputStream> iss = new ArrayList<>(4); 102 103 // eat up three "holes" (closed ends of pipe fd pairs) 104 for (int i = 0; i < 3; i++) 105 iss.add(new FileInputStream(BIG_FILE)); 106 do {} while (count(procFDsInUse()) == procFDs.length); 107 // hopefully this will racily occupy empty fd slot 108 iss.add(new FileInputStream(BIG_FILE)); 109 Thread.sleep(1); // Widen race window 110 for (InputStream is : iss) 111 is.close(); 112 } catch (InterruptedException e) { 113 break; 114 } catch (Exception e) { 115 throw new Error(e); 116 } 117 } 118 } 119 } 120 121 static class ExecLoop implements Runnable { 122 public void run() { 123 ProcessBuilder builder = new ProcessBuilder("/bin/true"); 124 while (!Thread.interrupted()) { 125 try { 126 // wait for OpenLoop to finish 127 do {} while (count(procFDsInUse()) > 0); 128 Process process = builder.start(); 129 InputStream is = process.getInputStream(); 130 process.waitFor(); 131 is.close(); 132 } catch (InterruptedException e) { 133 break; 134 } catch (Exception e) { 135 throw new Error(e); 136 } 137 } 138 } 139 } 140 } | 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 24 /** 25 * @test 26 * @bug 8024521 27 * @summary Closing ProcessPipeInputStream at the time the process exits is racy 28 * and leads to data corruption. Run this test manually (as 29 * an ordinary java program) with -Xmx8M to repro bug 8024521. 30 * @run main/othervm -Xmx8M -Dtest.duration=2 CloseRace 31 */ 32 33 import java.io.*; 34 import java.util.ArrayList; 35 import java.util.List; 36 import java.util.Map; 37 import java.util.concurrent.CountDownLatch; 38 39 public class CloseRace { 40 private static final String BIG_FILE = "bigfile"; 41 42 private static final int[] procFDs = new int[6]; 43 44 /** default value sufficient to repro bug 8024521. */ 45 private static final int testDurationSeconds 46 = Integer.getInteger("test.duration", 600); 47 48 private static final CountDownLatch threadsStarted 49 = new CountDownLatch(2); 50 51 static boolean fdInUse(int i) { 52 return new File("/proc/self/fd/" + i).exists(); 53 } 54 55 static boolean[] procFDsInUse() { 56 boolean[] inUse = new boolean[procFDs.length]; 57 for (int i = 0; i < procFDs.length; i++) 58 inUse[i] = fdInUse(procFDs[i]); 59 return inUse; 60 } 61 62 static int count(boolean[] bits) { 63 int count = 0; 64 for (int i = 0; i < bits.length; i++) 65 count += bits[i] ? 1 : 0; 66 return count; 67 } 68 69 static void dumpAllStacks() { 70 System.err.println("Start of dump"); 71 final Map<Thread, StackTraceElement[]> allStackTraces 72 = Thread.getAllStackTraces(); 73 for (Thread thread : allStackTraces.keySet()) { 74 System.err.println("Thread " + thread.getName()); 75 for (StackTraceElement element : allStackTraces.get(thread)) 76 System.err.println("\t" + element); 77 } 78 System.err.println("End of dump"); 79 } 80 81 public static void main(String args[]) throws Exception { 82 if (!(new File("/proc/self/fd").isDirectory())) 83 return; 84 85 // Catch Errors from process reaper 86 Thread.setDefaultUncaughtExceptionHandler 87 ((t, e) -> { e.printStackTrace(); System.exit(1); }); 88 89 try (RandomAccessFile f = new RandomAccessFile(BIG_FILE, "rw")) { 90 f.setLength(Runtime.getRuntime().maxMemory()); // provoke OOME 91 } 92 93 for (int i = 0, j = 0; j < procFDs.length; i++) 94 if (!fdInUse(i)) 95 procFDs[j++] = i; 96 97 Thread[] threads = { 98 new Thread(new OpenLoop()), 99 new Thread(new ExecLoop()), 100 }; 101 for (Thread thread : threads) 102 thread.start(); 103 104 threadsStarted.await(); 105 Thread.sleep(testDurationSeconds * 1000); 106 107 for (Thread thread : threads) 108 thread.interrupt(); 109 for (Thread thread : threads) { 110 thread.join(10_000); 111 if (thread.isAlive()) { 112 dumpAllStacks(); 113 throw new Error("At least one child thread (" 114 + thread.getName() 115 + ") failed to finish gracefully"); 116 } 117 } 118 } 119 120 static class OpenLoop implements Runnable { 121 public void run() { 122 threadsStarted.countDown(); 123 while (!Thread.interrupted()) { 124 try { 125 // wait for ExecLoop to finish creating process 126 do { 127 if (Thread.interrupted()) 128 return; 129 } while (count(procFDsInUse()) != 3); 130 List<InputStream> iss = new ArrayList<>(4); 131 132 // eat up three "holes" (closed ends of pipe fd pairs) 133 for (int i = 0; i < 3; i++) 134 iss.add(new FileInputStream(BIG_FILE)); 135 do { 136 if (Thread.interrupted()) 137 return; 138 } while (count(procFDsInUse()) == procFDs.length); 139 // hopefully this will racily occupy empty fd slot 140 iss.add(new FileInputStream(BIG_FILE)); 141 Thread.sleep(1); // Widen race window 142 for (InputStream is : iss) 143 is.close(); 144 } catch (InterruptedException e) { 145 break; 146 } catch (Exception e) { 147 throw new Error(e); 148 } 149 } 150 } 151 } 152 153 static class ExecLoop implements Runnable { 154 public void run() { 155 threadsStarted.countDown(); 156 ProcessBuilder builder = new ProcessBuilder("/bin/true"); 157 while (!Thread.interrupted()) { 158 try { 159 // wait for OpenLoop to finish 160 do { 161 if (Thread.interrupted()) 162 return; 163 } while (count(procFDsInUse()) > 0); 164 Process process = builder.start(); 165 InputStream is = process.getInputStream(); 166 process.waitFor(); 167 is.close(); 168 } catch (InterruptedException e) { 169 break; 170 } catch (Exception e) { 171 throw new Error(e); 172 } 173 } 174 } 175 } 176 } |