From nobody Tue Apr 7 21:49:30 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 F40CC3E3D93 for ; Wed, 11 Mar 2026 17:26:03 +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=1773249964; cv=none; b=l8vIkxqrLoeaMACWbYCgmzSuSQFsAHh2CJX0YEnvIR6jgxg1o6quxrSJD0z/wBIOOhGYGXgvsiUpFa67ecYlgbv0OocGFJv0x/kMqFAmJtHGcZQXdAqm05HQG/IvXq0ZQVrlOQnWSHenZ/XY20cMcht1LWl3LWLH7DQWNRc6A7Y= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1773249964; c=relaxed/simple; bh=WuYiqZxOOUSTMj+uHG8hKRHfxCYiMKZgeLbdCYDg2b0=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=DnauUF1b1K4RmSX+qaG4wgPiaKEb1K2aGVQMcBbjU0MdN01XoXbRqhmq0Eqbe8y3cMlYNLVxYe4nh/NdpQQdzC4s1A++24qWDnVopn6E0VowJYx5I/TAUmT6BmUVhsIfsDjb2dbLPk2+9cW/ELIhjQKUVTOoYjmEgYubF419KVA= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=e/YBSoHr; 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="e/YBSoHr" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 770FEC19424; Wed, 11 Mar 2026 17:26:03 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1773249963; bh=WuYiqZxOOUSTMj+uHG8hKRHfxCYiMKZgeLbdCYDg2b0=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=e/YBSoHrZXvKVCaNm1I2qkdjbYiDv4/SkTfvR+P+e9LZNC4PqrJ6Xu7bpJYGnVgHR nUGgMnFVOBXtq6x6+J89y7wKr/xkxL4yK0Gl09zt9Zoak12vspkZab+XovBojPoWFu TCtlfuq02+d9qr/N4n22xYFCN4asrtUGDWMcTFtpZdl3jUMHV5Ksq280e83UussN6U seaex4c1N6UM2Ztsyn64QYmrfKccf3GcdVQ+U/4+6CApe1YjaMwpw2bEJstyn2+1ZU PsuE37hUMKVQch+kCocRB2YDNXuTdgV/VWz9P+tnO4LgtduJOxX6ZJx8XANv0VTpb4 V8A4+pjMgzobA== From: "Lorenzo Stoakes (Oracle)" To: Andrew Morton Cc: "Liam R . Howlett" , Vlastimil Babka , Mike Rapoport , Suren Baghdasaryan , Michal Hocko , Jann Horn , Pedro Falcato , linux-mm@kvack.org, linux-kernel@vger.kernel.org, Jianzhou Zhao , Oscar Salvador Subject: [PATCH 3/3] mm/mremap: check map count under mmap write lock and abstract Date: Wed, 11 Mar 2026 17:24:38 +0000 Message-ID: <18be0b48eaa8e8804eb745974ee729c3ade0c687.1773249037.git.ljs@kernel.org> X-Mailer: git-send-email 2.53.0 In-Reply-To: References: 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" We are checking the mmap count in check_mremap_params(), prior to obtaining an mmap write lock, which means that accesses to current->mm->map_count might race with this field being updated. Resolve this by only checking this field after the mmap write lock is held. Additionally, abstract this check into a helper function with extensive ASCII documentation of what's going on. Reported-by: Jianzhou Zhao Closes: https://lore.kernel.org/all/1a7d4c26.6b46.19cdbe7eaf0.Coremail.luck= d0g@163.com/ Signed-off-by: Lorenzo Stoakes (Oracle) Reviewed-by: Pedro Falcato --- mm/mremap.c | 88 +++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 75 insertions(+), 13 deletions(-) diff --git a/mm/mremap.c b/mm/mremap.c index ba6c690f6c1b..ee46bbb031e6 100644 --- a/mm/mremap.c +++ b/mm/mremap.c @@ -1028,6 +1028,75 @@ static void vrm_stat_account(struct vma_remap_struct= *vrm, mm->locked_vm +=3D pages; } =20 +static bool __check_map_count_against_split(struct mm_struct *mm, + bool before_unmaps) +{ + const int sys_map_count =3D get_sysctl_max_map_count(); + int map_count =3D mm->map_count; + + mmap_assert_write_locked(mm); + + /* + * At the point of shrinking the VMA, if new_len < old_len, we unmap + * thusly in the worst case: + * + * old_addr+old_len old_addr+old_len + * |---------------.----.---------| |---------------| |---------| + * | . . | -> | +1 | -1 | +1 | + * |---------------.----.---------| |---------------| |---------| + * old_addr+new_len old_addr+new_len + * + * At the point of removing the portion of an existing VMA to make space + * for the moved VMA if MREMAP_FIXED, we unmap thusly in the worst case: + * + * new_addr new_addr+new_len new_addr new_addr+new_len + * |----.---------------.---------| |----| |---------| + * | . . | -> | +1 | -1 | +1 | + * |----.---------------.---------| |----| |---------| + * + * Therefore, before we consider the move anything, we have to account + * for 2 additional VMAs possibly being created upon these unmappings. + */ + if (before_unmaps) + map_count +=3D 2; + + /* + * At the point of MOVING the VMA: + * + * We start by copying a VMA, which creates an additional VMA if no + * merge occurs, then if not MREMAP_DONTUNMAP, we unmap the source VMA. + * In the worst case we might then observe: + * + * new_addr new_addr+new_len new_addr new_addr+new_len + * |----| |---------| |----|---------------|---------| + * | | | | -> | | +1 | | + * |----| |---------| |----|---------------|---------| + * + * old_addr old_addr+old_len old_addr old_addr+old_len + * |----.---------------.---------| |----| |---------| + * | . . | -> | +1 | -1 | +1 | + * |----.---------------.---------| |----| |---------| + * + * Therefore we must check to ensure we have headroom of 2 additional + * VMAs. + */ + return map_count + 2 <=3D sys_map_count; +} + +/* Do we violate the map count limit if we split VMAs when moving the VMA?= */ +static bool check_map_count_against_split(void) +{ + return __check_map_count_against_split(current->mm, + /*before_unmaps=3D*/false); +} + +/* Do we violate the map count limit if we split VMAs prior to early unmap= s? */ +static bool check_map_count_against_split_early(void) +{ + return __check_map_count_against_split(current->mm, + /*before_unmaps=3D*/true); +} + /* * Perform checks before attempting to write a VMA prior to it being * moved. @@ -1045,7 +1114,7 @@ static unsigned long prep_move_vma(struct vma_remap_s= truct *vrm) * which may not merge, then (if MREMAP_DONTUNMAP is not set) unmap the * source, which may split, causing a net increase of 2 mappings. */ - if (current->mm->map_count + 2 > get_sysctl_max_map_count()) + if (!check_map_count_against_split()) return -ENOMEM; =20 if (vma->vm_ops && vma->vm_ops->may_split) { @@ -1804,18 +1873,6 @@ static unsigned long check_mremap_params(struct vma_= remap_struct *vrm) if (vrm_overlaps(vrm)) return -EINVAL; =20 - /* - * We may unmap twice before invoking move_vma(), that is if new_len < - * old_len (shrinking), and in the MREMAP_FIXED case, unmapping part of - * a VMA located at the destination. - * - * In the worst case, both unmappings will cause splits, resulting in a - * net increased map count of 2. In move_vma() we check for headroom of - * 2 additional mappings, so check early to avoid bailing out then. - */ - if (current->mm->map_count + 4 > get_sysctl_max_map_count()) - return -ENOMEM; - return 0; } =20 @@ -1925,6 +1982,11 @@ static unsigned long do_mremap(struct vma_remap_stru= ct *vrm) return -EINTR; vrm->mmap_locked =3D true; =20 + if (!check_map_count_against_split_early()) { + mmap_write_unlock(mm); + return -ENOMEM; + } + if (vrm_move_only(vrm)) { res =3D remap_move(vrm); } else { --=20 2.53.0