diff --git a/security/lua/Makefile b/security/lua/Makefile index f5b4f62da812..8aa55c1166cd 100644 --- a/security/lua/Makefile +++ b/security/lua/Makefile @@ -8,5 +8,6 @@ obj-$(CONFIG_SECURITY_LUA_LSM) := lua-lsm.o ccflags-$(CONFIG_SECURITY_LUA_LSM_DEBUG) += -DDEBUG lua-lsm-y := lsm.o securityfs.o auxlib.o kvcache.o lsm_defs.o \ + lua_mm.o \ lua_kernel.o lua_fs.o lua_net.o \ lua_errno.o lua_capability.o lua_signal.o diff --git a/security/lua/lsm_defs.c b/security/lua/lsm_defs.c index 788d9d280916..454aeaa7c3e3 100644 --- a/security/lua/lsm_defs.c +++ b/security/lua/lsm_defs.c @@ -29,6 +29,7 @@ #include #include "lsm.h" #include "lua_object.h" +#include "lua_mm.h" #include "lsm_defs.h" @@ -1529,13 +1530,13 @@ LUA_LSM_INT_DEFINE4(mmap_file, struct file *, file, unsigned long, reqprot, } /** - * TODO: file_mprotect + * file_mprotect * Default: 0 */ LUA_LSM_INT_DEFINE3(file_mprotect, struct vm_area_struct *, vma, unsigned long, reqprot, unsigned long, prot) { - lua_pushnil(L); /* TODO: vma */ + newmprotectctx(L, vma, reqprot, prot); lua_pushnumber(L, (lua_Number)reqprot); lua_pushnumber(L, (lua_Number)prot); } diff --git a/security/lua/lua_mm.c b/security/lua/lua_mm.c new file mode 100644 index 000000000000..644d22f478c9 --- /dev/null +++ b/security/lua/lua_mm.c @@ -0,0 +1,204 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Lua based LSM + * + * Copyright (C) 2025 The Alibaba Cloud Linux Authors. + */ + +#include "debug.h" +#include +#include +#include +#include +#include +#include +#include +#include "auxlib.h" +#include "lua_object.h" +#include "lua_mm.h" + +#define MPROTECT_CTX_META "lua_lsm.mprotect_ctx" + +struct lua_lsm_mprotect_ctx { + struct file *file; +}; + +static struct lua_lsm_mprotect_ctx *tomprotectctx(lua_State *L, int idx) +{ + return luaL_checkudata(L, idx, MPROTECT_CTX_META); +} + +static void mprotect_ctx_set_bool(lua_State *L, const char *name, bool value) +{ + lua_pushboolean(L, value); + lua_setfield(L, -2, name); +} + +static void mprotect_ctx_set_ulong(lua_State *L, const char *name, + unsigned long value) +{ + lua_pushnumber(L, (lua_Number)value); + lua_setfield(L, -2, name); +} + +static void mprotect_ctx_copy_module(lua_State *L, int idx) +{ + lua_getfield(L, LUA_REGISTRYINDEX, CURR_ENV); + if (!lua_istable(L, -1)) { + lua_pop(L, 1); + return; + } + + lua_pushlightuserdata(L, MODULE_KEY); + lua_gettable(L, -2); + if (lua_islightuserdata(L, -1)) { + lua_pushlightuserdata(L, MODULE_KEY); + lua_pushvalue(L, -2); + lua_rawset(L, idx); + } + lua_pop(L, 2); +} + +static void mprotect_ctx_build_table(lua_State *L, struct vm_area_struct *vma, + unsigned long reqprot, unsigned long prot) +{ + vm_flags_t flags = vma->vm_flags; + bool file_backed = !!vma->vm_file; + bool shared = !!(flags & VM_SHARED); + bool was_readable = !!(flags & VM_READ); + bool was_writable = !!(flags & VM_WRITE); + bool was_executable = !!(flags & VM_EXEC); + bool requested_read = !!(reqprot & PROT_READ); + bool requested_write = !!(reqprot & PROT_WRITE); + bool requested_exec = !!(reqprot & PROT_EXEC); + bool readable = !!(prot & PROT_READ); + bool writable = !!(prot & PROT_WRITE); + bool executable = !!(prot & PROT_EXEC); + bool heap = vma->vm_mm && + vma->vm_start >= vma->vm_mm->start_brk && + vma->vm_end <= vma->vm_mm->brk; + bool stack = vma->vm_mm && + (vma_is_initial_stack(vma) || vma_is_stack_for_current(vma)); + + luaL_checkstack(L, 32, "mprotect_ctx"); + lua_newtable(L); + + mprotect_ctx_copy_module(L, lua_gettop(L)); + + mprotect_ctx_set_ulong(L, "start", vma->vm_start); + mprotect_ctx_set_ulong(L, "finish", vma->vm_end); + mprotect_ctx_set_ulong(L, "size", vma->vm_end - vma->vm_start); + + mprotect_ctx_set_bool(L, "file_backed", file_backed); + mprotect_ctx_set_bool(L, "anonymous", vma_is_anonymous(vma)); + mprotect_ctx_set_bool(L, "shared", shared); + mprotect_ctx_set_bool(L, "private", !shared); + mprotect_ctx_set_bool(L, "heap", heap); + mprotect_ctx_set_bool(L, "stack", stack); + mprotect_ctx_set_bool(L, "file_cow", file_backed && vma->anon_vma); + + mprotect_ctx_set_bool(L, "readable", readable); + mprotect_ctx_set_bool(L, "writable", writable); + mprotect_ctx_set_bool(L, "executable", executable); + mprotect_ctx_set_bool(L, "requested_read", requested_read); + mprotect_ctx_set_bool(L, "requested_write", requested_write); + mprotect_ctx_set_bool(L, "requested_exec", requested_exec); + mprotect_ctx_set_bool(L, "was_readable", was_readable); + mprotect_ctx_set_bool(L, "was_writable", was_writable); + mprotect_ctx_set_bool(L, "was_executable", was_executable); + + mprotect_ctx_set_bool(L, "gaining_exec", + executable && !was_executable); + mprotect_ctx_set_bool(L, "gaining_write", writable && !was_writable); + mprotect_ctx_set_bool(L, "write_to_exec", + was_writable && executable && !was_executable); + mprotect_ctx_set_bool(L, "implied_exec", executable && !requested_exec); + mprotect_ctx_set_bool(L, "wx", writable && executable); +} + +static int mprotect_ctx_file(lua_State *L) +{ + struct lua_lsm_mprotect_ctx *ctx = tomprotectctx(L, 1); + + if (!ctx->file) { + lua_pushnil(L); + return 1; + } + + *newgcfile_nomain(L) = get_file(ctx->file); + lua_getfenv(L, 1); + lua_setfenv(L, -2); + return 1; +} + +static int mprotect_ctx_index(lua_State *L) +{ + lua_getfenv(L, 1); + lua_pushvalue(L, 2); + lua_rawget(L, -2); + if (!lua_isnil(L, -1)) { + lua_remove(L, -2); + return 1; + } + lua_pop(L, 2); + + if (!lua_getmetatable(L, 1)) { + lua_pushnil(L); + return 1; + } + lua_pushvalue(L, 2); + lua_rawget(L, -2); + lua_remove(L, -2); + return 1; +} + +static int mprotect_ctx_gc(lua_State *L) +{ + struct lua_lsm_mprotect_ctx *ctx = tomprotectctx(L, 1); + + if (ctx->file) { + fput(ctx->file); + ctx->file = NULL; + } + return 0; +} + +static int mprotect_ctx_tostring(lua_State *L) +{ + struct lua_lsm_mprotect_ctx *ctx = tomprotectctx(L, 1); + + lua_pushfstring(L, "mprotect_ctx: <%p>", ctx); + return 1; +} + +static void mprotect_ctx_set_meta(lua_State *L) +{ + static const luaL_Reg meth[] = { + { "file", mprotect_ctx_file }, + { "__gc", mprotect_ctx_gc }, + { "__tostring", mprotect_ctx_tostring }, + { NULL, NULL } + }; + + if (luaL_newmetatable(L, MPROTECT_CTX_META)) { + lua_pushstring(L, "cannot set a protected metatable"); + lua_setfield(L, -2, "__metatable"); + luaL_register(L, NULL, meth); + lua_pushcfunction(L, mprotect_ctx_index); + lua_setfield(L, -2, "__index"); + } + lua_setmetatable(L, -2); +} + +void newmprotectctx(lua_State *L, struct vm_area_struct *vma, + unsigned long reqprot, unsigned long prot) +{ + struct lua_lsm_mprotect_ctx *ctx; + + ctx = lua_newuserdata(L, sizeof(*ctx)); + ctx->file = vma->vm_file ? get_file(vma->vm_file) : NULL; + + mprotect_ctx_set_meta(L); + mprotect_ctx_build_table(L, vma, reqprot, prot); + lua_setfenv(L, -2); +} diff --git a/security/lua/lua_mm.h b/security/lua/lua_mm.h new file mode 100644 index 000000000000..d2cf52332985 --- /dev/null +++ b/security/lua/lua_mm.h @@ -0,0 +1,18 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Lua based LSM + * + * Copyright (C) 2025 The Alibaba Cloud Linux Authors. + */ + +#ifndef _SECURITY_LUA_LSM_LUA_MM_H +#define _SECURITY_LUA_LSM_LUA_MM_H + +#include + +struct vm_area_struct; + +void newmprotectctx(lua_State *L, struct vm_area_struct *vma, + unsigned long reqprot, unsigned long prot); + +#endif /* ! _SECURITY_LUA_LSM_LUA_MM_H */