Skip to content

Conversation

jerboaa
Copy link
Collaborator

@jerboaa jerboaa commented Sep 10, 2021

NativeImage passes some arguments via the @file feature of the
JDK launcher. When some arguments, separated by new lines, contain
whitespace and aren't properly quoted, the JDK launcher code fails
to parse the argument file and the native image generator class is
never launched.

Shell-quoting arguments before writing them to an @argument file
avoids this issue.

Closes #3769

@jerboaa
Copy link
Collaborator Author

jerboaa commented Sep 10, 2021

@lazar-mitrovic Please review!

@lazar-mitrovic
Copy link
Contributor

Hi @jerboaa, thanks for reporting and looking into this bug!
This looks fine, but I would suggest quoting arguments in NativeImage in order to cover both JDK8 and JDK9+ code paths.

It would look something like this:

diff --git a/substratevm/src/com.oracle.svm.driver/src/com/oracle/svm/driver/NativeImage.java b/substratevm/src/com.oracle.svm.driver/src/com/oracle/svm/driver/NativeImage.java
index b73b7045d57..f9d00d12f87 100644
--- a/substratevm/src/com.oracle.svm.driver/src/com/oracle/svm/driver/NativeImage.java
+++ b/substratevm/src/com.oracle.svm.driver/src/com/oracle/svm/driver/NativeImage.java
@@ -1424,12 +1424,17 @@ public class NativeImage {
              */
             arguments.addAll(Arrays.asList(SubstrateOptions.WATCHPID_PREFIX, "" + ProcessProperties.getProcessID()));
         }
+
         List<String> finalImageBuilderArgs = createImageBuilderArgs(imageArgs, imagecp, imagemp);
 
         /* Construct ProcessBuilder command from final arguments */
         List<String> command = new ArrayList<>();
         command.add(canonicalize(config.getJavaExecutable()).toString());
+
         List<String> completeCommandList = new ArrayList<>(command);
