1 /* 2 * Copyright (c) 2013, 2014 Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 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 }