java-topology/docs/tickets/lua-0001-searchupvalue-linear-scan-per-reference.md
russell@unturf.com 9934133dcf whitepaper: 312 sites / 151 ecosystems — wave2+3 defect tables and PDF rebuild
Add 88 new defect entries to HIGH and MEDIUM tables:
  HIGH: mysql-0001/0002, mariadb-0001, redis-0001/0002, valkey-0001/0002, openvpn-0001,
        vlc-0001, prometheus-0001, otel-collector-0001, cockroachdb-0001..0004,
        tidb-0001..0008, kubernetes-0001/0002, go-0001, kotlin-0002, scala-0001,
        allegro5-0001, sdl2-0001, grafana-0001, clickhouse-0001, duckdb-0001,
        mongodb-0001, envoy-0001, istio-0001, cilium-0001, linkerd2-0001,
        linux-0001/0002/0003, tor-0002/0003, curl-0001, julia-0001, lua-0001,
        perl5-0001, nats-0001, spring-0003/0004, tomcat-0001, onos-0002, odl-0002

  MEDIUM: helm-0001, mariadb-0002, openssl-0001/0002, memcached-0001,
          cassandra-0001..0004, flink-0001, storm-0001/0002, zookeeper-0001..0003,
          pip-0001, gradle-0001, nginx-0001, haproxy-0001, caddy-0001, varnish-0001,
          ffmpeg-0001, gstreamer-0001, raylib-0001, love2d-0001, php-0001/0002,
          r-source-0001, cpython-0002, ruby-0001, rabbitmq-0003/0004, activemq-0001,
          ovs-0001, onos-0003, odl-0002, jetty-0001

PDF: 976K
2026-03-27 15:23:43 -04:00

74 lines
2.6 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# lua-0001: searchupvalue() O(n) linear scan per variable reference during compilation
**Target:** lua/lua
**Severity:** LOW-MEDIUM
**CWE:** CWE-407 (Inefficient Algorithmic Complexity)
**File:** `lparser.c``searchupvalue()` (~line 360), called from `singlevaraux()`
**Status:** PATCHED
## Description
During Lua compilation, every reference to a variable name goes through
`singlevaraux()``searchupvalue()`. `searchupvalue()` performs a linear scan
of the `FuncState.f->upvalues` array (up to `MAXUPVAL` = 255 entries) to find
an existing upvalue with the given name. A function with many upvalues that
references them frequently (e.g. in a tight inner loop body) pays O(n) per
reference at compile time. With M references and N upvalues: O(M × N).
Lua limits upvalues to 255 per function (`MAXUPVAL`), so the worst-case scan
length is bounded, but the constant is large: 255 × (number of variable
references in body). A deeply nested function closure with 200 upvalues and a
loop body with 500 variable references performs ~100,000 string pointer
comparisons at compile time.
## Root cause
```c
/* lparser.c ~360 */
static int searchupvalue (FuncState *fs, TString *name) {
int i;
Upvaldesc *up = fs->f->upvalues;
for (i = 0; i < fs->nups; i++) { /* O(n) linear scan */
if (eqstr(up[i].name, name)) return i;
}
return -1;
}
```
`eqstr` is a pointer comparison (Lua interns all strings), so each iteration is
cheap, but the scan still walks the entire upvalue list.
## Fix
Add a small hash map (`TString* → upvalue index`) to `FuncState`, populated in
`newupvalue()`. `searchupvalue()` becomes a single hash lookup.
Since `FuncState` is stack-allocated during parsing, a fixed-size open-address
table (power-of-two slots, 256 entries) fits without heap allocation:
```c
/* In FuncState (lparser.h): */
int upval_map[256]; /* slot → upvalue index; -1 = empty */
/* newupvalue(): after allocating */
int slot = luaO_str2num(name) & 255; /* or name->hash & 255 */
/* linear probe on collision */
upval_map[slot] = fs->nups - 1;
/* searchupvalue(): */
static int searchupvalue (FuncState *fs, TString *name) {
int slot = name->hash & 255;
/* probe up to 8 slots */
for (int probe = 0; probe < 8; probe++, slot = (slot+1)&255) {
int idx = fs->upval_map[slot];
if (idx < 0) return -1;
if (eqstr(fs->f->upvalues[idx].name, name)) return idx;
}
return searchupvalue_fallback(fs, name); /* full scan on overflow */
}
```
## Ops numbers (Java benchmark)
See `defects/lua/unit/LuaTest.java` (bench label "searchupvalue").
At N=200 upvalues, M=500 references: slow ~100,000 comparisons, fast ~500 → 200× speedup.