From nobody Sat Feb 7 15:10:26 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 97097224FA for ; Fri, 16 Jan 2026 02:32:19 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1768530739; cv=none; b=KxVa0Y8Flh5p1CTs+2d0dPegtEuPmrcxLwBZJjtL8K3HQVoHcxWgWr6/eIH3f5g+ruOGVLzGVYn9QI9L7xUPMF2x3Nx8owoawO7V67db2pXzrs9V5FpJt1CfcuBgQtlDHOxL5fiEl36QI+CuvYlSh6suKM3dgjCPDN1Vkp8+5mo= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1768530739; c=relaxed/simple; bh=50lxjWm2xLHtsHn/f7Sy+PMwP7CKRZI/eYECykWLSwQ=; h=From:To:Cc:Subject:Date:Message-ID:MIME-Version; b=UReIdabiDTv1tvdivjOadkwE9uTdp1+kFCUIYwiOpcT+ao7LDZpDYdj8/P/6imAyWxcRwHeCJHuPhy7zYkdyNCHtcCZhgbtOR/e/GlLhSw/uKLGJIKTVzN32jZgJMDmOWrCl9dTk2Ruc04HmEArXmUccRJP1+JTPu/U/DlsFnwc= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=Xy+M5dZs; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="Xy+M5dZs" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 0BF32C116D0; Fri, 16 Jan 2026 02:32:18 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1768530739; bh=50lxjWm2xLHtsHn/f7Sy+PMwP7CKRZI/eYECykWLSwQ=; h=From:To:Cc:Subject:Date:From; b=Xy+M5dZs4Q6by0Gp0XwqJ8DUZd014WGYO31wGuNYzFMQzoes089vGjHJw04OTMTET 5IAe9TecsiUAqbd7OL/d7KqVHGPeJSeuRLxQANDDD8Z6CvXrmdizCWoPTAckEUs8M3 aZQK0VcDj8J900jznU5wuX59qLqzK75GhTmlhWIGxRPOlJaOZ0mUjJCt62nsh9lNFC m1GoVL9u80DbPEyh+7AJ/N5xWU+fZVo+l6A6VN43Ad64gmrgy+4sKzMPDzw0U3RClz NT0ZAWYUxA4wxcWegVLLLklqaypHk9Dvh+x26Ekvn5JwfwDWBSzqc1jAKutEt6/bkW sHzy2GmaRo7Uw== From: Dennis Zhou To: Tejun Heo , Christoph Lameter , Andrew Morton Cc: Chris Mason , linux-mm@kvack.org, linux-kernel@vger.kernel.org, Dennis Zhou , Sebastian Andrzej Siewior Subject: [PATCH v2] percpu: add basic double free check Date: Thu, 15 Jan 2026 18:32:16 -0800 Message-ID: <20260116023216.14515-1-dennis@kernel.org> 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" This adds a basic double free check by validating the first bit of the allocation in alloc_map and bound_map are set. If the alloc_map bit is not set, then this means the area is currently unallocated. If the bound_map bit is not set, then we are not freeing from the beginning of the allocation. This is a respin of [1] adding the requested changes from me and Christoph. [1] https://lore.kernel.org/linux-mm/20250904143514.Yk6Ap-jy@linutronix.de/ Signed-off-by: Dennis Zhou Cc: Sebastian Andrzej Siewior --- v2: - moved pcpu_stats_area_dealloc() - added additional check for bit_off out of bounds mm/percpu.c | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/mm/percpu.c b/mm/percpu.c index 81462ce5866e..2f1ac2059a15 100644 --- a/mm/percpu.c +++ b/mm/percpu.c @@ -79,6 +79,7 @@ #include #include #include +#include #include #include #include @@ -1276,18 +1277,24 @@ static int pcpu_alloc_area(struct pcpu_chunk *chunk= , int alloc_bits, static int pcpu_free_area(struct pcpu_chunk *chunk, int off) { struct pcpu_block_md *chunk_md =3D &chunk->chunk_md; + int region_bits =3D pcpu_chunk_map_bits(chunk); int bit_off, bits, end, oslot, freed; =20 lockdep_assert_held(&pcpu_lock); - pcpu_stats_area_dealloc(chunk); =20 oslot =3D pcpu_chunk_slot(chunk); =20 bit_off =3D off / PCPU_MIN_ALLOC_SIZE; + if (unlikely(bit_off < 0 || bit_off >=3D region_bits)) + return 0; + + /* check double free */ + if (!test_bit(bit_off, chunk->alloc_map) || + !test_bit(bit_off, chunk->bound_map)) + return 0; =20 /* find end index */ - end =3D find_next_bit(chunk->bound_map, pcpu_chunk_map_bits(chunk), - bit_off + 1); + end =3D find_next_bit(chunk->bound_map, region_bits, bit_off + 1); bits =3D end - bit_off; bitmap_clear(chunk->alloc_map, bit_off, bits); =20 @@ -1303,6 +1310,8 @@ static int pcpu_free_area(struct pcpu_chunk *chunk, i= nt off) =20 pcpu_chunk_relocate(chunk, oslot); =20 + pcpu_stats_area_dealloc(chunk); + return freed; } =20 @@ -2225,6 +2234,7 @@ static void pcpu_balance_workfn(struct work_struct *w= ork) */ void free_percpu(void __percpu *ptr) { + static DEFINE_RATELIMIT_STATE(_rs, 60 * HZ, DEFAULT_RATELIMIT_BURST); void *addr; struct pcpu_chunk *chunk; unsigned long flags; @@ -2242,6 +2252,13 @@ void free_percpu(void __percpu *ptr) =20 spin_lock_irqsave(&pcpu_lock, flags); size =3D pcpu_free_area(chunk, off); + if (size =3D=3D 0) { + spin_unlock_irqrestore(&pcpu_lock, flags); + + if (__ratelimit(&_rs)) + WARN(1, "percpu double free or bad ptr\n"); + return; + } =20 pcpu_alloc_tag_free_hook(chunk, off, size); =20 --=20 2.43.0