Mercurial > hg > release > icedtea7-forest-2.6 > jdk
changeset 6572:139d3e3b62d4 jdk7u21-b04
8005943: (process) Improved Runtime.exec
Reviewed-by: alanb, ahgross
author | uta |
---|---|
date | Tue, 26 Feb 2013 15:58:40 +0400 |
parents | 292cce668342 |
children | 52c41d308415 |
files | src/share/classes/java/lang/ProcessBuilder.java src/windows/classes/java/lang/ProcessImpl.java |
diffstat | 2 files changed, 120 insertions(+), 24 deletions(-) [+] |
line wrap: on
line diff
--- a/src/share/classes/java/lang/ProcessBuilder.java Tue Feb 26 01:27:17 2013 +0400 +++ b/src/share/classes/java/lang/ProcessBuilder.java Tue Feb 26 15:58:40 2013 +0400 @@ -30,6 +30,7 @@ import java.io.InputStream; import java.io.OutputStream; import java.io.FileOutputStream; +import java.security.AccessControlException; import java.util.Arrays; import java.util.ArrayList; import java.util.List; @@ -1012,8 +1013,9 @@ String prog = cmdarray[0]; SecurityManager security = System.getSecurityManager(); - if (security != null) + if (security != null) { security.checkExec(prog); + } String dir = directory == null ? null : directory.toString(); @@ -1024,13 +1026,24 @@ redirects, redirectErrorStream); } catch (IOException e) { + String exceptionInfo = ": " + e.getMessage(); + Throwable cause = e; + if (security != null) { + // Can not disclose the fail reason for read-protected files. + try { + security.checkRead(prog); + } catch (AccessControlException ace) { + exceptionInfo = ""; + cause = ace; + } + } // It's much easier for us to create a high-quality error // message than the low-level C code which found the problem. throw new IOException( "Cannot run program \"" + prog + "\"" + (dir == null ? "" : " (in directory \"" + dir + "\")") - + ": " + e.getMessage(), - e); + + exceptionInfo, + cause); } } }
--- a/src/windows/classes/java/lang/ProcessImpl.java Tue Feb 26 01:27:17 2013 +0400 +++ b/src/windows/classes/java/lang/ProcessImpl.java Tue Feb 26 15:58:40 2013 +0400 @@ -144,6 +144,88 @@ } + // We guarantee the only command file execution for implicit [cmd.exe] run. + // http://technet.microsoft.com/en-us/library/bb490954.aspx + private static final char CMD_BAT_ESCAPE[] = {' ', '\t', '<', '>', '&', '|', '^'}; + private static final char WIN32_EXECUTABLE_ESCAPE[] = {' ', '\t', '<', '>'}; + + private static boolean isQuoted(boolean noQuotesInside, String arg, + String errorMessage) { + int lastPos = arg.length() - 1; + if (lastPos >=1 && arg.charAt(0) == '"' && arg.charAt(lastPos) == '"') { + // The argument has already been quoted. + if (noQuotesInside) { + if (arg.indexOf('"', 1) != lastPos) { + // There is ["] inside. + throw new IllegalArgumentException(errorMessage); + } + } + return true; + } + if (noQuotesInside) { + if (arg.indexOf('"') >= 0) { + // There is ["] inside. + throw new IllegalArgumentException(errorMessage); + } + } + return false; + } + + private static boolean needsEscaping(boolean isCmdFile, String arg) { + // Switch off MS heuristic for internal ["]. + // Please, use the explicit [cmd.exe] call + // if you need the internal ["]. + // Example: "cmd.exe", "/C", "Extended_MS_Syntax" + + // For [.exe] or [.com] file the unpaired/internal ["] + // in the argument is not a problem. + boolean argIsQuoted = isQuoted(isCmdFile, arg, + "Argument has embedded quote, use the explicit CMD.EXE call."); + + if (!argIsQuoted) { + char testEscape[] = isCmdFile + ? CMD_BAT_ESCAPE + : WIN32_EXECUTABLE_ESCAPE; + for (int i = 0; i < testEscape.length; ++i) { + if (arg.indexOf(testEscape[i]) >= 0) { + return true; + } + } + } + return false; + } + + private static String getExecutablePath(String path) + throws IOException + { + boolean pathIsQuoted = isQuoted(true, path, + "Executable name has embedded quote, split the arguments"); + + // Win32 CreateProcess requires path to be normalized + File fileToRun = new File(pathIsQuoted + ? path.substring(1, path.length() - 1) + : path); + + // From the [CreateProcess] function documentation: + // + // "If the file name does not contain an extension, .exe is appended. + // Therefore, if the file name extension is .com, this parameter + // must include the .com extension. If the file name ends in + // a period (.) with no extension, or if the file name contains a path, + // .exe is not appended." + // + // "If the file name !does not contain a directory path!, + // the system searches for the executable file in the following + // sequence:..." + // + // In practice ANY non-existent path is extended by [.exe] extension + // in the [CreateProcess] funcion with the only exception: + // the path ends by (.) + + return fileToRun.getPath(); + } + + private long handle = 0; private OutputStream stdin_stream; private InputStream stdout_stream; @@ -156,30 +238,31 @@ final boolean redirectErrorStream) throws IOException { - // Win32 CreateProcess requires cmd[0] to be normalized - cmd[0] = new File(cmd[0]).getPath(); + // The [executablePath] is not quoted for any case. + String executablePath = getExecutablePath(cmd[0]); + + // We need to extend the argument verification procedure + // to guarantee the only command file execution for implicit [cmd.exe] + // run. + String upPath = executablePath.toUpperCase(); + boolean isCmdFile = (upPath.endsWith(".CMD") || upPath.endsWith(".BAT")); StringBuilder cmdbuf = new StringBuilder(80); - for (int i = 0; i < cmd.length; i++) { - if (i > 0) { - cmdbuf.append(' '); - } + + // Quotation protects from interpretation of the [path] argument as + // start of longer path with spaces. Quotation has no influence to + // [.exe] extension heuristic. + cmdbuf.append('"'); + cmdbuf.append(executablePath); + cmdbuf.append('"'); + + for (int i = 1; i < cmd.length; i++) { + cmdbuf.append(' '); String s = cmd[i]; - if (s.indexOf(' ') >= 0 || s.indexOf('\t') >= 0) { - if (s.charAt(0) != '"') { - cmdbuf.append('"'); - cmdbuf.append(s); - if (s.endsWith("\\")) { - cmdbuf.append("\\"); - } - cmdbuf.append('"'); - } else if (s.endsWith("\"")) { - /* The argument has already been quoted. */ - cmdbuf.append(s); - } else { - /* Unmatched quote for the argument. */ - throw new IllegalArgumentException(); - } + if (needsEscaping(isCmdFile, s)) { + cmdbuf.append('"'); + cmdbuf.append(s); + cmdbuf.append('"'); } else { cmdbuf.append(s); }