[ 
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)

Reply via email to