OpenResty Lua传参时字符串值意外变更、日志重复打印问题求助
问题根因可能性排序及排查方案
一、参数异常的可能原因
self.renders[category].render(context)多返回值展开导致参数错位
Lua中如果函数直接作为另一个函数的参数调用,会把所有返回值依次传入后面的参数位。如果你的self.renders[realnode_ini].render方法返回2个及以上的值,那么file_ex.write(file, render_result)会自动把render的所有返回值按顺序填充到write的参数列表,相当于:
local r1, r2 = self.renders[category].render(context) file_ex.write(file, r1, r2)
如果你的render方法第一个返回值是空/错误值,第二个才是内容,甚至返回的第一个值就是/config/字符串,就会导致write函数的参数错位。
排查方式:在agent_gen.lua的_render函数中,先把render的返回值存到局部变量再传参,同时打印返回值个数:
local render_res = {self.renders[category].render(context)} ngx_log(ngx_NOTICE, "render return count: ", #render_res, ", first res: ", render_res[1]) return file_ex.write(file, render_res[1])
- FFI调用内存越界篡改字符串内容
你代码中使用了libc_ex相关的ffi封装,如果ffi函数存在缓冲区溢出、野指针写操作,刚好覆盖了file变量对应的字符串内存地址,就会出现字符串内容莫名其妙被修改的情况,这是OpenResty ffi开发中非常常见的隐性问题。
排查方式:暂时注释掉file_ex.write中libc_ex.file_exist、libc_ex.chmod的调用,直接执行文件写入逻辑,看参数是否恢复正常。如果正常,就逐一排查libc_ex封装的函数是否存在内存越界问题。 _get_realnode_ini_file多返回值问题
如果_get_realnode_ini_file(uid)返回多个值,而你在调用agent_gen._render的时候直接把该函数作为参数传入,也会出现参数展开错位的问题,虽然你打日志的时候只取到第一个值,但后续操作可能因为参数错位导致值被覆盖。
排查方式:同样把_get_realnode_ini_file的返回值先存到局部变量再传参,打印返回值个数验证。
二、日志重复打印的可能原因
你看到的两条error日志其实不是重复打印,而是两个不同位置调用了同一行日志代码:
- 第一条error日志是你在
_render_ini_file函数中自己加的ngx_log(ngx_ERR, 'write filename: ', file)调用(你贴的代码里漏了这个调用,但是日志里明确显示调用栈是_render_ini_file()) - 第二条error日志才是
file_ex.write函数内部打出来的日志
如果确认你没在_render_ini_file里加过这行日志,那就是以下两种情况: - 你加载了重复的
file_ex模块,两个不同路径的file_ex.lua被先后require,同一个行号刚好都有打日志的代码 - OpenResty配置中开启了
log_subrequest on,且你的逻辑触发了子请求,同一份逻辑被主请求和子请求各执行了一次,排查下nginx.conf中对应的server块配置即可。
临时修复方案
先把所有函数调用的返回值都先存到局部变量再传参,避免Lua多返回值隐式展开的坑,这是Lua开发中最容易踩的隐性问题之一:
-- code.lua 修改后 local function _render_ini_file(agent_gen, uid, instance_data) local context = {} local target_file = _get_realnode_ini_file(uid) -- 先存局部变量,截断多返回值 return agent_gen._render(agent_gen, 'realnode_ini', context, target_file) end -- agent_gen.lua 修改后 local function _render(self, category, context, file) ngx_log(ngx_NOTICE, 'Render ', category, ' to ', file) local render_content = self.renders[category].render(context) -- 先存局部变量,只取第一个返回值 return file_ex.write(file, render_content) end
修改后如果参数恢复正常,就可以确认是多返回值展开的问题。如果还是有问题,就优先排查ffi封装的libc_ex函数的内存安全问题。
内容的提问来源于stack exchange,提问作者daze_lzy
相关产品推荐
相关产品推荐

