From 40da5d42ca33396c234aad06a9024177ae2eaca0 Mon Sep 17 00:00:00 2001 From: Zongyao Chen Date: Fri, 8 May 2026 17:53:53 +0800 Subject: [PATCH] lua-lsm: fix AF_UNIX sockaddr marshalling lengths AF_UNIX socket addresses are not C strings in all cases. Abstract socket names may contain embedded NUL bytes and their usable length comes from the sockaddr length passed by the hook. Carry the sockaddr length together with the pointer exposed to Lua and use that length when formatting AF_UNIX paths. This preserves abstract socket names and avoids reading past the supplied sockaddr. Signed-off-by: Zongyao Chen --- security/lua/lsm_defs.c | 12 +++++- security/lua/lua_net.c | 45 ++++++++++++++++++++-- security/lua/lua_object.h | 80 ++++++++++++++++++++++++++++++++++++++- 3 files changed, 131 insertions(+), 6 deletions(-) diff --git a/security/lua/lsm_defs.c b/security/lua/lsm_defs.c index 788d9d280916..37afc422ccc1 100644 --- a/security/lua/lsm_defs.c +++ b/security/lua/lsm_defs.c @@ -22,6 +22,7 @@ #include /* for __MAP */ #include /* for ktime_get */ #include +#include #include #include #include @@ -2662,8 +2663,17 @@ static int build_sockaddr(lua_State *L, struct sockaddr *address, int addrlen) if (addrlen < SIN6_LEN_RFC2133) return 0; break; + case AF_UNIX: + if (addrlen < offsetof(struct sockaddr_un, sun_path)) + return 0; + break; + } + { + struct lua_sockaddr *p = newsockaddr(L); + + p->addr = address; + p->addrlen = addrlen; } - *newsockaddr(L) = address; return 1; } diff --git a/security/lua/lua_net.c b/security/lua/lua_net.c index ba701c79b019..1f550692d6f0 100644 --- a/security/lua/lua_net.c +++ b/security/lua/lua_net.c @@ -7,6 +7,7 @@ #include "debug.h" #include +#include #include #include #include @@ -70,6 +71,30 @@ static const char *family_tostring(sa_family_t sa_family) return family; } +static size_t sockaddr_un_path_len(const struct sockaddr_un *sunaddr, int addrlen) +{ + int offset = offsetof(struct sockaddr_un, sun_path); + size_t path_len; + + if (addrlen <= offset) + return 0; + + path_len = addrlen - offset; + + /* + * Pathname sockets are C strings; abstract sockets keep their full + * declared length, including embedded NUL bytes. + */ + if (sunaddr->sun_path[0] != '\0') { + const char *nul = memchr(sunaddr->sun_path, '\0', path_len); + + if (nul) + path_len = nul - sunaddr->sun_path; + } + + return path_len; +} + /*********************************** sock ***********************************/ static int net_sock_socket(lua_State *L) @@ -368,11 +393,16 @@ static int sockaddr_addrs(lua_State *L) } lua_pushinteger(L, ntohs(((struct sockaddr_in6 *)sa)->sin6_port)); return 3; - case AF_UNIX: + case AF_UNIX: { + struct sockaddr_un *sunaddr = (struct sockaddr_un *)sa; + int addrlen = tosockaddr_addrlen(L, 1); + size_t path_len = sockaddr_un_path_len(sunaddr, addrlen); + lua_pushstring(L, "unix"); - lua_pushstring(L, ((struct sockaddr_un *)sa)->sun_path); + lua_pushlstring(L, sunaddr->sun_path, path_len); return 2; } + } return 0; } @@ -391,9 +421,16 @@ static int meth_sockaddr_tostring(lua_State *L) l = snprintf(buffer, sizeof(buffer), "sa.inet6: %pISpc", sa); lua_pushlstring(L, buffer, l); break; - case AF_UNIX: - lua_pushfstring(L, "sa.unix: %s", ((struct sockaddr_un *)sa)->sun_path); + case AF_UNIX: { + struct sockaddr_un *sunaddr = (struct sockaddr_un *)sa; + int addrlen = tosockaddr_addrlen(L, 1); + size_t sp_len = sockaddr_un_path_len(sunaddr, addrlen); + + lua_pushliteral(L, "sa.unix: "); + lua_pushlstring(L, sunaddr->sun_path, sp_len); + lua_concat(L, 2); break; + } case AF_NETLINK: lua_pushfstring(L, "sa.netlink: %d", ((struct sockaddr_nl *)sa)->nl_pid); break; diff --git a/security/lua/lua_object.h b/security/lua/lua_object.h index d7588acc1815..94fdaf86ff5a 100644 --- a/security/lua/lua_object.h +++ b/security/lua/lua_object.h @@ -137,7 +137,6 @@ LUA_OBJECT(func, net, tundev, void *, NULL) \ LUA_OBJECT(func, net, socket, struct socket *, NULL) \ LUA_OBJECT(func, net, skb, struct sk_buff *, NULL) \ - LUA_OBJECT(func, net, sockaddr, struct sockaddr *, NULL) \ LUA_OBJECT(func, security, key, struct key *, NULL) \ LUA_OBJECT(object, block, bdev, struct block_device *, NULL) \ LUA_OBJECT(object, fs, inode, struct inode *, NULL) \ @@ -157,4 +156,83 @@ LUA_OBJECTS_LIST #undef LUA_OBJECT +/* + * sockaddr is defined manually because it needs to carry addrlen alongside + * the pointer, so Lua-side methods can derive correct bounds for AF_UNIX + * sun_path (including abstract namespace sockets with embedded NULs). + * + * NOTE: These definitions mirror what LUA_OBJECT_func_DEFINE would generate. + * If that macro is ever extended (e.g. new raw/gc helpers), update this + * section to stay in sync. + */ +struct lua_sockaddr { + struct sockaddr *addr; + int addrlen; +}; + +static inline struct lua_sockaddr *newsockaddr_nomain(lua_State *L) +{ + struct lua_sockaddr *p = lua_newuserdata(L, sizeof(*p)); + + p->addr = NULL; + p->addrlen = 0; + luaL_getmetatable(L, METHOD_NAME(sockaddr)); + lua_setmetatable(L, -2); + return p; +} + +static inline struct lua_sockaddr *newsockaddr(lua_State *L) +{ + struct lua_sockaddr *p = newsockaddr_nomain(L); + + lua_getfield(L, LUA_REGISTRYINDEX, CURR_ENV); + lua_setfenv(L, -2); + return p; +} + +static inline struct lua_sockaddr *tosockaddarp(lua_State *L, int idx) +{ + return (struct lua_sockaddr *)checkudata3(L, idx, METHOD_NAME(sockaddr)); +} + +static inline struct sockaddr *tosockaddr(lua_State *L, int idx) +{ + return tosockaddarp(L, idx)->addr; +} + +static inline int tosockaddr_addrlen(lua_State *L, int idx) +{ + return tosockaddarp(L, idx)->addrlen; +} + +static int rawmeth_sockaddr_type(lua_State *L) +{ + lua_pushstring(L, "sockaddr"); + lua_pushboolean(L, 0); + return 2; +} + +static int rawmeth_sockaddr_tostring(lua_State *L) +{ + struct sockaddr *o = tosockaddr(L, 1); + + lua_pushfstring(L, "sockaddr: <%p>", o); + return 1; +} + +static inline void create_sockaddr_meta(lua_State *L, + const luaL_Reg *funcs, const luaL_Reg *gc) +{ + static const luaL_Reg basemeths[] = { + { "__tostring", rawmeth_sockaddr_tostring }, + { NULL, NULL } + }; + static const luaL_Reg rawmeths[] = { + { "type", rawmeth_sockaddr_type }, + { NULL, NULL } + }; + createmeta3(L, "sockaddr", basemeths, METHOD_NAME_GC(sockaddr), gc, + METHOD_NAME(sockaddr), funcs, METHOD_NAME_RAW(sockaddr), rawmeths); +} + #endif /* ! _SECURITY_LUA_LSM_LUA_OBJECT_H */