Intermittent Failure of Elvis Operator Due to Concurrent Variable Modification

Description

We have observed an issue with the Elvis Operator (?:) in the Lucee CFML language where it intermittently fails or behaves unexpectedly. This issue appears to occur when a variable involved in the Elvis Operator expression is either deleted or created concurrently while the expression is being evaluated.

Detailed Description: The Elvis Operator is designed to return the value of its first operand if it exists and is not null, and otherwise return the value of its second operand. However, under certain conditions involving concurrent modification of variables (either creation or deletion), the operator does not perform as expected it throws an exception.

Steps to Reproduce: A test case that demonstrates this issue can be found in our test suite: Elvis Operator Test Case

Environment

None

Activity

Pothys - MitrahSoft 
6 February 2024 at 13:13
(edited)

I've checked this issue with lucee versions 6.0.1.63-SNAPSHOT and 5.4.5.11-SNAPSHOT. When variables are modified simultaneously, the elvis operator works fine and does not throw an exception.
Commit link(6.0) : https://github.com/lucee/Lucee/commit/4bc593c14730944878262ea24bd58fcc82ad2385
Commit link(5.4) : https://github.com/lucee/Lucee/commit/9afed8372208afe2d4c312bd4f89baf1daf5fce9

Michael Offner 
5 February 2024 at 16:09

For Lucee 5 the change is limited to the issue at hand, the Lucee 6 implemention get a step further and is able to handle the not existence of UDF correctly (Lucee 5 and ACF cannot).

Michael Offner 
1 February 2024 at 14:31
(edited)

The reason the code uses a ternary operator is because the right operant should only be touched in case the left operant fails and when this was written the only thing available on bytecode level was the ternary operator.

Michael Offner 
1 February 2024 at 14:22

Following the generated java code for this server.a.b.c.d?:"empty" Elvis Operation:

pc.us().set( TEST, Elvis.operate(pc, 11.0D, new Key[]{A, B, C, D}) ? pc.get(pc.getCollection(pc.getCollection(pc.serverScope().get(A), B), C), D) : "empty" );

As you can set this is translated into a tenary operation where first the variable is validated and if fine, loaded, but this step is not atomar.

The method “operate“ should handle all of this, not only the validation.

Michael Offner 
1 February 2024 at 14:05

the java strace of the issue

lucee.runtime.exp.ExpressionException: The key [D] does not exist, the structure is empty at lucee.runtime.type.util.StructSupport.invalidKey(StructSupport.java:65) at lucee.runtime.type.StructImpl.get(StructImpl.java:149) at lucee.runtime.util.VariableUtilImpl.get(VariableUtilImpl.java:278) at lucee.runtime.PageContextImpl.get(PageContextImpl.java:1567) at test7_cfm$cf.threadCall(/test7.cfm:12) at lucee.runtime.thread.ChildThreadImpl.execute(ChildThreadImpl.java:203) at lucee.runtime.thread.ChildThreadImpl.run(ChildThreadImpl.java:145)
Fixed

Details

Assignee

Reporter

Priority

Labels

Fix versions

New Issue warning screen

Before you create a new Issue, please post to the mailing list first https://dev.lucee.org

Once the issue has been verified, one of the Lucee team will ask you to file an issue

Created 1 February 2024 at 14:00
Updated 6 February 2024 at 13:15
Resolved 6 February 2024 at 13:13