Using ClassLoader to make your game moddable

I spent the whole day going down a rabbit-hole of reading about this stuff, and have concluded it’s probably best to just add support for lua and only whitelist certain functionality lol. But thank you guys for all the help!

Interesting thread! And interesting to read the thread @CommanderKeith linked to and remember what I was working on at the time! :slight_smile: Since then, PraxisCORE (the runtime from PraxisLIVE) has become an actor-model system where every actor has its own ClassLoader and every actor accepts it’s behaviour as a String of Java code that can be defined and redefined as it’s running. It’s fun, and can be really powerful for certain uses, and is almost certainly not suitable for untrusted code in any form whatsoever! :wink:

But what’s the point of securing your code anyway? If it’s just for users to mod things for themselves, what’s the problem with just providing an API that they can do anything with? On the other hand, if your aim is that people can share mods and you feel you need to lock things down for the safety of other users then I’d suggest being careful of any executable code, whatever the language. Interesting that today I also read about the plan to deprecate serialization in the JDK because of its potential as an attack vector for arbitrary code execution. If this lockdown is important to you, perhaps a simple DSL (domain specific language), not Turing complete, as declarative as possible, is the way to go?

I played with LuaJ a bit today and found out it has some very powerful tools for calling Java functions straight from the lua. I started out with a lua file that’s something simple like this:

function Main(GameTools, WorldTools, HUDTools, PlayerTools)
  print('Hello World from lua file! Player Name: ' .. PlayerTools:getName())
end

And it successfully printed:

Hello World from lua file! Player Name: Cody

I wanted to see how far down I could take the rabbit hole and ended up trying this:

function Main(GameTools, WorldTools, HUDTools, PlayerTools)
  print('Hello World from lua file! First Item in Player Inventory: ' .. PlayerTools:getPlayer():getInventory():get(0):getName())
end

And holy hell, it printed out this:

Hello World from lua file! First Item in Player Inventory: Old Hover-Bike

It went all the way down into the EntityPlayer, down it his Inventory, and then got an Item out if its ArrayList and was able to successfully call getName(). That’s pretty much exactly what I was looking for in my mod support functionality. Of course, I’ll probably abstract and limit what the lua file can call so that the modders can’t go too far down the rabbit hole and accidentally break something, but it’s still cool that it has that potential.

I’ll have to keep reading up to make sure the lua support can’t be used maliciously though. I haven’t used lua since my teenage source modding days, so I can’t really remember what kind of functionality it has.

@nsigma
I’m not really a stickler on protecting my code, but it’s definitely about protecting the users from douchebags who will hide viruses in their mods. The Java VM is pretty powerful so I imagine giving full control over to the modder could end up being pretty dangerous.

Yes, I’m interested to know how this fixes your “problem” rather than just providing a different language for people to write their exploits in?!

Obviously it’s in text not bytecode, but that’s the same as the Java options talked about earlier and really relies on the user understanding or trusting the code source. You’re likely to still need to look into ClassLoader white-listing and/or SecurityManagers with this? Unless LuaJ adds this already on top of the normal Java scripting support?

Incidentally, another Java option that might be interesting to look at embedding in a game engine might be JShell?

Wouldn’t Javascript also be a good option for allowing user scripty stuff?

Cas :slight_smile:

Seems the obvious solution with Nashorn there, assuming you’re not building a “turtles all the way down” system like myself - ie. where you’re running performance stuff in the main loop.

It still doesn’t solve the security problem though. And these days it’s possible to write a Java API that’s more readable and less verbose than a JavaScript one - now that’s a refreshing change! ;D

My engine has a Component that can load JavaScript or Java source code. JavaScript is plugged into Nashorn (this is very simple and I think a viable scripting solution for most of the use cases out there), Java is compiled at runtime and loaded with the given classloader…just like every other sane module system does it. For the Java components I implement, I compile against a small API that can be used on provided scope in maven (component interface has init(EngineContext context), update(float deltaSeconds) functions and so on). You can limit the objects that the foreign component can use right there, by the interface definitions you provide. You still have the problem that one can execute arbitrary Java code in his component. Having different classloaders is optional here and has to be used when you want to isolate multiple extensions (external modules) from each other.

Another approach would be to go modular with the JPMS…with this you could export modules only to your own modules, but you can’t prevent arbitrary code execution. All in all, I don’t think it’s worth the hassle at all.

And can reflect on and access private fields of anything you do pass in unless you’ve locked that down?

Besides the security aspects of using classloaders, it also depends if you’re allowing runtime code updates. Throwaway classloaders are then useful because they get GC’d when the old class goes out of scope.

