From nobody Mon Jun 8 11:01:13 2026 Received: from mail-pl1-f174.google.com (mail-pl1-f174.google.com [209.85.214.174]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id E088C3264F6 for ; Fri, 29 May 2026 13:12:51 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.214.174 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780060373; cv=none; b=aVTcD+/RfwqOZTdH5QQ0GTwLYPpWyz1xgjAaahl0BwVbibnMJzr5UJh/eLBp82hIlJJOAcOURMzyDn7FQYekMtSUI8UC3W2pJngCJklojphqBUg+kZ3lvdLKdwWQaFibb46KTIT+zYQYngS2S1yLDsc/lxA8eQbx6KpAzltgxdI= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780060373; c=relaxed/simple; bh=dGRgEL6swzJB8YYgGNjvjlHYTTzZHRh9CGbzH7Nuupw=; h=From:To:Cc:Subject:Date:Message-ID:MIME-Version; b=LtHEyP0sNaSR3CSQ6Bl4BAjo/OkeKQ8fo9rkn7ZcrQQg+ZlRh34jT7xsMD3KYRhTEHZKKhnnmHDfgHen7Yt8+LPrMsIEbJniU0IiOIcG9irRLgZYslwuI8d32AHBed3CQPXSDwnBJe8qOemdj71/Tz9FLE3u+doEVJmw2W2u2PA= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=DTfcf8Zy; arc=none smtp.client-ip=209.85.214.174 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="DTfcf8Zy" Received: by mail-pl1-f174.google.com with SMTP id d9443c01a7336-2bd80b3aa13so92685395ad.0 for ; Fri, 29 May 2026 06:12:51 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1780060371; x=1780665171; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:from:to:cc:subject:date:message-id:reply-to; bh=6vmq+XnGc2xFQDaiWK+vJ6XdpgCZZ/AvN0Z8WhEzc4M=; b=DTfcf8ZyjZXV6ix4Sj1/T61yTBqPzQJdDtoEhEhsUOrFFN9B36XDzbx2aE/kfk96zl 2DU6Q8ZOGsv3P47SeWqyXuov2jTyelv/Ngo7bW2xRp6clDpXBthuuRbhvb3FUstBiSRA GDTO0whcS9BYdyno1Nv0HE/LUU2oAaOyszarWyy+9c/yYzxJkgkriwEouQvhgNzD/uhv zYbpVKY/Tg7aA1dN2FvY4LBXmO/z9/cwjFdo+ZVG5WQ3O/0VNzaZYYbXiAX/Yq8uBHdi ngt5FAJNc2nsodHciGBT5212sBf4De9V50GenGSZFa8maVFAZP2jhrjHWSPjh8psF0Ja 3Dbw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1780060371; x=1780665171; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:x-gm-gg:x-gm-message-state:from:to:cc:subject:date :message-id:reply-to; bh=6vmq+XnGc2xFQDaiWK+vJ6XdpgCZZ/AvN0Z8WhEzc4M=; b=EjmZj3ueQBg6cmdXb9f1boSz69zs2EerOdNPjseUnECxI99xoowl6BgbeqfINqDz57 /rciFjdsFd1NIYq/NV5IcUPZH9Gt8IkNJoe680hhuqflnE0LApXSlKmMyhFlGBiiOuUQ AGnRdtdWQgC3RFnEatPApe6xVIu/M0l1iE4glExjC6HZaqW29U+agApTQBYmHO9Zk48m Vw3Rq6j8BrMt+6X9M722lNKKNkalLz40kt2fajmrM1HmYbMAN8tjH/jGI6i+/zazmeJw WgjAtBXFKUiw8RddGSXb64YpQtvMDdyRJsIrLU227w7EJdnk0QtiybCedydOUUVNtIo7 doeQ== X-Forwarded-Encrypted: i=1; AFNElJ/dQcvLBlGvkNp4tW5Rl9x1uEWZlZATgzK+F1yY9QgVuBD+8QlOB1KFbdAVp6cgDpDNEdy626czYOKk7+w=@vger.kernel.org X-Gm-Message-State: AOJu0Yxf9yAbGV4x4xVorRr7JlZiI+O9yghETutzFKMAY6/so33NvCZy wtOlWjRMouNrppk3sgW+VmW12lDHUJLILpXA8mZk9kySPsD9yYk085yP X-Gm-Gg: Acq92OEsevxlP49LYxxIQJahPP7NROQ2k6CsNScLKvlNTghQQB071+wIo01iZMODgtg EMi+eIwm8Y1BrthOPG4qbiy1rtI/govDwoUWGT62AVrfBceZxyXlfB/+cRUw+pe6PbCD+deUq9W FQRpfuoyHhtTBDHCiS5rlaOcZzd9GBcA+V319RKvTzW7Zp90BsRCOAZZa77LuKvD8VAM6ylVh/5 UByuIr7r0JzwMqz6LDyQBdSiVouycCZRThHxMEU1r2XqeCq19aWKOOwwZs19F2R7F2mUSPTKfq3 ggiq/DpIPFTAKrX5UXIZJlOBbnyazcN47eA3lfctOSp+klNDqSJvw+7x2OvZrTUsqhwnYGsr8AG leE1eLs8uFSW6a7OHJiMXeNshbbDTSTXNE4Mw5y+va7xlkvpDcFDviqajnFOIUiQCbdOOjXdi5u QQIvI0WLgs7i8+x0NG5nHKBOf0aUrCRq8q2vIxxv2O9GtJjf/WAgNTuvA0X4UPE5x5ja9285RXP 6NO1DMtq46zJ/iICg== X-Received: by 2002:a17:902:ce0c:b0:2bd:6327:b4f5 with SMTP id d9443c01a7336-2bf20cdbdd2mr34287215ad.40.1780060370904; Fri, 29 May 2026 06:12:50 -0700 (PDT) Received: from deepanshu-kernel-hacker.. ([2405:201:682f:383f:14aa:de8f:50e3:19a1]) by smtp.gmail.com with ESMTPSA id d9443c01a7336-2bf239fd85dsm19191035ad.25.2026.05.29.06.12.47 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 29 May 2026 06:12:50 -0700 (PDT) From: Deepanshu Kartikey To: viro@zeniv.linux.org.uk, brauner@kernel.org, jack@suse.cz Cc: linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org, Deepanshu Kartikey , syzbot+69a3d7738ad3aa175caf@syzkaller.appspotmail.com Subject: [PATCH v3] eventpoll: fix use-after-free in clear_tfile_check_list() Date: Fri, 29 May 2026 18:42:42 +0530 Message-ID: <20260529131242.89876-1-kartikey406@gmail.com> X-Mailer: git-send-email 2.43.0 Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" syzbot reports a slab-use-after-free read in clear_tfile_check_list() during EPOLL_CTL_ADD when ep_insert() takes an error path that calls ep_remove() before do_epoll_ctl_file() drains ctx->tfile_check_list. ep_remove_file() decides whether to kmem_cache_free() a struct epitems_head by testing v->next =3D=3D NULL, on the convention that NULL means "this head is not linked on any check list". list_file() pushes a head onto ctx->tfile_check_list by storing the previous list head into head->next; when the list is empty that store is NULL, so the head that ends up at the tail of the check list also has next =3D=3D NULL. ep_remove_file() then misreads the tail as "not linked" and frees it. clear_tfile_check_list() later walks the list and dereferences head->next on the freed object: BUG: KASAN: slab-use-after-free in clear_tfile_check_list+0x114/0x380 fs/eventpoll.c:2443 Allocated by task 5985: ep_attach_file fs/eventpoll.c:1751 [inline] ep_register_epitem fs/eventpoll.c:1833 [inline] ep_insert+0x512/0x1820 fs/eventpoll.c:1876 Freed by task 5985: ep_remove+0x155/0x2a0 fs/eventpoll.c:1135 ep_insert+0x1372/0x1820 Terminate ctx->tfile_check_list with EP_UNACTIVE_PTR (the existing non-NULL sentinel used elsewhere in this file for inactive list slots) so that next =3D=3D NULL unambiguously means "not on any check list". list_file() stores the sentinel when the list is empty, reverse_path_check() and clear_tfile_check_list() stop the walk on the sentinel, and do_epoll_ctl_file() seeds ctx->tfile_check_list with it. The guard in ep_remove_file() then correctly refuses to free a head while it remains on the check list. Reported-by: syzbot+69a3d7738ad3aa175caf@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=3D69a3d7738ad3aa175caf Link: https://lore.kernel.org/all/20260523091107.61880-1-kartikey406@gmail.= com/T/ [v1] Link: https://lore.kernel.org/all/20260529042507.87593-1-kartikey406@gmail.= com/T/ [v2] Signed-off-by: Deepanshu Kartikey --- v3: Drop the (struct epitems_head *) casts on EP_UNACTIVE_PTR. void * converts implicitly to typed pointers in C; the casts were noise. v2: Reuse the existing EP_UNACTIVE_PTR sentinel instead of adding a new EP_TFILE_LIST_END define, per review feedback. --- fs/eventpoll.c | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/fs/eventpoll.c b/fs/eventpoll.c index a569e98d4a99..1756c5f489a0 100644 --- a/fs/eventpoll.c +++ b/fs/eventpoll.c @@ -472,7 +472,13 @@ static void list_file(struct file *file, struct ep_ctl= _ctx *ctx) =20 head =3D container_of(file->f_ep, struct epitems_head, epitems); if (!head->next) { - head->next =3D ctx->tfile_check_list; + /* + * Terminate the check list with EP_UNACTIVE_PTR (non-NULL) + * so that head->next =3D=3D NULL unambiguously means "not on + * any check list". ep_remove_file() relies on that + * invariant to decide whether the head is safe to free. + */ + head->next =3D ctx->tfile_check_list ? : EP_UNACTIVE_PTR; ctx->tfile_check_list =3D head; } } @@ -1685,7 +1691,7 @@ static int reverse_path_check(struct ep_ctl_ctx *ctx) { struct epitems_head *p; =20 - for (p =3D ctx->tfile_check_list; p; p =3D p->next) { + for (p =3D ctx->tfile_check_list; p !=3D EP_UNACTIVE_PTR; p =3D p->next) { int error; path_count_init(ctx); rcu_read_lock(); @@ -2438,11 +2444,12 @@ static int ep_loop_check(struct ep_ctl_ctx *ctx, st= ruct eventpoll *ep, static void clear_tfile_check_list(struct ep_ctl_ctx *ctx) { rcu_read_lock(); - while (ctx->tfile_check_list) { + while (ctx->tfile_check_list !=3D EP_UNACTIVE_PTR) { struct epitems_head *head =3D ctx->tfile_check_list; ctx->tfile_check_list =3D head->next; unlist_file(head); } + ctx->tfile_check_list =3D EP_UNACTIVE_PTR; rcu_read_unlock(); } =20 @@ -2601,7 +2608,7 @@ int do_epoll_ctl_file(struct file *f, int op, struct = epoll_key *tf, int full_check; struct eventpoll *ep; struct epitem *epi; - struct ep_ctl_ctx ctx =3D { }; + struct ep_ctl_ctx ctx =3D { .tfile_check_list =3D EP_UNACTIVE_PTR }; =20 /* The target file descriptor must support poll */ if (!file_can_poll(tf->file)) --=20 2.43.0