Skip to content

bugfix: server_rewrite_by_lua* used the global VM when lua_code_cache off - #2531

Open
xiaobiaozhao wants to merge 1 commit into
openresty:masterfrom
xiaobiaozhao:fix/server-rewrite-code-cache-off
Open

xiaobiaozhao wants to merge 1 commit into
openresty:masterfrom
xiaobiaozhao:fix/server-rewrite-code-cache-off

Conversation

@xiaobiaozhao

Copy link
Copy Markdown
Contributor

I hereby granted the copyright of the changes in this pull request
to the authors of this lua-nginx-module project.

Problem

When lua_code_cache off; is configured, server_rewrite_by_lua*
(server_rewrite_by_lua_block / server_rewrite_by_lua_file) does not
work correctly. If the handler touches ngx.ctx or yields (e.g.
ngx.sleep, cosockets), the request is aborted with a 500 error:

lua entry thread aborted: runtime error: content_by_lua(nginx.conf:44):2:
attempt to index field 'ctx' (a nil value)

Even without yielding, the per-request code cache is not honored: edited
Lua files are not reloaded across requests as lua_code_cache off
requires.

Environment

  • nginx core: 1.31.6
  • ngx_lua: master (ngx_http_lua_version = 10032)
  • Built with --with-debug -DNGX_LUA_USE_ASSERT, OpenResty LuaJIT

Root cause

ngx_http_lua_server_rewrite_handler() fetched the Lua VM before the
request context was created:

lscf = ngx_http_get_module_srv_conf(r, ngx_http_lua_module);
llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module);
L = ngx_http_lua_get_lua_vm(r, NULL);   /* too early */
...
if (ctx == NULL) {
    ctx = ngx_http_lua_create_ctx(r);   /* builds the per-request VM */
}

With lua_code_cache off, ngx_http_lua_create_ctx() creates a fresh
per-request Lua VM stored in ctx->vm_state. Because the VM was fetched
before create_ctx(), ngx_http_lua_get_lua_vm(r, NULL) fell back to the
persistent global VM (lmcf->lua). Consequences:

  1. Code cache reset had no effect on the VM actually used, defeating
    lua_code_cache off for this phase.
  2. ngx_http_lua_new_thread() saw L == lmcf->lua and reused cached
    global coroutine threads instead of per-request ones.
  3. On yield/resume the entry thread and the resume path ended up on
    mismatched VMs, so ngx.ctx (stored per-request) was nil in the
    content phase, aborting the request as shown above.

The sibling rewrite phase handler (ngx_http_lua_rewriteby.c) does not
have this problem: it fetches the VM inside the inline/file handlers,
i.e. after create_ctx() has run.

Fix

  • Fetch the VM after create_ctx() and pass the ctx explicitly:
    L = ngx_http_lua_get_lua_vm(r, ctx);
  • Reset lscf->srv.server_rewrite_src_ref = LUA_REFNIL when
    !llcf->enable_code_cache, in both the inline and file handlers, so
    chunks are recompiled per request (mirrors rewrite_src_ref handling).

Minimal reproduction

lua_code_cache off;

server_rewrite_by_lua_block {
    ngx.sleep(0.001)
    ngx.ctx.a = "resumed"
}

location = /t {
    content_by_lua_block { ngx.say(ngx.ctx.a) }
}

… off.

ngx_http_lua_server_rewrite_handler() fetched the Lua VM before the
request context was created. With "lua_code_cache off", create_ctx()
builds a fresh per-request VM in ctx->vm_state, so fetching the VM too
early always returned the persistent global VM. That defeated per-request
code caching, reused global coroutine threads, and caused a VM mismatch
on yield/resume which aborted requests with "attempt to index field
'ctx' (a nil value)".

Fetch the VM after create_ctx() and pass ctx, mirroring the rewrite phase
handler. Also reset server_rewrite_src_ref to LUA_REFNIL when the code
cache is disabled so inline/file chunks are recompiled per request.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant