From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from mx0b-0031df01.pphosted.com (mx0b-0031df01.pphosted.com [205.220.180.131]) (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 E1478393DFB for ; Sat, 13 Jun 2026 17:22:06 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=205.220.180.131 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781371328; cv=none; b=PedwlF6RNjRGrt7G1bCXkhwetONNfQ5X216nAInOBJEF+fVyaAbAmC1LL3Vst5llbFLtqkX44ZvMXvrve/icZK42fVTfpr7L+I4iEVNw8OPhtHT68eJ+0uWY/igLGD5IRhTSFlBYKPoqpBiwEPAORoByThbeYPxTdutmL+O1Ltg= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781371328; c=relaxed/simple; bh=poCfMca4I5bU16k0x2EJY1kUzaPSBtmGjAWZRE/6A1o=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=rex21kMNEHzZ58mmas0tXyJm/WIR2S/VowztnB+7mMyKL8u36xGLI9/kyeIW4BKTKhIt5Ee+SF/KMRDeTqusvh7P0NBMHVNdUkliwPOBGy8wNj9pwI5F1kZwz9zUfuZTqPl6FV9b5FJWLbhEsPw4ZX1pFc6xYQ7A1/cjhLoJ0bA= ARC-Authentication-Results:i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=oss.qualcomm.com; spf=pass smtp.mailfrom=oss.qualcomm.com; dkim=pass (2048-bit key) header.d=qualcomm.com header.i=@qualcomm.com header.b=iVjSsrb/; dkim=pass (2048-bit key) header.d=oss.qualcomm.com header.i=@oss.qualcomm.com header.b=bd9x+jFw; arc=none smtp.client-ip=205.220.180.131 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=oss.qualcomm.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=oss.qualcomm.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=qualcomm.com header.i=@qualcomm.com header.b="iVjSsrb/"; dkim=pass (2048-bit key) header.d=oss.qualcomm.com header.i=@oss.qualcomm.com header.b="bd9x+jFw" Received: from pps.filterd (m0279870.ppops.net [127.0.0.1]) by mx0a-0031df01.pphosted.com (8.18.1.11/8.18.1.11) with ESMTP id 65DGRCGA3473452 for ; Sat, 13 Jun 2026 17:22:05 GMT DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=qualcomm.com; h= cc:content-transfer-encoding:content-type:date:from:in-reply-to :message-id:mime-version:references:subject:to; s=qcppdkim1; bh= ZE6jPRdfaoewtNIfPrpNjI8xcezU+gskgS6mTgukQkI=; b=iVjSsrb/w+HmpHn0 xmXyx/qzypr06CxDILrTITkNa82ugjOXc8TeWL7dvyQlvCNwa7kc6n+YM+eHVeqP 2c0/GXJ+mfC862xvdh7emtUJ140SiG0OrRktal89n+9pAfj/ZY/D5rLuAIVgrTSO gfoB6E8sN4x8a6qzG38Zos4QlsMe3/fApVQ7b6G1IbgJm03O8Ns28/rQ1m4Ptgz5 BmbA3RgQCsghsZ+KJ0WGTFnb/UTxRaCkPu4wu6vE9fVkD8zx9qCEJuc87RIxdLeC 0dNLAZ22iR0ApvrrmAD4j7Zc+1QYltXC8wOAlhMNQ1cteUg3voAzGQsH5oTGdgPz qGK1tA== Received: from mail-pf1-f200.google.com (mail-pf1-f200.google.com [209.85.210.200]) by mx0a-0031df01.pphosted.com (PPS) with ESMTPS id 4eryc6smuk-1 (version=TLSv1.3 cipher=TLS_AES_128_GCM_SHA256 bits=128 verify=NOT) for ; Sat, 13 Jun 2026 17:22:05 +0000 (GMT) Received: by mail-pf1-f200.google.com with SMTP id d2e1a72fcca58-84240683a82so1460214b3a.1 for ; Sat, 13 Jun 2026 10:22:05 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=oss.qualcomm.com; s=google; t=1781371325; x=1781976125; darn=vger.kernel.org; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:from:to:cc:subject:date:message-id :reply-to; bh=ZE6jPRdfaoewtNIfPrpNjI8xcezU+gskgS6mTgukQkI=; b=bd9x+jFwWl/iUUY4peI8sRQLMMkww7/cmR7Oo0JwOuiqFaNWQccWWNjWkhFaMLA4pF JJ0wFmWP6cFvJZkHSyOmlypOanzTxtLiH919q2kaTirm6+tYeWedYc5GPA5120HfbuSs b+nfn0ggRZNiFmzr7rw9l7mYIB3QP+uYkbqD8fM0RFEH/JWDWZmnjRajgdQBVQFoFCDZ SvUkXJ4NKwkjSV9Bb0Krgp+veQKUYZ7lntwgcngL6qTTkXLwibU8vEbozbBXY52/riuV PnoECIGGYbOgiObAQM2yRoPABjNpLdQp7Lzd9gTUSn4rwNVU/rBaT50+cWlXMDje3Q3r 3/yg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1781371325; x=1781976125; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:x-gm-gg:x-gm-message-state:from:to :cc:subject:date:message-id:reply-to; bh=ZE6jPRdfaoewtNIfPrpNjI8xcezU+gskgS6mTgukQkI=; b=gsYV12vgio/7J2J+IwqL0fzKMCUo6aNX1fxoWVFfkn0zfUDKrmtKqpXdakaOB5UH/y NnSn9JtVfGYf2G0U65ATY4OS+ZhbdMxhBjS62k2pLS8vA+0rHUmflVQBygRNODrG1XyF Pi4DYe4lLTYkTLUoVThL5Y9oAi1IxtiB+65hAxXjGvnbaNsl2DCdtZBj7MFBgQIoV2ub bDd+at/ItZhcC1S3/rF5uWqcdj4tPUqqFFqGDyzIZFC3NhyrQqv0ztJM6X8NOPoFTI1g eGGnl9PMBU7aVGxuuMdikcfqLbMuKo4l5sxV+zLjussv6+XXzYBLlhErWWTXwijnZsrM 1lUQ== X-Forwarded-Encrypted: i=1; AFNElJ9qtJIpPh8p9K1D9sfOrKGULzQnH2lPfNfr5sUNooN9DVmj3cDG2YazaNC1debS81xpyragmpf1jhB/A2w=@vger.kernel.org X-Gm-Message-State: AOJu0YxRukataMGqtprpAmWXOTjpHDuVjfA1tEOedEYFzqYkXT1gbmLd 8UgeYShEmnB0DC6XWPeHSOq1dA6IH2C6lOXb7JHe/hvlc+jkkUj99aZlhtNwOS+9TKs9gyCzvgF KCkX3+QBLaxGNyZFkXC51nGQtQkoUhyNret66BpgKiOq8imI61mrk6Seszjwbggz9YuU= X-Gm-Gg: Acq92OGeohGpWFyajmVuG9h06lxOi3oKgC7tS9Xdbj1nGeqH2nrn6xeDs9DvbyNSNIe Fwjut9wgSaOoRw7PC6zb/j7rdR6JwpiNPUEKGL/yMvmJUAmj+N8aAmJnSezjLLNhZxNuEjzRqdo hLNNIwmRd0l4gkGnXebIUQeqiH0Se+OmMlmI3KbB5Mrvt+n/ZozwSFEwwoUiB6+3YtoeJZyYlRZ c2meGJg0vsXkkwSc491E8JMOJ3BKmWrV5lIQi2wrjoBhNPznsUW5oWgyXF6/xzxoV0EwdHPWGzP 53rXy4838c2mhG7/wGUz2QxuySOzJf2PKqq6EnwICuPcPcC9JcMnaeeWI319NQkH5/FKv7or/SJ 3T6XRVvMy/OiYakkbWLGNM7L4uP4MAmJ6GTQPiVJXNLXztwy51TakIA== X-Received: by 2002:a05:6a00:2351:b0:842:5f67:eada with SMTP id d2e1a72fcca58-8434ce4070amr6578014b3a.5.1781371324374; Sat, 13 Jun 2026 10:22:04 -0700 (PDT) X-Received: by 2002:a05:6a00:2351:b0:842:5f67:eada with SMTP id d2e1a72fcca58-8434ce4070amr6577978b3a.5.1781371323837; Sat, 13 Jun 2026 10:22:03 -0700 (PDT) Received: from hu-pranarya-hyd.qualcomm.com ([202.46.22.19]) by smtp.gmail.com with ESMTPSA id d2e1a72fcca58-8434accbec5sm5390913b3a.16.2026.06.13.10.21.55 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 13 Jun 2026 10:22:03 -0700 (PDT) From: Pranjal Arya Date: Sat, 13 Jun 2026 22:49:54 +0530 Subject: [PATCH RFC 12/12] mm/vmalloc: harden bump-allocator alloc/free against UBSAN array bounds Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: 7bit Message-Id: <20260613-vmalloc_maple-v1-12-0aa740bb944b@oss.qualcomm.com> References: <20260613-vmalloc_maple-v1-0-0aa740bb944b@oss.qualcomm.com> In-Reply-To: <20260613-vmalloc_maple-v1-0-0aa740bb944b@oss.qualcomm.com> To: Andrew Morton , Uladzislau Rezki , "Liam R. Howlett" , Alice Ryhl , Andrew Ballance Cc: linux-arm-msm@vger.kernel.org, linux-mm@kvack.org, linux-kernel@vger.kernel.org, maple-tree@lists.infradead.org, Lorenzo Stoakes , Pranjal Shrivastava , Will Deacon , Suzuki K Poulose , Neil Armstrong , Mostafa Saleh , Balbir Singh , Suren Baghdasaryan , Marco Elver , Dmitry Vyukov , Alexander Potapenko , Shuah Khan , Dev Jain , Brendan Jackman , Puranjay Mohan , Santosh Shukla , Wyes Karny , Pranjal Arya , Sudeep Holla X-Mailer: b4 0.15.2 X-Developer-Signature: v=1; a=ed25519-sha256; t=1781371215; l=7915; i=pranjal.arya@oss.qualcomm.com; s=20260516; h=from:subject:message-id; bh=poCfMca4I5bU16k0x2EJY1kUzaPSBtmGjAWZRE/6A1o=; b=kGgYknKWsSNBxkdk0W8RRR1QefOuH5dooHWWewlUdfNTWdwNuW3qNCnaMf0NIthki3GTPNlfk WKtc4hRw6hxDBELESN9aTttJLWCPG90L6t21yIt7WTILVL//TKeMdM4 X-Developer-Key: i=pranjal.arya@oss.qualcomm.com; a=ed25519; pk=ymtcTlccEIDsi3ErhpjIoZZHKdPBYWGWW0Lchs5MsbE= X-Proofpoint-Spam-Info: AW1haW4tMjYwNjEzMDE4MCBTYWx0ZWRfX7SDpCJ7/NK/f zy0C8Dk8hga9RKVjovf4O9KjsRtB4qa7mKFQgxkEzzuH2Cozk8cCLHIlps4MtR+Xfmv2RBMtLb9 UDBTIrW7FR4fUTE0sHkRuIuh/Mg7G40= X-Authority-Analysis: v=2.4 cv=Oop/DS/t c=1 sm=1 tr=0 ts=6a2d91bd cx=c_pps a=mDZGXZTwRPZaeRUbqKGCBw==:117 a=fChuTYTh2wq5r3m49p7fHw==:17 a=IkcTkHD0fZMA:10 a=FelO9ux0wxsA:10 a=s4-Qcg_JpJYA:10 a=VkNPw1HP01LnGYTKEx00:22 a=u7WPNUs3qKkmUXheDGA7:22 a=gowsoOTTUOVcmtlkKump:22 a=EUspDBNiAAAA:8 a=a2jIHL7vm5qZbxVYpdMA:9 a=QEXdDO2ut3YA:10 a=zc0IvFSfCIW2DFIPzwfm:22 X-Proofpoint-GUID: 4KZw-ePEUv_8GP4kgImLtqTFOH6DB_ZF X-Proofpoint-ORIG-GUID: 4KZw-ePEUv_8GP4kgImLtqTFOH6DB_ZF X-Proofpoint-Spam-Details-Enc: AW1haW4tMjYwNjEzMDE4MCBTYWx0ZWRfX2Pbd3KW2CHx+ HSgXEW6bj4/pnIKMT3PswP1KwpnA75fuZwx84GmP/IYd36vqL8gj4qMO7hrtEXsZDafWsAXcaBe 7FF6pn8Jo0561+CZMy2DAzuF07FvU9BGBs1QvabAs834Hhiwci9/uAh25axQZpoVlLsBwa4Izj5 vNOEjVs8ClVkgzbO5hZMOsLCEzEyeBUbHZngzAUvCboorPgArfZDdSS/7rZyjyChXbtrHRfKvso QmWpLTNRifi7cSla/ZZ+OjxbT1mJDO/RGLPBKnu34Bb3eZgxD8yMSj9x1M4jZgGxiIahMZFfjeS K2tZwpZsJed9fg/XF6yhhmOC5Q/zNRrTbwGM6jgd5PRVaRKr6pgDF/TW0gfsELqHM9zX5bzBNP2 9omsuCVcSE31Mvjg2q6neO+nT8iL0prPtq5vUx9yhY0YkRCNk0vb47h4V5JoYeiMOmi7ad4nLdj IQGjlclNMSGtsGmiyGQ== X-Proofpoint-Virus-Version: vendor=baseguard engine=ICAP:2.0.293,Aquarius:18.0.1143,Hydra:6.1.125,FMLib:17.12.100.49 definitions=2026-06-13_03,2026-06-12_03,2025-10-01_01 X-Proofpoint-Spam-Details: rule=outbound_notspam policy=outbound score=0 priorityscore=1501 phishscore=0 impostorscore=0 lowpriorityscore=0 clxscore=1015 bulkscore=0 spamscore=0 malwarescore=0 suspectscore=0 adultscore=0 classifier=typeunknown authscore=0 authtc= authcc= route=outbound adjust=0 reason=mlx scancount=1 engine=8.22.0-2606040000 definitions=main-2606130180 Real-hardware testing on a Snapdragon X1E80100 exposed a panic during boot-time module loading via finit_module -> kernel_read_file -> vmalloc: Internal error: UBSAN: array index out of bounds Call trace: vmap_bump_alloc alloc_vmap_area __vmalloc_node_range_noprof vmalloc_noprof kernel_read_file __arm64_sys_finit_module UBSAN's array-bounds sanitiser triggers on the indexed write loop: for (i = 0; i < n_pages; i++) chunk->page_va[idx + i] = va; Harden the bump path: - Centralise the eligibility predicate in vmap_bump_eligible() and add it to alloc_vmap_area() so vmap_bump_refill() is only called for requests the bump path can actually serve. Add PAGE_ALIGNED(size) and align > 0 to the predicate (defensive; alloc_vmap_area's callers always satisfy these but the explicit check is cheap and prevents the trap path from being entered with bad inputs). - In vmap_bump_alloc(), use check_add_overflow() for the new bump pointer, validate aligned >= chunk->base (defensive against metadata corruption), and bounds-check idx and idx + n_pages against VMAP_BUMP_CHUNK_PAGES before touching page_va[]. Replace the indexed page_va[] store loop with a pointer walk: slot = &chunk->page_va[idx]; for (i = n_pages; i > 0; i--) *slot++ = va; The pointer-increment form is not subject to the array-bounds sanitiser instrumentation that fires on chunk->page_va[idx + i]. - In vmap_bump_unlink(), validate n_pages > 0 and n_pages <= VMAP_BUMP_CHUNK_PAGES - idx before the memset, so a corrupted va->va_end cannot drive a write past the end of page_va[]. - Track the chunk's owner CPU at refill time and compare against per_cpu(vmap_bump_cur, owner_cpu) on unlink. The previous this_cpu_read(vmap_bump_cur) compared the chunk against the *current* CPU's chunk, which is wrong when free runs on a CPU other than the chunk owner: it could either retire a chunk that is still the owner's current, or skip retirement on a chunk that has already been replaced. No semantic change to the bump-path policy or to the addresses returned. Builds clean on x86_64 and arm64 (full bzImage / Image). Signed-off-by: Pranjal Arya --- mm/vmalloc.c | 62 +++++++++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 49 insertions(+), 13 deletions(-) diff --git a/mm/vmalloc.c b/mm/vmalloc.c index 6991054e1cba..03f10b6b815c 100644 --- a/mm/vmalloc.c +++ b/mm/vmalloc.c @@ -2508,6 +2508,7 @@ struct vmap_bump_chunk { unsigned long limit; unsigned long bump; atomic_t alloced; /* # outstanding pages */ + int owner_cpu; struct list_head link; /* on vmap_bump_chunks */ struct rcu_head rcu; /* deferred free */ struct vmap_area *page_va[VMAP_BUMP_CHUNK_PAGES]; @@ -2517,6 +2518,16 @@ static DEFINE_PER_CPU(struct vmap_bump_chunk *, vmap_bump_cur); static LIST_HEAD(vmap_bump_chunks); static DEFINE_SPINLOCK(vmap_bump_chunks_lock); +static __always_inline bool +vmap_bump_eligible(unsigned long size, unsigned long align, + unsigned long vstart, unsigned long vend) +{ + return vstart == VMALLOC_START && vend == VMALLOC_END && + size > 0 && PAGE_ALIGNED(size) && + size <= VMAP_BUMP_CHUNK_SIZE / 2 && + align > 0 && align <= VMAP_BUMP_CHUNK_SIZE / 2; +} + /* * Coarse [lo, hi) bounds covering every active vmap_bump_chunk's * range. vmap_chunk_lookup() rejects out-of-range addresses (e.g. @@ -2582,11 +2593,10 @@ vmap_bump_alloc(unsigned long size, unsigned long align, { struct vmap_bump_chunk *chunk; struct vmap_area *va; - unsigned long aligned, idx, n_pages, i; + struct vmap_area **slot; + unsigned long aligned, new_bump, idx, n_pages, i; - if (vstart != VMALLOC_START || vend != VMALLOC_END || - size == 0 || size > VMAP_BUMP_CHUNK_SIZE / 2 || - align > VMAP_BUMP_CHUNK_SIZE / 2) + if (!vmap_bump_eligible(size, align, vstart, vend)) return NULL; va = kmem_cache_alloc_node(vmap_area_cachep, gfp_mask, node); @@ -2607,22 +2617,34 @@ vmap_bump_alloc(unsigned long size, unsigned long align, kmem_cache_free(vmap_area_cachep, va); return NULL; } + aligned = ALIGN(chunk->bump, align); - if (aligned + size > chunk->limit) { + if (aligned < chunk->base || + check_add_overflow(aligned, size, &new_bump) || + new_bump > chunk->limit) { preempt_enable(); kmem_cache_free(vmap_area_cachep, va); return NULL; } - chunk->bump = aligned + size; + idx = vmap_chunk_page_idx(chunk, aligned); n_pages = size >> PAGE_SHIFT; - for (i = 0; i < n_pages; i++) - chunk->page_va[idx + i] = va; + if (unlikely(idx >= VMAP_BUMP_CHUNK_PAGES || + n_pages > VMAP_BUMP_CHUNK_PAGES - idx)) { + preempt_enable(); + kmem_cache_free(vmap_area_cachep, va); + return NULL; + } + + chunk->bump = new_bump; + slot = &chunk->page_va[idx]; + for (i = n_pages; i > 0; i--) + *slot++ = va; atomic_add(n_pages, &chunk->alloced); preempt_enable(); va->va_start = aligned; - va->va_end = aligned + size; + va->va_end = new_bump; va->vm = NULL; /* * Encode the destination vmap_node so the existing per-node pool @@ -2651,6 +2673,7 @@ vmap_bump_refill(gfp_t gfp_mask) { struct vmap_bump_chunk *new_chunk; unsigned long base; + int cpu; new_chunk = kvzalloc(sizeof(*new_chunk), gfp_mask); if (!new_chunk) @@ -2670,6 +2693,7 @@ vmap_bump_refill(gfp_t gfp_mask) new_chunk->limit = base + VMAP_BUMP_CHUNK_SIZE; new_chunk->bump = base; atomic_set(&new_chunk->alloced, 0); + new_chunk->owner_cpu = -1; INIT_LIST_HEAD(&new_chunk->link); spin_lock(&vmap_bump_chunks_lock); @@ -2681,6 +2705,8 @@ vmap_bump_refill(gfp_t gfp_mask) spin_unlock(&vmap_bump_chunks_lock); preempt_disable(); + cpu = smp_processor_id(); + new_chunk->owner_cpu = cpu; this_cpu_write(vmap_bump_cur, new_chunk); preempt_enable(); @@ -2699,6 +2725,7 @@ static struct vmap_area * vmap_bump_unlink(unsigned long addr) { struct vmap_bump_chunk *chunk; + struct vmap_bump_chunk *owner_cur; struct vmap_area *va; unsigned long idx, n_pages; @@ -2715,6 +2742,8 @@ vmap_bump_unlink(unsigned long addr) return NULL; n_pages = (va->va_end - va->va_start) >> PAGE_SHIFT; + if (unlikely(!n_pages || n_pages > VMAP_BUMP_CHUNK_PAGES - idx)) + return NULL; memset(&chunk->page_va[idx], 0, n_pages * sizeof(va)); /* @@ -2725,8 +2754,12 @@ vmap_bump_unlink(unsigned long addr) * TLB entries until the next lazy-purge flush, so reusing them * before the flush is unsafe. Forward-only bump avoids that. */ + if (unlikely(chunk->owner_cpu < 0 || chunk->owner_cpu >= nr_cpu_ids)) + return va; + + owner_cur = READ_ONCE(per_cpu(vmap_bump_cur, chunk->owner_cpu)); if (atomic_sub_return(n_pages, &chunk->alloced) == 0 && - chunk != this_cpu_read(vmap_bump_cur)) { + chunk != owner_cur) { spin_lock(&vmap_bump_chunks_lock); list_del_rcu(&chunk->link); spin_unlock(&vmap_bump_chunks_lock); @@ -2781,11 +2814,14 @@ static struct vmap_area *alloc_vmap_area(unsigned long size, * find_unlink_vmap_area() consult vmap_chunk_lookup() before * falling back to busy.mt. */ - va = vmap_bump_alloc(size, align, vstart, vend, gfp_mask, node, - va_flags); - if (!va && vmap_bump_refill(gfp_mask) == 0) + va = NULL; + if (vmap_bump_eligible(size, align, vstart, vend)) { va = vmap_bump_alloc(size, align, vstart, vend, gfp_mask, node, va_flags); + if (!va && vmap_bump_refill(gfp_mask) == 0) + va = vmap_bump_alloc(size, align, vstart, vend, gfp_mask, + node, va_flags); + } if (va) { if (vm) { vm->addr = (void *)va->va_start; -- 2.34.1