Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Move arity-checking into variable-arity method bodies #7751

Merged
merged 3 commits into from
Apr 12, 2023

Conversation

headius
Copy link
Member

@headius headius commented Apr 11, 2023

During testing of indy-based compilation, it became obvious that we need to ensure variable-arity method bodies do their own arity-checking, so that direct calls from indy or Java do not cause array index errors. This PR adds such arity-checking to all such methods, following these rules:

Methods should do arity-checking manually if:

  • they have a minimum argument count, or
  • they have a maximum argument count
  • and they access that argument list or pass it to normal Java methods that access that argument list

Methods do not need a manual check if they do not have minimums or maximums, or if they only pass their argument list to another variable-arity method that does a manual check.

This PR also removes arity-checking from the generated invokers, to avoid double-checking.

The main risk from this change lies in third-party extension code that has variable-arity methods but no manual checking. Passing incorrect numbers of arguments to these methods may trigger an array index error, as it would segfault in MRI (which also does not automatically check arity).

These paths are automatically arity-checked but only along the
DynamicMethod.call path. This leaves direct Java calls and indy
binding without arity checking. Given the impossibility of
automatically arity-checking the direct call paths, it seems best
to always arity-check inside variable-arity native methods. We may
want to audit all such places and remove the automatic arity-
checking happening in DynamicMethod.call(..., IRbuyObject[]) so
we are always checking at the point of use, where the stack trace
will reflect the method name, and without double-checking in both
generated wrappers and in the method itself.
@headius headius added this to the JRuby 9.4.3.0 milestone Apr 11, 2023
@headius headius force-pushed the more_indy_fixes branch 2 times, most recently from 21613ba to c782f00 Compare April 12, 2023 19:42
In order to ensure that arity checks always happen along variable-
arity paths, it makes sense for us to move the check into the
method bodies rather than relying on the generated wrapper or the
caller to do the check. This has several advantages:

* Direct calls from Java will now be checked.
* Direct calls from invokedynamic call sites will be checked.
* The generated invokers will have less data and do less arity-
  checking.

Splitting methods by arity will still enable direct calls to
bypass the arity check.

The pattern I used here, which we may want to adopt as a
recommended pattern, is as follows:

* If the method accesses the contents of the array direct or
* If the method passes the array on to other code that accesses
  its contents directly without a check then
* A manual arity-check should be performed before any other
  operations.
@headius headius changed the title More indy fixes Move arity-checking into variable-arity method bodies Apr 12, 2023
headius added a commit to headius/stringio that referenced this pull request Apr 12, 2023
As part of jruby/jruby#7751 we are recommending that all extension
code manually check the arity of incoming arguments to variable-
arity methods, as in CRuby. This ensures that all call paths will
be checked, including direct paths from Java or invokedynamic, and
avoids array indexing errors in these situations.
This will be fixed once ruby/stringio#48 is merged and released.
@headius headius merged commit 5108bbd into jruby:master Apr 12, 2023
@headius headius deleted the more_indy_fixes branch April 12, 2023 20:26
headius added a commit to ruby/stringio that referenced this pull request Apr 12, 2023
As part of jruby/jruby#7751 we are recommending that all extension code
manually check the arity of incoming arguments to variable- arity
methods, as in CRuby. This ensures that all call paths will be checked,
including direct paths from Java or invokedynamic, and avoids array
indexing errors in these situations.
headius added a commit to headius/jruby that referenced this pull request Apr 12, 2023
@JasonLunn
Copy link
Contributor

This PR introduced the behavior change in 9.4.3.0 observed in #7851. Given that this breaks 3rd party extensions that work correctly on 9.4.2.0 and below, I recommend rolling this back in 9.4.3.1 so that JRuby users using these extensions are not negatively impacted by an upgrade from an older version of JRuby.

If there is a long term need for this breaking change, it should be rescheduled and introduced in a major version bump.

headius added a commit to headius/jruby that referenced this pull request Jul 17, 2023
These were all moved to manual arity-checking in jruby#7751,
but that led to breakage when third-party extensions had varargs
paths that did not check arity manually (see jruby#7851).
Instead we restore the default to auto arity-check with this flag
provided for opting out (jruby#7680).
headius added a commit to headius/jruby that referenced this pull request Jul 17, 2023
These were all moved to manual arity-checking in jruby#7751,
but that led to breakage when third-party extensions had varargs
paths that did not check arity manually (see jruby#7851).
Instead we restore the default to auto arity-check with this flag
provided for opting out (jruby#7680).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants