Closures and Injecting Source Code

I needed a way to change what a Lua program does by letting a user write their own code. I spent yesterday working out how to do this. The solution comes down to closures and injecting source code.


Closures



I mentioned closures in a previous post. As a review a closure is like a function which can be anonymous (unnamed) and passed around as a function. Here's an example:



Screen Shot 2021-04-06 at 8.54.07 AM

The closure is the function assigned to f.


This creates a closure that adds two numbers. What if we wanted to multiply instead of adding? We would need another closure. And if we needed to do something completely different, we would need a totally new closure. That could be tedious. Wouldn't it be nice if we could have the user type in the command they want and then execute it? Lua, and some other programs, allow you to do just that. Now, a warning:

Warning: This is a powerful, and potentially dangerous (security) problem.

Injection



The other part of my solution is something known as "injection". We're going to inject source code into the above example, but we'll take it in steps. Make sure you understand how the above example and closures in general work.

I'm going to simplify the example to just adding two numbers. This should make understanding what I'm doing easier.
Screen Shot 2021-04-06 at 9.05.05 AM


The first thing we need is a way to execute a string of code. I'm going to convert the closure to a string.

Screen Shot 2021-04-06 at 9.07.36 AM

Note, the return at the beginning. I'll explain this shortly.


The variable f now has a string. We need to convert this string into something that Lua can execute. We do this with a function called load.

local result = load(string)

Which returns an executable function. The program now becomes:

Screen Shot 2021-04-06 at 9.27.51 AM

It won't run. The variable add contains a pointer to the function we just created, not a function we can call directly. We need to use another Lua function, pcall (pointer call), to execute the function pointed to by add. This is the reason for the extra return in the string. We don't want to return the function, we want to return what the function returns. The pcall call looks like this:

local error, result = pcall(pointer_to_code)

Note that we pass in a pointer to a block of code. Our code is a function which means we need to treat it as such which makes the actual call:

local result = pcall(pointer_to_call() )

That is, we need to make it clear to pcall that we want to execute a function (and not just code), so the extra parentheses are required for this example to run. We can then print the result.

So the program now becomes:

Screen Shot 2021-04-06 at 9.31.01 AM

This will run and print 3.

Screen Shot 2021-04-06 at 9.31.47 AM

Now we can execute a string as if it were code. How does this help with my original problem? I can change the string and execute it to get a different result, or even a completely different function.


Screen Shot 2021-04-06 at 9.38.15 AM


Screen Shot 2021-04-06 at 9.38.57 AM


To take this even further (without example code) you can read the string from a file, or have the user type a string in response to a prompt and then execute it.

That's power!

Remember what Spiderman always says…

I actually use this technique quite often in other languages that support executing strings as code (I wish Swift did), but as I mentioned, this opens up all sorts of security issues in uncontrolled environments.

That's enough for today.





This site does not track your information.