Safety of the QVM and its JIT compiler

Is the QVM (both interpreter and the JIT compiled code) safe? How does it make sure that the engine’s memory isn’t accessed?

I tried to look for this on the internet, I read that it’s safe, but couldn’t find explanations on how that’s achieved.

Using untrusted add-ons in ioquake3 (that may be automatically installed if cl_allowDownload is enabled) is unlikely to ever be considered safe. It is quite unfortunate. It spans across QVM, file loaders, cvar system, and loading native DLLs.

QVM was designed for cross-platform not safety. There are QVM security improvements in ioquake3, attempting to limit read/write to a single contiguous memory allocation for the QVM. There is also a known unpatched QVM security vulnerability GH-358 right now that allows reading and writing arbitrary engine memory.

A block of memory is allocated for the QVM. The size allocated is a power of two so access can use a bit mask. All QVM bytecode commands that read/write memory are bit masked and then added to the QVM’s base memory address. In theory all QVM memory access is limited to it’s memory block. It’s not possible to pass an arbitrary engine memory address into the QVM to access (e.g., a text string) so the QVM must make a system call into the engine to copy it to QVM memory. (Bit masking was added in ioquake3, it did not exist in the original Quake 3 QVM implementation.)

The system calls into the engine, however can access all engine memory, and do not currently validate accessing QVM memory correctly. GH-358

If I remember correctly there have been other low level exploits using specific QVM bytecode but I don’t have references on-hand.

The file loaders for BSP levels and models (and maybe images) are not validated to prevent out of bounds memory access. So they could possibly be used for an exploit to run native code.

There have been issues in the past (now fixed) for allowing running native code by simply enabling cl_allowDownload 1 on the client and having the auto downloaded pk3 set some cvars to run the pk3 as a DLL (as zip archives can be appended to other files).

1 Like

So, every time the bytecode tries to access any memory address, the VM turns the address into a valid one.

It sounds like the VM itself is probably safe although there’s no strong guarantee for that (good mechanism to constrain memory accesses and no known vulnerabilities in the bytecode operations), while the environment that ioquake3 offers the VM is likely broken in a few points (confirmed at least for few of the “standard” syscalls).

Thank you for sharing all you wrote!