As noted in our security docs we currently have no protection against a billion laughs attack by FTL authors, either at compile-time or run-time.
Note the attack vector here is a malicious FTL author, which might be unlikely but should be considered for some usage scenarios. We are not talking about runtime issues where the attacker controls only the substitution, not the FTL message.
and you'll use up a lot of memory at compile time.
We could protect against this by a combination of some kind of depth counter and reference counter in the compiler, and bailout when we hit the limits. In real world FTL, there is very rarely a need to have lots of references to other items, or deeply nested references.
Run-time
We don't inline messages at the call site, so the equivalent with messages would produce a run-time issue:
Attempting to use the last function in the chain would produce a very large string at runtime.
We could address this in two ways:
At run-time - the compiled code for each message could check call depth in some way (e.g. by a passed in current_depth parameter). This would be a performance hit on every message, and relatively speaking a very large one for the common case.
At compile time, by:
noting that we already disallow cycles i.e. recursion or mutual recursion.
We go to the bottom (functions that call no other functions) and label functions with no dependencies with calls_others_depth=0
and go up the chain, adding calls_others_depth = max(function.calls_others_depth for function in this_function.functions_that_i_call) to each function.
We can then impose some kind of low limit on this depth (e.g. 4)
In addition we applying a low limit on the number of substitutions allowed per message (e.g. 10)
We may need to make some of these limits configurable.
As per normal fluent rules, we should not bail out with exceptions in these cases, but produce message functions that:
have truncated output
emit errors at compile-time/run-time errors as appropriate (normally both)
As noted in our security docs we currently have no protection against a billion laughs attack by FTL authors, either at compile-time or run-time.
Note the attack vector here is a malicious FTL author, which might be unlikely but should be considered for some usage scenarios. We are not talking about runtime issues where the attacker controls only the substitution, not the FTL message.
Compile-time
Example
Due to our current strategy of inlining all terms and simplifying, this will attempt to generate a function like:
and you'll use up a lot of memory at compile time.
We could protect against this by a combination of some kind of depth counter and reference counter in the compiler, and bailout when we hit the limits. In real world FTL, there is very rarely a need to have lots of references to other items, or deeply nested references.
Run-time
We don't inline messages at the call site, so the equivalent with messages would produce a run-time issue:
Which compiles to something like:
Attempting to use the last function in the chain would produce a very large string at runtime.
We could address this in two ways:
At run-time - the compiled code for each message could check call depth in some way (e.g. by a passed in
current_depth
parameter). This would be a performance hit on every message, and relatively speaking a very large one for the common case.At compile time, by:
calls_others_depth=0
calls_others_depth = max(function.calls_others_depth for function in this_function.functions_that_i_call)
to each function.We may need to make some of these limits configurable.
As per normal fluent rules, we should not bail out with exceptions in these cases, but produce message functions that: