[ruby-core:118253] [Ruby master Feature#6648] Provide a standard API for retrieving all command-line flags passed to Ruby

Issue #6648 has been updated by Eregon (Benoit Daloze). shyouhei (Shyouhei Urabe) wrote in #note-29:
The current situation is that ruby is not the only valid executable that the method takes. Allowing untrusted inputs for it means it has to be secure for everything. This is too much a hustle. Better find a fine-grained alternative.
There is no untrusted input involved here, because the user chooses what flags to pass to `ruby`. If ruby flags can be injected by an attacker, then all is lost regardless of this change (e.g. they can just inject `-r/backdoor.rb`). Regarding the Windows concern, it is the exact same problem for e.g. `spawn("dir", "*.mp3", "/s")`. From what I can see, it is completely separate from this ticket. The code for this on Windows must already escape as much as feasible, and if it fails it's a bug of that code which should be fixed to fix `spawn` etc in general, nothing to change in `RbConfig.ruby_args`. Or if the escaping fails maybe it's just considered a Windows limitation, independent of this ticket. For example, the user is running `ruby --yjit -rmytracing script.rb`, the only addition is the script can now find out the ruby flags it was called with (`["--yjit", "-rmytracing"]`). If the script spawn subprocesses, it already did before, so nothing changes there. It can choose to use `RbConfig.ruby_args`, and that's fine, the user running the script is responsible for whether it's safe to run the script, as always. Let's take the MSpec use-case (mentioned before in https://bugs.ruby-lang.org/issues/6648#note-11), what we want is to run Ruby subprocess with the same Ruby flags. So if e.g. `ruby --yjit -rmytracing path/to/mspec` is called, then if specs create subprocesses (via `ruby_exe()`), then those subprocesses (running some fixture) also have `--yjit -rmytracing`, as desired. You might argue `RUBYOPT` could be used instead, but that is problematic for various reasons: some flags are not allowed in RUBYOPT, RUBYOPT gets propagated arbitrarily far which is not necessarily desired (including to other Ruby implementations and executables written in Ruby, etc). A concrete example I often run into is running ruby/spec with TruffleRuby, I pass `--core-load-path=.../src/main/ruby/truffleruby` to use core library files from disk in development. It is critical that subprocesses in specs also use that (otherwise we'd get an inconsistent core library). The current workaround is to pass that flag both to the ruby process and as `-T`, which is quite ugly but it also slow, because it means mspec must create an extra subprocess just to apply these `-T` flags (it actually uses `exec` but that's just as slow): ``` $ mxbuild/truffleruby-jvm/bin/ruby \ --experimental-options --core-load-path=src/main/ruby/truffleruby \ spec/mspec/bin/mspec run \ --config spec/truffleruby.mspec \ -t .../mxbuild/truffleruby-jvm/bin/ruby \ --excl-tag fails --excl-tag slow \ -T--vm.ea -T--vm.esa \ -T--experimental-options -T--core-load-path=src/main/ruby/truffleruby ``` (as you can see there are already some bugs there because the -T and regular flags don't match exactly). (if you think this could be wrapped in some helper script, it already is, but it changes nothing because it must accept arbitrary ruby flags to be passed) With `RbConfig.ruby_args`, MSpec can know which ruby flags it was passed, which would avoid needing the extra subprocess, and it would be: ``` $ mxbuild/truffleruby-jvm/bin/ruby \ --vm.ea --vm.esa \ --experimental-options --core-load-path=src/main/ruby/truffleruby \ spec/mspec/bin/mspec run \ --config spec/truffleruby.mspec \ -t .../mxbuild/truffleruby-jvm/bin/ruby \ --excl-tag fails --excl-tag slow ``` This would be a killer feature when attaching a debugger, because then one could just `myruby -rmydebug spec/mspec/bin/mspec` and it would start running specs with the debugger (e.g. for TruffleRuby with the Java debugger). Instead of the current situation where the debugger is started on this mspec "wrapper" which just exec's to handle `-T` flags and is very annoying. This happens in CRuby just as much, for example https://github.com/ruby/ruby/blob/69c0b1438a45938e79e63407035f116de4634dcb/s... is a workaround causing some duplication. And it makes much more messy to e.g. running a single spec under `gdb`/`lldb`. A very similar situation happens for `make test-all` I would imagine. I guess currently any Ruby subprocesses incorrectly omits Ruby flags, which means the test coverage is lower than intended (e.g. for --yjit, --rjit and all other flags). This would also be convenient for the way to run built-but-not-installed-ruby, which every CRuby developer uses. ---------------------------------------- Feature #6648: Provide a standard API for retrieving all command-line flags passed to Ruby https://bugs.ruby-lang.org/issues/6648#change-108750 * Author: headius (Charles Nutter) * Status: Assigned * Assignee: matz (Yukihiro Matsumoto) ---------------------------------------- Currently there are no standard mechanisms to get the flags passed to the currently running Ruby implementation. The available mechanisms are not ideal: * Scanning globals and hoping they have not been tweaked to new settings * Using external wrappers to launch Ruby * ??? Inability to get the full set of command-line flags, including flags passed to the VM itself (and probably VM-specific) makes it impossible to launch subprocess Ruby instances with the same settings. A real world example of this is "((%bundle exec%))" when called with a command line that sets various flags, a la ((%jruby -Xsome.vm.setting --1.9 -S bundle exec%)). None of these flags can propagate to the subprocess, so odd behaviors result. The only option is to put the flags into an env var (((|JRUBY_OPTS|)) or ((|RUBYOPT|))) but this breaks the flow of calling a simple command line. JRuby provides mechanisms to get all its command line options, but they require calling Java APIs from Ruby's API set. Rubinius provides its own API for accessing comand-line options, but I do not know if it includes VM-level flags as well as standard Ruby flags. I know there is a (({RubyVM})) namespace in the 2.0 line. If that namespace is intended to be general-purpose for VM-level features, it would be a good host for this API. Something like... ``` class << RubyVM def vm_args; end # returns array of command line args *not* passed to the target script def script; end # returns the script being executed...though this overlaps with $0 def script_args; end # returns args passed to the script...though this overlaps with ARGV, but that is perhaps warranted since ARGV can be modified (i.e. you probably want the original args) end ``` -- https://bugs.ruby-lang.org/
participants (1)
-
Eregon (Benoit Daloze)