[
https://issues.apache.org/jira/browse/CASSANDRA-11052?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=15154070#comment-15154070
]
Robert Stupp commented on CASSANDRA-11052:
------------------------------------------
The purpose of the UDF sandbox is to prevent execution of "malicious" code.
Such code is technically everything that accesses internal classes or
not-allowed Java runtime classes or even 3rd party libraries (see
CASSANDRA-7395 - "pure functions") or network sockets, files, etc or starts
threads or could possibly halt execution. Especially threads are really bad as
we want to move from SEDA to a thread-per-core.
You're right, UDFByteCodeVerifier can only verify Java UDFs (more exact: byte
code). This is a known limitation and basically the reason why we have two
config options in {{cassandra.yaml}}: {{enable_user_defined_functions}} +
{{enable_scripted_user_defined_functions}}. Since we have the byte code, we can
inspect it and also modify it - that is just not possible with script engines.
You can prevent the start of a new thread with the security manager - but
that's maybe just not enough. Assume that such a fork-join-pool already exists
(for whatever reason), that permission check would just not be triggered since
the pool's already started.
The other thing is that these lambdas implicitly create new fields and inner
classes and methods in the generated Java UDF class. This is prevented by the
UDFByteCodeVerifier as you could intentionally inject such stuff to execute
"malicious" code. Imagine that you could for example inject some static
initialization code.
CASSANDRA-9954 would extend the UDFByteCodeVerifier with some byte code
instrumentation to remove the currently necessary separate thread pool for UDF
execution for Java UDFs. This effectively prevents {{synchronized}}, calls to
Object.wait and such stuff but also injects code to abort a long-running UDF.
We want to have support for "unsandboxed UDFs" with CASSANDRA-9862 for people
who really know what they are doing - so no security manager and no byte code
verification/manipulation.
Don't get me wrong - I'd like to see J8 lambda support for UDFs, but that must
comply with our policies. I think the right way to do that is to extend byte
code verification.
> Cannot use Java 8 lambda expression inside UDF code body
> --------------------------------------------------------
>
> Key: CASSANDRA-11052
> URL: https://issues.apache.org/jira/browse/CASSANDRA-11052
> Project: Cassandra
> Issue Type: Improvement
> Components: CQL
> Reporter: DOAN DuyHai
> Assignee: Robert Stupp
> Fix For: 3.x
>
> Attachments: 11052-2.patch, 11052.patch
>
>
> When creating the following **UDF** using Java 8 lambda syntax
> {code:sql}
> CREATE FUNCTION IF NOT EXISTS music.udf(state map<text,bigint>, styles
> list<text>)
> RETURNS NULL ON NULL INPUT
> RETURNS map<text,bigint>
> LANGUAGE java
> AS $$
> styles.forEach((Object o) -> {
> String style = (String)o;
> if(state.containsKey(style)) {
> state.put(style, (Long)state.get(style)+1);
> } else {
> state.put(style, 1L);
> }
> });
>
> return state;
> $$;
> {code}
> I got the following exception:
> {code:java}
> Caused by: com.datastax.driver.core.exceptions.InvalidQueryException: Could
> not compile function 'music.udf' from Java source:
> org.apache.cassandra.exceptions.InvalidRequestException: Java source
> compilation failed:
> Line 2: The type java.util.function.Consumer cannot be resolved. It is
> indirectly referenced from required .class files
> Line 2: The method forEach(Consumer) from the type Iterable refers to the
> missing type Consumer
> Line 2: The target type of this expression must be a functional interface
> at
> com.datastax.driver.core.Responses$Error.asException(Responses.java:136)
> at
> com.datastax.driver.core.DefaultResultSetFuture.onSet(DefaultResultSetFuture.java:179)
> at
> com.datastax.driver.core.RequestHandler.setFinalResult(RequestHandler.java:184)
> at
> com.datastax.driver.core.RequestHandler.access$2500(RequestHandler.java:43)
> at
> com.datastax.driver.core.RequestHandler$SpeculativeExecution.setFinalResult(RequestHandler.java:798)
> at
> com.datastax.driver.core.RequestHandler$SpeculativeExecution.onSet(RequestHandler.java:617)
> at
> com.datastax.driver.core.Connection$Dispatcher.channelRead0(Connection.java:1005)
> at
> com.datastax.driver.core.Connection$Dispatcher.channelRead0(Connection.java:928)
> at
> io.netty.channel.SimpleChannelInboundHandler.channelRead(SimpleChannelInboundHandler.java:105)
> at
> io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:318)
> at
> io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:304)
> at
> io.netty.handler.timeout.IdleStateHandler.channelRead(IdleStateHandler.java:266)
> at
> io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:318)
> at
> io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:304)
> at
> io.netty.handler.codec.MessageToMessageDecoder.channelRead(MessageToMessageDecoder.java:103)
> at
> io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:318)
> at
> io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:304)
> at
> io.netty.handler.codec.ByteToMessageDecoder.fireChannelRead(ByteToMessageDecoder.java:276)
> at
> io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:263)
> at
> io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:318)
> at
> io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:304)
> at
> io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:846)
> at
> io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:131)
> at
> io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:511)
> at
> io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:468)
> at
> io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:382)
> at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:354)
> at
> io.netty.util.concurrent.SingleThreadEventExecutor$2.run(SingleThreadEventExecutor.java:112)
> ... 1 more
> {code}
> It looks like the compiler requires importing java.util.Consumer but I have
> checked the source code and compiler options already support Java 8 source
> code so I'm pretty puzzled here ...
> /cc [~snazy]
--
This message was sent by Atlassian JIRA
(v6.3.4#6332)