+        completeCommandList.addAll(Stream.concat(arguments.stream(), finalImageBuilderArgs.stream()).collect(Collectors.toList()));
+
+        arguments = arguments.stream().map(SubstrateUtil::quoteShellArg).collect(Collectors.toList());
         if (config.useJavaModules()) { // Only in JDK9+ 'java' executable supports @argFiles.
             command.add(createVMInvocationArgumentFile(arguments));
         } else {
@@ -1437,7 +1442,6 @@ public class NativeImage {
         }
         command.add(createImageBuilderArgumentFile(finalImageBuilderArgs));
 
-        completeCommandList.addAll(Stream.concat(arguments.stream(), finalImageBuilderArgs.stream()).collect(Collectors.toList()));
         final String commandLine = SubstrateUtil.getShellCommandString(completeCommandList, true);
         if (isDiagnostics()) {
             // write to the diagnostics dir

@jerboaa
Copy link
Collaborator Author

jerboaa commented Sep 10, 2021

Hi @jerboaa, thanks for reporting and looking into this bug!
This looks fine, but I would suggest quoting arguments in NativeImage in order to cover both JDK8 and JDK9+ code paths.

I don't think it's needed. JDK 8 uses the commandline for passing arguments. A List is being passed to ProcessBuilder for JDK 8 which doesn't have that problem. What's more, it would double-quote the arguments for --verbose launches. For that reason I'd prefer this approach. Thoughts?

@Karm
Copy link
Contributor

Karm commented Sep 10, 2021

@jerboaa This patch is not enough to fix the JDK 17 Mandrel build on Windows (last known successful build Graal 36cfbfa, JDK17 ff6a9382f5ed.

Previous failure ✔️

Fixed in this PR.

DEBUG [build] Patch native image...
DEBUG [build] mandrelVersion: 21.3.0-dev66970229cc4
DEBUG [build] Launcher line BEFORE: "%location%..\..\..\bin\java" -XX:+UnlockExperimentalVMOptions -XX:+EnableJVMCI --upgrade-module-path %location%\..\..\jvmci\graal.jar --add-modules org.graalvm.truffle,org.graalvm.sdk --module-path %location%\..\..\truffle\truffle-api.jar:%location%\..\..\jvmci\graal-sdk.jar %jvm_args% %app_path_arg% "%absolute_cp%" %main_class% %launcher_args%
DEBUG [build] launcherMatcher.group(1): "%location%..\..\..\bin\java" -XX:+UnlockExperimentalVMOptions -XX:+EnableJVMCI
DEBUG [build] launcherMatcher.group(2):  --upgrade-module-path %location%\..\..\jvmci\graal.jar --add-modules org.graalvm.truffle,org.graalvm.sdk --module-path %location%\..\..\truffle\truffle-api.jar:%location%\..\..\jvmci\graal-sdk.jar %jvm_args% %app_path_arg% "%absolute_cp%" %main_class% %launcher_args%
DEBUG [build] Launcher line AFTER: "%location%..\..\..\bin\java" -XX:+UnlockExperimentalVMOptions -XX:+EnableJVMCI -Dorg.graalvm.version="21.3.0-dev66970229cc4" -Dorg.graalvm.config="Mandrel Distribution" --upgrade-module-path %location%\..\..\jvmci\graal.jar --add-modules org.graalvm.truffle,org.graalvm.sdk --module-path %location%\..\..\truffle\truffle-api.jar:%location%\..\..\jvmci\graal-sdk.jar %jvm_args% %app_path_arg% "%absolute_cp%" %main_class% %launcher_args%
DEBUG [build] Build native agents...
DEBUG [OperatingSystem] Execute [.\mandrel-java17-21.3.0-dev66970229cc4\lib\svm\bin\native-image.cmd, --macro:native-image-agent-library] in C:\workspace\workspace\mandrel-maste---c3557c9d
Error: Could not find or load main class Distribution
Caused by: java.lang.ClassNotFoundException: Distribution
Error: Image build request failed with exit status 1
Exception in thread "main" java.lang.RuntimeException: java.lang.RuntimeException: Failed, exit code: 1
    at OperatingSystem.exec(build.java:1733)
    at build.buildAgents(build.java:188)
    at build.main(build.java:170)
Caused by: java.lang.RuntimeException: Failed, exit code: 1
    at OperatingSystem.exec(build.java:1718)
    at build.buildAgents(build.java:188)
    at build.main(build.java:170)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:568)
    at jdk.compiler/com.sun.tools.javac.launcher.Main.execute(Main.java:419)
    at jdk.compiler/com.sun.tools.javac.launcher.Main.run(Main.java:192)
    at jdk.compiler/com.sun.tools.javac.launcher.Main.main(Main.java:132)

With the patch in this PR ❌

Still problematic arguments:

DEBUG [build] Patch native image...
DEBUG [build] mandrelVersion: 21.3.0-devc4f6af1e281
DEBUG [build] Launcher line BEFORE: "%location%..\..\..\bin\java" -XX:+UnlockExperimentalVMOptions -XX:+EnableJVMCI --upgrade-module-path %location%\..\..\jvmci\graal.jar --add-modules org.graalvm.truffle,org.graalvm.sdk --module-path %location%\..\..\truffle\truffle-api.jar:%location%\..\..\jvmci\graal-sdk.jar %jvm_args% %app_path_arg% "%absolute_cp%" %main_class% %launcher_args%
DEBUG [build] launcherMatcher.group(1): "%location%..\..\..\bin\java" -XX:+UnlockExperimentalVMOptions -XX:+EnableJVMCI
DEBUG [build] launcherMatcher.group(2):  --upgrade-module-path %location%\..\..\jvmci\graal.jar --add-modules org.graalvm.truffle,org.graalvm.sdk --module-path %location%\..\..\truffle\truffle-api.jar:%location%\..\..\jvmci\graal-sdk.jar %jvm_args% %app_path_arg% "%absolute_cp%" %main_class% %launcher_args%
DEBUG [build] Launcher line AFTER: "%location%..\..\..\bin\java" -XX:+UnlockExperimentalVMOptions -XX:+EnableJVMCI -Dorg.graalvm.version="21.3.0-devc4f6af1e281" -Dorg.graalvm.config="Mandrel Distribution" --upgrade-module-path %location%\..\..\jvmci\graal.jar --add-modules org.graalvm.truffle,org.graalvm.sdk --module-path %location%\..\..\truffle\truffle-api.jar:%location%\..\..\jvmci\graal-sdk.jar %jvm_args% %app_path_arg% "%absolute_cp%" %main_class% %launcher_args%
DEBUG [build] Build native agents...
DEBUG [OperatingSystem] Execute [.\mandrel-java17-21.3.0-devc4f6af1e281\lib\svm\bin\native-image.cmd, --macro:native-image-agent-library] in C:\workspace\workspace\mandrel-maste---c3557c9d
Error: VM option 'UseJVMCICompiler' is experimental and must be enabled via -XX:+UnlockExperimentalVMOptions.
Error: The unlock option must precede 'UseJVMCICompiler'.
Error: Could not create the Java Virtual Machine.
Error: A fatal exception has occurred. Program will exit.
Error: Image build request failed with exit status 1
Exception in thread "main" java.lang.RuntimeException: java.lang.RuntimeException: Failed, exit code: 1
    at OperatingSystem.exec(build.java:1733)
    at build.buildAgents(build.java:188)
    at build.main(build.java:170)
Caused by: java.lang.RuntimeException: Failed, exit code: 1
    at OperatingSystem.exec(build.java:1718)
    at build.buildAgents(build.java:188)
    at build.main(build.java:170)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:568)
    at jdk.compiler/com.sun.tools.javac.launcher.Main.execute(Main.java:419)
    at jdk.compiler/com.sun.tools.javac.launcher.Main.run(Main.java:192)
    at jdk.compiler/com.sun.tools.javac.launcher.Main.main(Main.java:132)

@jerboaa
Copy link
Collaborator Author

jerboaa commented Sep 10, 2021

@jerboaa This patch is not enough to fix the JDK 17 Mandrel build on Windows (last known successful build Graal [36cfbfa]

@Karm Yes, I've noticed. This seems to be yet another - unrelated - build break :/ It builds/runs fine for me based on revision 1299996. So this must have been introduced later.

@lazar-mitrovic
Copy link
Contributor

I don't think it's needed. JDK 8 uses the commandline for passing arguments. A List is being passed to ProcessBuilder for JDK 8 which doesn't have that problem. What's more, it would double-quote the arguments for --verbose launches. For that reason I'd prefer this approach. Thoughts?

Oh, my bad! In that case this LGTM!

@jerboaa
Copy link
Collaborator Author

jerboaa commented Sep 10, 2021

@lazar-mitrovic Thanks for the review! Could you please help getting this integrated?

@lazar-mitrovic
Copy link
Contributor

I'm in the process of integrating this, but it seems that there are some issues with certain internal tests on Windows.
@pejovica can you please take a look and finish merging this PR?

@jerboaa
Copy link
Collaborator Author

jerboaa commented Sep 13, 2021

I'm in the process of integrating this, but it seems that there are some issues with certain internal tests on Windows.

Thanks, @lazar-mitrovic. Much appreciated. Please let me know if there is something which needs changing.

@jerboaa
Copy link
Collaborator Author

jerboaa commented Sep 14, 2021

@lazar-mitrovic @pejovica Any update on this? Is this good to go or are there updates needed?

@jerboaa
Copy link
Collaborator Author

jerboaa commented Sep 20, 2021

Could somebody please help integrate this? It would be a fix needed for the 21.3 release too. Thanks!

@jerboaa
Copy link
Collaborator Author

jerboaa commented Sep 20, 2021

@jerboaa This patch is not enough to fix the JDK 17 Mandrel build on Windows (last known successful build Graal 36cfbfa, JDK17 ff6a9382f5ed.

FTR, this failure was unrelated and got fixed with #3793

@zakkak zakkak added this to the 21.3 milestone Sep 20, 2021
@gradinac
Copy link
Contributor

Hey @jerboaa, thank you for your contribution! :) It seems like this fix doesn't work on Windows, attempting to build any native images on Windows yields:

Error opening zip file or JAR manifest missing : C:devgraalsdkmxbuildwindows-amd64GRAALVM_DB41C456C5_JAVA11graalvm-db41c456c5-java11-21.3.0-devlibsvmbuildersvm.jar
Error occurred during initialization of VM
agent library failed to init: instrument
Error: Image build request failed with exit status 1

I suspect the issue here may be because of the argfile rules: backslashes don't need to be escaped if the option they are used in isn't quoted, but if it is, then they need to be escaped.
In this case, the problematic option is:

'-javaagent:C:\dev\graal\sdk\mxbuild\windows-amd64\GRAALVM_DB41C456C5_JAVA11\graalvm-db41c456c5-java11-21.3.0-dev\lib\svm\builder\svm.jar'

However, it appears other options would cause problems further down the road, like:

-cp
'C:\dev\graal\sdk\mxbuild\windows-amd64\GRAALVM_DB41C456C5_JAVA11\graalvm-db41c456c5-java11-21.3.0-dev\lib\svm\builder\objectfile.jar;C:\dev\graal\sdk\mxbuild\windows-amd64\GRAALVM_DB41C456C5_JAVA11\graalvm-db41c456c5-java11-21.3.0-dev\lib\svm\builder\pointsto.jar;C:\dev\graal\sdk\mxbuild\windows-amd64\GRAALVM_DB41C456C5_JAVA11\graalvm-db41c456c5-java11-21.3.0-dev\lib\svm\builder\svm.jar'

Would you have time to look into this?

@jerboaa
Copy link
Collaborator Author

jerboaa commented Sep 21, 2021

@gradinac OK, good to know. I'll look into it.

@Karm
Copy link
Contributor

Karm commented Sep 21, 2021

I can reproduce this in Mandrel build too, left Windows ❌ , right Linux ✔️

org.graalvm.nativeimage.builder/com.oracle.svm.hosted.NativeImageGeneratorRunner
Error opening zip file or JAR manifest missing : C:     mpmandrel-java11-22.0.0-dev6299166libsvmbuildersvm.jar
Error occurred during initialization of VM

screenshot

We will amend the PR.

@Karm
Copy link
Contributor

Karm commented Sep 21, 2021

screenshot
Left Windows ❌ , Right Windows fixed ✔️
@jerboaa I will open a PR for your branch...

@jerboaa jerboaa force-pushed the arguments_files_fix_jdk branch 2 times, most recently from 1a92a4f to f81d522 Compare September 21, 2021 16:07
@jerboaa
Copy link
Collaborator Author

jerboaa commented Sep 21, 2021

@gradinac The updated patch should now work on Windows. It's was a bit tricky since \ is an escape character in argument files and we don't want to double-escape it. Say on Linux sombody passes foo\tbar as argument. We don't want it to translate to foo\\tbar. So what I've opted for is to actually escape the paths where they are constructed and properly translate them.

@jerboaa
Copy link
Collaborator Author

jerboaa commented Sep 21, 2021

Hey @jerboaa, thank you for your contribution! :) It seems like this fix doesn't work on Windows, attempting to build any native images on Windows yields:

Error opening zip file or JAR manifest missing : C:devgraalsdkmxbuildwindows-amd64GRAALVM_DB41C456C5_JAVA11graalvm-db41c456c5-java11-21.3.0-devlibsvmbuildersvm.jar
Error occurred during initialization of VM
agent library failed to init: instrument
Error: Image build request failed with exit status 1

I suspect a similar issue could happen when using --verbose and copy-pasting the native-image command as it stands (prior to this patch).

@gradinac
Copy link
Contributor

This is a good point, though I suspect we may still hit issues: backslashes in other native-image options. A user could, for example, specify -H:Path on Windows - in this case, the actual value of -H:Path would be different from the one the user specified. This would be true of any option involving paths, though I'm not sure if we have any other options where backslashes may be valid

@jerboaa
Copy link
Collaborator Author

jerboaa commented Sep 22, 2021

This is a good point, though I suspect we may still hit issues: backslashes in other native-image options. A user could, for example, specify -H:Path on Windows - in this case, the actual value of -H:Path would be different from the one the user specified. This would be true of any option involving paths, though I'm not sure if we have any other options where backslashes may be valid

More candidates seem to be:

-H:CCompilerPath=...                         Provide custom path to C compiler used for query code compilation and linking. Default: None 
-H:CLibraryPath=<string>*                    Search path for C libraries passed to the linker (list of comma-separated directories).
-H:ConfigurationResourceRoots=<string>*      Resource path above configuration resources for dynamic features at runtime.
-H:DebugInfoSourceSearchPath=<string>*       Search path for source files for Application or GraalVM classes (list of comma-separated directories or jar files).
-H:DumpPath="graal_dumps"                    The directory where various Graal dump files are written.
-H:DynamicProxyConfigurationFiles=<string>*  One or several (comma-separated) paths to JSON files that specify lists of interfaces that define Java proxy classes.
-H:FallbackExecutorClasspath=...             Internal option used to specify Classpath for FallbackExecutor. Default: None
-H:InspectServerContentPath="inspect"        Path to the contents of the Inspect web server.
-H:LinkerRPath=<string>*                     Path passed to the linker as the -rpath (list of comma-separated directories).
-H:Path=...                                  Directory of the image file to be generated. Default: None
-H:ReflectionConfigurationFiles=<string>*    One or several (comma-separated) paths to JSON files that specify which program elements should be made available via
-H:SerializationConfigurationFiles=<string>* One or several (comma-separated) paths to JSON files that specify lists of serialization configurations. [Extra help
-R:DumpPath="graal_dumps"                    The directory where various Graal dump files are written.
-R:InspectServerContentPath="inspect"        Path to the contents of the Inspect web server.
--native-compiler-path
                      provide custom path to C compiler used for query code compilation
--configurations-path <search path of option-configuration directories>

Shall I fix them all, or do you suggest to go a different route? As it stands if any one of these options contained a space it would likely break. It's not really inherent to this patch, but to the argument file change (7c4a040) as a whole.

@gradinac
Copy link
Contributor

Fixing all of these options manually, along with the ones we've already changed would work but it would be very brittle in the long run. I'm not aware of any options which would use backslashes, so perhaps another approach we could take is to always escape backslashes in quoted options - this would greatly reduce the overall maintenance in the future. What do you think?

@jerboaa
Copy link
Collaborator Author

jerboaa commented Sep 22, 2021

Fixing all of these options manually, along with the ones we've already changed would work but it would be very brittle in the long run. I'm not aware of any options which would use backslashes, so perhaps another approach we could take is to always escape backslashes in quoted options - this would greatly reduce the overall maintenance in the future. What do you think?

Sure, lets go with that.

I'll point out that it'll not work for certain corner cases. Like user-provided input that contains \. These would get translated to \\ in arg files and the interpreted result would be different than the user intended.

NativeImage passes some arguments via the @file feature of the
JDK launcher. When some arguments, separated by new lines, contain
whitespace *and* aren't properly quoted, the JDK launcher code fails
to parse the argument file and the native image generator class is
never launched.

Shell-quoting arguments before writing them to an @argument file
avoids this issue. Due to a Windows argument file quirk, where
'\' don't need to be escaped if NOT quoted, but DO need escaping
when they are quoted we unconditionally escape '\'.

Closes oracle#3769
@jerboaa jerboaa force-pushed the arguments_files_fix_jdk branch from f81d522 to d11728e Compare September 22, 2021 14:14
@jerboaa
Copy link
Collaborator Author

jerboaa commented Sep 22, 2021

@gradinac Updated patch. Take 3 :) Thoughts? I can make the \ escaping windows conditional if that's preferred, fwiw.

@gradinac
Copy link
Contributor

@jerboaa Thank you very much! :) It looks great, I'll start integrating this and also try to backport it to 21.3. For now, I would leave it uniform across all platforms - however, we can revisit it if it ends up being needed.

@jerboaa
Copy link
Collaborator Author

jerboaa commented Sep 22, 2021

@jerboaa Thank you very much! :) It looks great, I'll start integrating this and also try to backport it to 21.3. For now, I would leave it uniform across all platforms - however, we can revisit it if it ends up being needed.

Thank you for taking care of integrating it!

@graalvmbot graalvmbot merged commit 82b65b6 into oracle:master Sep 25, 2021
@zakkak
Copy link
Collaborator

zakkak commented Sep 28, 2021

@gilles-duboscq Hi! Could we cherry-pick this change to the 21.3 release branch by any chance?

This is now part of 21.3 as well 9579cc9

Thank you @gradinac !

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

native-image argument files break when arguments contain spaces

7 participants