BUG/MINOR: hlua_fcn: fix potential UAF with Queue:pop_wait()

If Queue:pop_wait() excecuted from a stream context and pop_wait() is
aborted due to a Lua or ressource error, then the waiting object pointing
to the task will still be registered, so if the task eventually dissapears,
Queue:push() may try to wake invalid task pointer..

To prevent this bug from happening, we now rely on notification_* API to
deliver waiting signals. This way signals are properly garbage collected
when a lua context is destroyed.

It should be backported in 2.8 with 86fb22c55 ("MINOR: hlua_fcn: add Queue
class").
This patch depends on ("MINOR: task: add thread safe notification_new and
notification_wake variants")
This commit is contained in:
Aurelien DARRAGON 2025-04-01 11:01:45 +02:00
parent b77b1a2c3a
commit c6fa061f22

View File

@ -515,22 +515,10 @@ struct hlua_queue_item {
struct mt_list list;
};
/* used to store wait entries in queue->wait_tasks */
struct hlua_queue_wait
{
struct task *task;
struct mt_list entry;
};
/* This is the memory pool containing struct hlua_queue_item (queue items)
*/
DECLARE_STATIC_POOL(pool_head_hlua_queue, "hlua_queue", sizeof(struct hlua_queue_item));
/* This is the memory pool containing struct hlua_queue_wait
* (queue waiting tasks)
*/
DECLARE_STATIC_POOL(pool_head_hlua_queuew, "hlua_queuew", sizeof(struct hlua_queue_wait));
static struct hlua_queue *hlua_check_queue(lua_State *L, int ud)
{
return hlua_checkudata(L, ud, class_queue_ref);
@ -557,8 +545,6 @@ static int hlua_queue_push(lua_State *L)
{
struct hlua_queue *queue = hlua_check_queue(L, 1);
struct hlua_queue_item *item;
struct mt_list back;
struct hlua_queue_wait *waiter;
if (lua_gettop(L) != 2 || lua_isnoneornil(L, 2)) {
luaL_error(L, "unexpected argument");
@ -583,9 +569,7 @@ static int hlua_queue_push(lua_State *L)
MT_LIST_APPEND(&queue->list, &item->list);
/* notify tasks waiting on queue:pop_wait() (if any) */
MT_LIST_FOR_EACH_ENTRY_LOCKED(waiter, &queue->wait_tasks, entry, back) {
task_wakeup(waiter->task, TASK_WOKEN_MSG);
}
notification_wake_mt(&queue->wait_tasks);
lua_pushboolean(L, 1);
return 1;
@ -641,24 +625,25 @@ static int hlua_queue_pop(lua_State *L)
static int _hlua_queue_pop_wait(lua_State *L, int status, lua_KContext ctx)
{
struct hlua_queue *queue = hlua_check_queue(L, 1);
struct hlua_queue_wait *wait = lua_touserdata(L, 2);
/* new pop attempt */
if (!_hlua_queue_pop(L, queue)) {
struct hlua *hlua;
hlua = hlua_gethlua(L);
if (!notification_new_mt(&hlua->com, &queue->wait_tasks, hlua->task)) {
lua_pushnil(L);
return 1; /* memory error, return nil */
}
hlua_yieldk(L, 0, 0, _hlua_queue_pop_wait, TICK_ETERNITY, 0); // wait retry
return 0; // never reached, yieldk won't return
}
/* remove task from waiting list */
MT_LIST_DELETE(&wait->entry);
pool_free(pool_head_hlua_queuew, wait);
return 1; // success
}
static int hlua_queue_pop_wait(lua_State *L)
{
struct hlua_queue *queue = hlua_check_queue(L, 1);
struct hlua_queue_wait *wait;
struct hlua *hlua;
BUG_ON(!queue);
@ -678,21 +663,6 @@ static int hlua_queue_pop_wait(lua_State *L)
/* no pending items, waiting required */
wait = pool_alloc(pool_head_hlua_queuew);
if (!wait) {
lua_pushnil(L);
return 1; /* memory error, return nil */
}
wait->task = hlua->task;
MT_LIST_INIT(&wait->entry);
/* add task to queue's wait list */
MT_LIST_TRY_APPEND(&queue->wait_tasks, &wait->entry);
/* push wait entry at index 2 on the stack (queue is already there) */
lua_pushlightuserdata(L, wait);
/* Go to waiting loop which immediately performs a new attempt to make
* sure we didn't miss a push during the wait entry initialization.
*
@ -738,20 +708,8 @@ static int hlua_queue_new(lua_State *L)
static int hlua_queue_gc(struct lua_State *L)
{
struct hlua_queue *queue = hlua_check_queue(L, 1);
struct hlua_queue_wait *wait;
struct hlua_queue_item *item;
/* Purge waiting tasks (if any)
*
* It is normally not expected to have waiting tasks, except if such
* task has been aborted while in the middle of a queue:pop_wait()
* function call.
*/
while ((wait = MT_LIST_POP(&queue->wait_tasks, typeof(wait), entry))) {
/* free the wait entry */
pool_free(pool_head_hlua_queuew, wait);
}
/* purge remaining (unconsumed) items in the queue */
while ((item = MT_LIST_POP(&queue->list, typeof(item), list))) {
/* free the queue item */