-
Notifications
You must be signed in to change notification settings - Fork 915
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
JIT mode allows nulled out IRubyObject array to propagate null #7914
Comments
@kares I believe you added the test originally. Can you explain why you expected it to pass? Perhaps it just accidentally passed and you codified that behavior in the expectation? @enebo Any thoughts on why this gets by the interpreter? The |
@headius if it indirects through a temporary variable we will null to nil the value on access. Part of me wonders what breaks if we remove that. I could see that we added it because we do not properly deal with uninitialized lvars (which is odd because access of an lvar will be nil). |
@enebo That's what I figured when I punted on this, but I was unable to locate where that actually happens. I did come up with a short reproduction to show that the interpreter allows this nulled array to work as an argument array, but JIT propagates the null and fails: class Foo
def foo(a)
p a
end
end
foo = Foo.new
jfoo = JRuby.ref(foo)
ary = org.jruby.runtime.builtin.IRubyObject[1].new
jfoo.callMethod(JRuby.runtime.current_context, "foo", ary) This is essentially what the test is doing, though it calls through a generated method from
Edit: |
@headius |
The snippet as IR:
So a is assigned null and we do not seem to check that at all on the way in but the return will access |
So I guess the question is whether IR should be failing here (invalid arguments in the argument array) or if JIT should gain some enhancement to avoid this null propagating further. I'm currently of the opinion that this is an invalid argument array. It would (should?) never happen during normal execution, and this example and the test above are going through a back door to force a null into the call stack. But I don't like when these bugs come up and nulls start to propagate. Perhaps we need a debug mode that will null check arguments, variable lookups (ivars, constants, globals), and return values for null, raising immediately when one is seen? This was difficult to track down and my first thought was that something was broken in the JIT or invokedynamic logic, so I spun my wheels trying to fix something that perhaps wasn't actually broken. A null-debugging flag we could turn on might help future cases. |
@headius I agree. A potential compromise where it will still work but we can at least detect it would be to make the recv instrs assert() is null. Then ordinary execution would work but we should see it fail in our test suite. |
I guess I should clarify that assert() is not really a compromise but just another variant of your debug mode idea. |
I scanned through JVMVisitor and it wouldn't be too hard to decorate all the incoming-argument logic with some conditional verification. I don't want it to be always there (like a Java assert) so it should still be behind a flag I think. |
The following test attempts to call into a Java method
Reflector.invoke
which calls back into a reified Ruby method via Java reflection. In the process, it eventually passes an empty IRubyObject[1] to the Rubybar
method.jruby/spec/java_integration/reify/become_java_spec.rb
Lines 67 to 68 in d7a87d4
When the JIT is disabled, the null value contained in the array never ends up in the
arg
argument as a null; it is a nil and the subsequent method calls work against that nil value.When the JIT is disabled, however, the null ends up propagating all the way to the calls, eventually causing NPE.
I have not found an explanation for this, but I have managed to confirm that in both modes, an
IRubyObject[] {null}
does indeed propagate through theReflector.invoke
method, but in the interpreter the null value becomes a nil before it is visible in `arg.I suspect that the interpreter has additional null guards or ends up populating
arg
with nil before it can become null. In any case, I don't believe this is a JIT bug, because the test logic in both cases clearly passes a non-zero-length IRubyObject[] with a null element, and that is never allowed. Perhaps there should be a better error than to propagate the null value, but propagation is part of what the IR and JIT optimizations are supposed to do.I'm unclear what the original intent of this particular test was. It has existed since 2f363d0 in 2016, probably around the time we were formalizing the "become_java" feature, but the way this tries to invoke the generated static method should always fail for a {null} argument array.
As such, I'm going to disable the offending test lines and we'll have to figure out whether they should be expected to pass.
The text was updated successfully, but these errors were encountered: