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

Catch and wrap AssertionError and NoClassDefFoundError in groovy scripts #19958

Merged
merged 1 commit into from Aug 12, 2016

Conversation

dakrone
Copy link
Member

@dakrone dakrone commented Aug 11, 2016

Previously, we only caught subclasses of Exception, however, there are
some cases when Errors are thrown instead of Exceptions. These two cases
are assert and when a class cannot be found.

Without this change, the exception would bubble up to the
uncaughtExceptionHandler, which would in turn, exit the JVM
(related: #19923).

A note of difference between regular Java asserts and Groovy asserts,
from http://docs.groovy-lang.org/docs/latest/html/documentation/core-testing-guide.html

"Another important difference from Java is that in Groovy assertions are
enabled by default. It has been a language design decision to remove the
possibility to deactivate assertions."

In the event that a user uses an assert such as:

def bar=false; assert bar, "message";

The GroovyScriptEngineService throws a NoClassDefFoundError being unable
to find the java.lang.StringBuffer class. It is highly recommended
that any Groovy scripting user switch to using regular exceptions rather
than unconfigurable Groovy assertions.

Resolves #19806

@dakrone dakrone added >bug :Core/Infra/Scripting Scripting abstractions, Painless, and Mustache v5.0.0-beta1 labels Aug 11, 2016
@nik9000
Copy link
Member

nik9000 commented Aug 11, 2016

As much as I don't like catching java's AssertionError to catch a groovy feature, it looks like that is required or we'll exit the JVM on fairly innocuous errors in user scripts.

LGTM

@nik9000
Copy link
Member

nik9000 commented Aug 11, 2016

Do we need the same protection in the other language plugins? Probably not painless, but lang-javascript and lang-python might need it.

@dakrone dakrone changed the title Catch AssertionError and NoClassDefFoundError in groovy scripts Catch and wrap AssertionError and NoClassDefFoundError in groovy scripts Aug 11, 2016
@@ -293,7 +293,7 @@ public Object run() {
// NOTE: we truncate the stack because IndyInterface has security issue (needs getClassLoader)
// we don't do a security check just as a tradeoff, it cannot really escalate to anything.
return AccessController.doPrivileged((PrivilegedAction<Object>) script::run);
} catch (Exception e) {
} catch (Exception | AssertionError | NoClassDefFoundError e) { // Groovy asserts are not java asserts, and cannot be disabled
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should inspect the AssertionError and make sure that it comes from Groovy (by inspecting the stack trace and looking for assertFailed and other relevant Groovy calls).

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay, I added a check for this, and a comment explaining why

@rmuir
Copy link
Contributor

rmuir commented Aug 11, 2016

Also why not simply add StringBuffer for groovy if it makes its assert work?

https://github.com/elastic/elasticsearch/blob/master/core/src/main/java/org/elasticsearch/script/ClassPermission.java#L79

The list is very simple: based on the old groovy sandbox, but that sandbox was not really "working" as well, for example java.lang.String was never whitelisted, yet it worked, because that mechanism was swiss-cheese.

So we may have to add classes like stringbuilder/buffer or other things.

@nik9000
Copy link
Member

nik9000 commented Aug 11, 2016

why not simply add StringBuffer for groovy if it makes its assert work?

We may want to add those classes too, but we need something like that so that other asserts floating around groovy don't shoot the JVM. We also don't want asserts users write to shoot the jvm, or any time they call Class.forName somehow.

@rmuir
Copy link
Contributor

rmuir commented Aug 11, 2016

Yes: I agree the two things are ... somewhat unrelated. But this is tied to the issue there, and adds a test that groovy's assert throws NoClassDefFoundError, which i really don't think needs to happen.

public void testGroovyScriptsThatThrowErrors() throws Exception {
assertFailure("assert false, \"msg\";", AssertionError.class);
assertFailure("def foo=false; assert foo;", AssertionError.class);
assertFailure("def bar=false; assert bar, \"msg2\"", NoClassDefFoundError.class);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess this is because we are not granting the class permission for StringBuffer but I think we should?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, I added the permissions for that (StringBuffer)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, but that was before we knew that we would need to add a permission for InvokeHelper too? So maybe we should just not add it since it will not help with getting asserts to work? Sorry.

@dakrone
Copy link
Member Author

dakrone commented Aug 11, 2016

Also why not simply add StringBuffer for groovy if it makes its assert work?

I can certainly add StringBuffer, however, that isn't the only class required to get groovy asserts to work, it also needs org.codehaus.groovy.runtime.InvokerHelper, which I think looks dangerous to add. I wish I could disable groovy's asserts entirely :-/

@rmuir
Copy link
Contributor

rmuir commented Aug 11, 2016

yes, lets leave that one out of it, serialization/reflection/etc, its not a good guy. Thanks for checking.

@@ -123,6 +123,13 @@ public void testEvilGroovyScripts() throws Exception {
}
}

public void testGroovyScriptsThatThrowErrors() throws Exception {
assertFailure("assert false, \"msg\";", AssertionError.class);
assertFailure("def foo=false; assert foo;", AssertionError.class);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For extra super paranoia's sake can you add a test that throws NoClassDefFoundError so we have something independent that checks that user scripts that do that don't halt the jvm?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In order to do that, we have to add permissions for the java.lang.NoClassDefFoundError in ClassPermission.java. Are we sure we want to add permissions for that class only to add a test case?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Makes sense to me.

@jasontedor
Copy link
Member

LGTM.

Previously, we only caught subclasses of Exception, however, there are
some cases when Errors are thrown instead of Exceptions. These two cases
are `assert` and when a class cannot be found.

Without this change, the exception would bubble up to the
`uncaughtExceptionHandler`, which would in turn, exit the JVM
(related: elastic#19923).

A note of difference between regular Java asserts and Groovy asserts,
from http://docs.groovy-lang.org/docs/latest/html/documentation/core-testing-guide.html

"Another important difference from Java is that in Groovy assertions are
enabled by default. It has been a language design decision to remove the
possibility to deactivate assertions."

In the event that a user uses an assert such as:

```groovy
def bar=false; assert bar, "message";
```

The GroovyScriptEngineService throws a NoClassDefFoundError being unable
to find the `java.lang.StringBuffer` class. It is *highly* recommended
that any Groovy scripting user switch to using regular exceptions rather
than unconfiguration Groovy assertions.

Resolves elastic#19806
@dakrone dakrone force-pushed the handle-groovy-errors-better branch from f9bee1c to aab8c1f Compare August 12, 2016 19:06
@dakrone dakrone merged commit aab8c1f into elastic:master Aug 12, 2016
@dakrone dakrone deleted the handle-groovy-errors-better branch August 16, 2016 22:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
>bug :Core/Infra/Scripting Scripting abstractions, Painless, and Mustache v5.0.0-beta1
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants