fix: replace eval-based start command with shell-free javaexec launcher (#1301)#1316
fix: replace eval-based start command with shell-free javaexec launcher (#1301)#1316stokpop wants to merge 4 commits into
Conversation
|
Medium:
This means source buildpack usage such as Suggested fix: update the source/git wrapper to build |
|
I did a deeper pass on the The issue I think still needs addressing is lifecycle/packaging integration:
A safer fix would be to make the source/git wrapper build |
|
Thanks for the thorough review. Addressed in the three commits just pushed:
|
|
@ramonskie thanks for bringing the source/git use case to my attention, I was not aware of that. |
|
could you squash the commit or if needed squash it in sensible parts.. |
…dfoundry#1301) Remove `eval "exec java $JAVA_OPTS ..."` from all container start commands. Replace with a two-layer approach: - Pure-bash `_expand_env_vars` in profile.d assembles JAVA_OPTS from .opts files, expanding $VAR/${VAR} references without executing command substitutions. - Shell-free `javaexec` binary tokenizes JAVA_OPTS at JVM launch (POSIX word-split + quote-removal, no globbing, no $(...) execution) and execs the JVM directly. User JAVA_OPTS from the environment are also expanded ($VAR/${VAR} only) and warned about if they contain $(...) or backtick command substitutions. Closes cloudfoundry#1301
…e behavior - Integration tests: Spring Boot and Play containers verify $(...) in user JAVA_OPTS reaches JVM as literal text (not executed). - Unit tests: end-to-end javaexec tests covering tokenization, word-split behavior for vars-with-spaces, and command-substitution warning output. - Docs: framework-java_opts.md covers runtime expansion rules; ruby-migration guide documents differences from old eval behavior.
…ion warning
- jvmkill and memory-calculator now use $DEPS_DIR instead of hardcoded
/home/vcap/deps so they work in non-default CF dep layouts.
- javaexec tokenizer preserves $(...), ${...}, and backtick spans as single
tokens (no splitting on spaces inside them).
- Warning for command substitution in JAVA_OPTS now shows only the matching
token, not the full JAVA_OPTS value.
…k usage
bin/finalize only built src/java/finalize/cli into a temp dir; packaged
buildpacks got bin/javaexec from scripts/build.sh, but source/git usage
(e.g. WithBuildpacks("https://github.com/cloudfoundry/java-buildpack.git"))
had no bin/javaexec and finalize aborted before release completed.
- bin/finalize now also builds src/java/javaexec/cli into the same temp dir
and passes the path via JAVAEXEC_BINARY_PATH.
- InstallJavaexecLauncher() (renamed from private) prefers JAVAEXEC_BINARY_PATH
and falls back to <buildpackDir>/bin/javaexec for packaged buildpacks.
- Three unit tests cover packaged path, source/git env-var override, and the
missing-binary error case.
5dc7b57 to
de38d37
Compare
|
@ramonskie cleaned up the commits |
Problem
The start command used
eval "exec java $JAVA_OPTS ..."to launch the JVM.evalreinterprets its argument as shell code, which made it fundamentallydifficult to preserve JAVA_OPTS values exactly as specified:
C:\path→C:path)JVM arguments (
-Dfoo="bar baz"→-Dfoo=bar+baz)*/7→ filenames)Each issue required a new escaping workaround; combinations of edge cases
kept producing regressions. The root cause is that
evalis the wrong toolfor passing an arbitrary string as JVM arguments.
Note: this PR replaces #1303 (which can be closed if this is merged, or revived from draft if this PR is not to be merged).
Solution
1.
javaexeclauncher (src/java/javaexec/)A compiled Go binary invoked as
exec $DEPS_DIR/<idx>/bin/javaexec "$JAVA_HOME/bin/java" <args>.It tokenizes
$JAVA_OPTSusing POSIX word-splitting and quote-removal rules,but performs no further processing:
$(...),${...}, backticksubstitutions, and shell metacharacters (
&,|,;,>) are all passedliterally to the JVM. Nested
$(...)and${...}forms with spaces insideare kept as one token.
2. Pure-bash
_expand_env_varsinprofile.d/00_java_opts.shExpands
$VAR/${VAR}references using only bash builtins — neverre-interprets quotes, backslashes, or metacharacters. The single known trusted
substitution
$(nproc)is resolved explicitly.\$VARpasses a literal$VARto the JVM (Ruby buildpack parity). A WARNING is emitted to stderr(showing only the offending token) when
$(...)or a backtick appears inuser
JAVA_OPTS.Other fixes included
memory_calculator.go,jvmkill.go: replace hardcoded/home/vcap/deps/with
$DEPS_DIRso installs work on non-default CF stacks.Test coverage
$(...)grouping,${...}extended forms, backtick, nested parens, full manifest scenario.runStartCommandtests: exact v5.x regression:JAVA_OPTShandling broken (pipes/newlines in 5.0.2, quoting in 5.0.3) #1301 reproducer (cron + quotedspaces), pipe, metacharacters, command substitution not executed (marker
file guard),
\$VARescape, warning token extraction, POSIX word-splitdocumentation.
runStartCommandsmoke test verifying javaexec fires forSpring Boot, Java Main, Play, Tomcat containers.
Closes #1301