⊘ SEALED — This article is embargoed until the work it describes is complete.

№ 17: Removing Lua from Redis

A DDD.1 procedure for hardening Redis by compiling out its most dangerous subsystem, and a roadmap for dissolving the binary down to only what we use.

The CERT History

Redis ships with an embedded Lua 5.1 interpreter. It has been there since Redis 2.6, released in 2012. In the years since, this interpreter has become the single richest source of critical vulnerabilities in the Redis codebase.

Consider the recent record:

  • CVE-2025-49844 (CVSS 10.0, Critical) — A thirteen-year-old use-after-free in luaY_parser. An authenticated user sends a crafted Lua script that triggers garbage collection while the parser still references freed memory. Result: native code execution outside the Lua sandbox. The vulnerability existed in every version of Redis with Lua scripting — for thirteen years.
  • CVE-2025-46817 (CVSS 9.8, Critical) — An integer overflow in luaB_unpack. A crafted unpack() call wraps the stack size calculation, causing Redis to unpack vastly more values than the input array contains. Stack corruption, server crash, potential remote code execution.
  • CVE-2025-46818 (CVSS 7.3, High) — A Lua sandbox escape via unprotected metatables. An authenticated attacker modifies basic type metatables (strings, booleans) to inject code that executes under another user’s privileges. The scriptingInit function failed to set metatables as read-only.
  • CVE-2024-46981 (Critical) — Another use-after-free in the Lua garbage collector. Same pattern: crafted script, GC manipulation, code execution outside the sandbox.
  • CVE-2024-31449 (CVSS 7.0, High) — Lua library commands exploited for remote code execution. Redis’s embedded Lua engine runs user scripts within a context that proved insufficiently isolated.
  • CVE-2022-24735 (High) — Weak boundaries in the Lua execution environment allow a low-privileged authenticated user to persist code that later runs under a higher-privileged user’s context.

The pattern is unmistakable. Six CVEs in four years. Two rated Critical (CVSS ≥ 9.8), all requiring only authenticated access. The Lua engine is not an incidental attack surface — it is the attack surface. A thirteen-year-old use-after-free means this code was never safe; we just didn’t know it yet.

Our Use Case

The Patchworks appliance (№ 15) serves a single purpose: it accepts XADD messages on one Redis stream from authenticated visitors. The visitor ACL permits exactly four commands: AUTH, PING, XADD, and nothing else.

We do not use EVAL. We do not use EVALSHA. We do not use FUNCTION LOAD. We do not use Lua in any form. The Lua interpreter is dead weight — thousands of lines of C code linked into our binary that serve no purpose except to be exploited.

The Removal

We compiled Redis from source with the Lua scripting engine removed. Not disabled by configuration — removed from the binary. The resulting redis-server does not contain luaL_newstate. There is no interpreter to corrupt, no parser to use-after-free, no sandbox to escape, no metatables to hijack.

EVAL returns an error not because a config flag says so, but because the code that implements it does not exist.

This is the difference between a locked door and a wall. Configuration can be changed. Missing code cannot be exploited.

DDD.1: The Procedure

We call this DDD.1 — the first formal procedure in our Demolition-Driven Development practice. The steps:

  1. Identify the candidate. A subsystem we do not use, with a known vulnerability history. Lua qualified on both counts.
  2. Establish a test surface. Conformance tests that exercise every operational and error path we depend on. If our tests pass without the subsystem, the removal is safe.
  3. Remove. Compile without the subsystem. Not disable — remove.
  4. Verify. Full test suite passes. The binary works. The removed commands return errors. The tests that exercise remaining functionality still pass.
  5. Document. Record the CVE rationale, the test evidence, and the verification. The removal is not an opinion — it is an auditable decision with a paper trail.

What Comes Next

Lua was the obvious first target. But the methodology generalizes.

Phase 1: Code Coverage

We will instrument our Redis usage to produce code coverage reports that map exactly which Redis source code paths our operational and error scenarios exercise. AUTH, PING, XADD, XRANGE, XREADGROUP, ACL management. Every command we use, every error we handle. The coverage map becomes the ground truth for what we need.

Phase 2: Module Removal (DDD.1)

Redis includes modules we will never use: Geo, HyperLogLog, pattern-based Pub/Sub, Cluster, and others. For each:

  1. Code coverage confirms zero hits across all operational scenarios
  2. Remove the module from the build
  3. Full test suite passes
  4. The coverage report IS the proof that removal is safe

Same DDD.1 procedure as Lua, but now systematic and evidence-based across every subsystem.

Phase 3: Line-Level Dissolution

Beyond module removal. Within the modules we do use, there are code paths we never exercise. Error handlers for configurations we don’t set. Protocol branches for commands we don’t issue. Fallback logic for modes we don’t enable.

We will apply mutation analysis to the Redis source itself. Mutate a line of C — does our test suite catch it? Lines that can be mutated without test failure are candidates for removal. What remains after dissolution is the minimum viable Redis for our exact use case.

The payoff is both security and performance. A smaller binary means a smaller attack surface, fewer instruction cache misses, better branch prediction, less memory pressure. Security and performance emerge from the same discipline: knowing exactly what code you need, and removing everything else.

The Principle

Most hardening advice says: configure your software more carefully. Set this flag. Disable that option. Restrict this ACL. That advice assumes the software is correct and you just need to use it properly.

The CERT history of Redis Lua tells a different story. The software contained a thirteen-year-old use-after-free. Configuration cannot protect you from code that was never safe. The only defense against vulnerable code you don’t need is to not have it.

Absence is the strongest security control. You cannot exploit what does not exist.

Ruach Tov is open-source AI infrastructure research. If this work is valuable to you, consider supporting the project.