Building secure sandboxes:

  1. is (very) hard
  2. will almost certainly have holes in it.
  3. takes significant effort
  4. Increases complexity (both for you, and potential modders)
  5. limits what modders can achieve
  6. can be trivially bypassed by simple out-of-app instructions that most users will blindly follow (e.g. “Mod installation steps: 1) Run this executable installer”)

A better approach is IMO to allow mods to do anything they wish, and simply warn the user of the risks within your game’s UI.
If you cannot make a sandbox absolutely secure (you can’t), the illusion of security becomes more dangerous than an absence of security.

Of course, like I said, arbitrary code execution is something that is not really possible to prevent, when you give the user the chance to plug in his own Java code.

But if you provide an interface, for example let your user implement a component like


interface DialogExtension {
  List<DialogOptions> getDialogOptions()
}

and your user can only use your interfaces to compile against, then the attack vector is very small, isn’t it? For example he can not access your engine instance. Or the dialog manager. Is this worth something?

reads post
looks at profile name

Checks out. Lol.

Jokes aside, he has a point. I’m probably just going to leave this lua thing as is unless I find a huge gaping problem. I didn’t expect it to work this well though, it just kind of works out of the box, and I figured I’d have to do a lot of abstracting to get it to work. The parser is literally like 40 lines.

After reading about the ClassLoader, I really don’t think it’d be good for mods though. You have to still compile the project into a jar and go through all of that, but it’s hard to give the player the dependencies they need to do it. You’d pretty much have to split your project into two projects, where one has the usable API by plugins and then the other is just your normal game (so that the plugins can’t sneak into the main game functionality and break something). Then again some people would argue you’d want to actually give them that functionality. I’m kinda on the ehhh of giving them unrestricted control rather than tightly controlled getters and setters.

It just seems like ClassLoader would be more trouble than its worth in a game scenario. I can see its applications in more serious software though, of course. You probably want something more lightweight for games that’s easy to pick up. Just my two cents.

The user doesn’t just have your code to compile against, they have the entire Java API too.
Not just the public APIs, but all the private stuff too.
Through these classes there’s a whole host of ways to interact with a running VM, (Reflection, Unsafe, runtime attachment of a javaagent, etc etc), the file system, and the OS.

You can block it with a restrictive parent class loader, or security manager, but unless you’re extremely thorough, there will be holes.

How about implementing a LISPy-dialect specific to your game/engine on top of a ASM library? Doesn’t work properly on Android/iOS though…

Huh, what?! Someone really should have told me that sooner! ;D

I used to use Janino for this - https://janino-compiler.github.io/janino/ Great, incredibly easy to embed, but no lambda support :emo: It also has code to use the Compiler interface, but that means you have to include javac. No compile to jar with Janino at all though - doesn’t even go to a file - straight from String to Java bytecode at runtime.

I’m using some seriously forked code from Janino still in PraxisCORE.

I don’t know if I’m a little bit late to the party and if that’s actually what you want, but I’ve used this to just dynamically load and initialize objects from jars:

URLClassLoader classLoader = URLClassLoader.newInstance( new URL[] { fileInJar.toURI().toURL() } );
Class<?> clazz = classLoader.loadClass( className );
Constructor<?> constructor = clazz.getConstructor();
Mod createdInstance = (Mod)constructor.newInstance();

Just to kind of report back in with my usage of Lua to mod Java games, not only can you access Java from Lua, but I’ve also found a way to actually restrict the usable packages to specific ones.

I recommend checking out LuaJ if you haven’t already:
http://www.luaj.org/luaj/3.0/README.html

To make a whitelist of packages in LuaJ, simply extend LuajavaLib and override this function:

	protected Class<?> classForName(String name) throws ClassNotFoundException {
		Class<?> clazz = Class.forName(name, true, ClassLoader.getSystemClassLoader());

		return (isInWhitelist(clazz) ? clazz : null);
	}

I had made reasonable progress stubbed out a proof-of-concept version of what I described, but sadly didn’t VC it and the drive the code it was on when south. (Yeah I should know better)

Nooooooo :point:

Class.forName(…) actually executes the static-blocks of the class, if not already loaded.

If you want to whitelist classes, you’d check isInWhitelist(String name), not isInWhitelist(Class clazz)

Ironic to use the 3-argument version and then not pass in false, which at least might make it vaguely safe! :slight_smile:

… yes, use String!

I use this for the server part of my multiplayer engine so I can hot-deploy to a cluster of machines asynchronously:

But if you are developing locally you can hot-reload code changes, I think something from here should work: