From patchwork Thu Nov 10 20:17:30 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Yuxuan Luo X-Patchwork-Id: 1702297 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@legolas.ozlabs.org Authentication-Results: legolas.ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=lists.ubuntu.com (client-ip=91.189.94.19; helo=huckleberry.canonical.com; envelope-from=kernel-team-bounces@lists.ubuntu.com; receiver=) Authentication-Results: legolas.ozlabs.org; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=canonical.com header.i=@canonical.com header.a=rsa-sha256 header.s=20210705 header.b=eP7sdbK1; dkim-atps=neutral Received: from huckleberry.canonical.com (huckleberry.canonical.com [91.189.94.19]) (using TLSv1.2 with cipher ECDHE-ECDSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by legolas.ozlabs.org (Postfix) with ESMTPS id 4N7Y6t5gVVz23mc for ; Fri, 11 Nov 2022 07:17:57 +1100 (AEDT) Received: from localhost ([127.0.0.1] helo=huckleberry.canonical.com) by huckleberry.canonical.com with esmtp (Exim 4.86_2) (envelope-from ) id 1otDzg-0004pY-P1; Thu, 10 Nov 2022 20:17:48 +0000 Received: from smtp-relay-internal-1.internal ([10.131.114.114] helo=smtp-relay-internal-1.canonical.com) by huckleberry.canonical.com with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.86_2) (envelope-from ) id 1otDze-0004pD-8E for kernel-team@lists.ubuntu.com; Thu, 10 Nov 2022 20:17:46 +0000 Received: from mail-qk1-f200.google.com (mail-qk1-f200.google.com [209.85.222.200]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by smtp-relay-internal-1.canonical.com (Postfix) with ESMTPS id 5684F4129B for ; Thu, 10 Nov 2022 20:17:45 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=canonical.com; s=20210705; t=1668111465; bh=xPznVIK8tvqAZ9W/l44/sp/xytVh+Jl5CEMslAJ4VCM=; h=From:To:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=eP7sdbK1YlUrY8oHQCkRwHji4ykwnecR71254O+939ct9/VT6S1HkwWcMJ1rllDJO x6DKB4JexPyPP+ZaAQTAtJsaFInQagFYbKt7SriVcO2TcoH9+9Gkeixi9DUSAQC9L8 A5tD8j2MlGXj6ARDYH+JnYEoBBTFYUewyy6GxHW7S4Ixt2oDWGp7JNLymgM7YY9QeW H+S1DBt1i/c+KaPbB4nB3MUTgZ/XooEIpaFUXCGN4/FxBLooR/5HCuMZW4HawXg4CV D4b24B8rbzxcAohOmefa2rVRqXZ8IDc8N66i2c5bmfJ1BpIUKxVCTWOTGaCBU7EL+S 721fjqtIVzNgA== Received: by mail-qk1-f200.google.com with SMTP id bp10-20020a05620a458a00b006fa29f253dcso3012236qkb.11 for ; Thu, 10 Nov 2022 12:17:45 -0800 (PST) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=xPznVIK8tvqAZ9W/l44/sp/xytVh+Jl5CEMslAJ4VCM=; b=7OPP226c34vNJtGyaIpBawm3KSX2b17KbwpkDAkQk3Up6hvQLx/a2bei3suvJ0Rq85 PH3i2O5vvqO1Akxd9lJwiZVubEimwiBmeT6hVDlwlrVT7YmAY7Z1rFGSCcQWSn7gvqRX BSdOnQQysu/IsEdUB3fo5wkaBZz7aquFOFdK8gB7G0p68tSncdG1EfekjY6JN8P9fMRP WwjcUM+zFRziM7ffrjckqffJLX6UdqeClDOkSMjUxcyE6f0bG5JG6btcwMDY7e7hxmSO 0ZL+t+CQrI3mzxrpXUIOh7ziNa72391lplPQGLFmvfmVMVqgzrIDLv4oK4ElFeesOGfj m00w== X-Gm-Message-State: ACrzQf34mOfzxA8bqa4bbXao3+6wK+HJDhaENccpue3Tj9+dATuidmJ8 mJBsjuBVZoBvNCn+rzrmL9vQ+w+oXbnRDTAk8I+i9Qd5k7HNEXBSs7U8JhcuIkPLNryOP5lzo2u zjJ4kD+gHePb7Ne8o7P0xtKvjyjcTj66RK8FA9usO4Q== X-Received: by 2002:a05:622a:11c2:b0:3a5:7f7a:9db1 with SMTP id n2-20020a05622a11c200b003a57f7a9db1mr21075837qtk.687.1668111463943; Thu, 10 Nov 2022 12:17:43 -0800 (PST) X-Google-Smtp-Source: AMsMyM4aOYFegWSQ/1kv8qVPve5NTD3+czseEcIybqYWKEqxXKIAeGV3VRheemiwnlyKQiePOVeD6Q== X-Received: by 2002:a05:622a:11c2:b0:3a5:7f7a:9db1 with SMTP id n2-20020a05622a11c200b003a57f7a9db1mr21075825qtk.687.1668111463531; Thu, 10 Nov 2022 12:17:43 -0800 (PST) Received: from cache-ubuntu.hsd1.nj.comcast.net ([2601:86:200:98b0:e7d0:be88:9c08:616a]) by smtp.gmail.com with ESMTPSA id b3-20020ac85403000000b00342f8d4d0basm125923qtq.43.2022.11.10.12.17.42 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 10 Nov 2022 12:17:42 -0800 (PST) From: Yuxuan Luo To: kernel-team@lists.ubuntu.com Subject: [SRU][Jammy][PATCH] mm/rmap: Fix anon_vma->degree ambiguity leading to double-reuse Date: Thu, 10 Nov 2022 15:17:30 -0500 Message-Id: <20221110201742.27485-1-yuxuan.luo@canonical.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: References: MIME-Version: 1.0 X-BeenThere: kernel-team@lists.ubuntu.com X-Mailman-Version: 2.1.20 Precedence: list List-Id: Kernel team discussions List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: kernel-team-bounces@lists.ubuntu.com Sender: "kernel-team" From: Jann Horn anon_vma->degree tracks the combined number of child anon_vmas and VMAs that use the anon_vma as their ->anon_vma. anon_vma_clone() then assumes that for any anon_vma attached to src->anon_vma_chain other than src->anon_vma, it is impossible for it to be a leaf node of the VMA tree, meaning that for such VMAs ->degree is elevated by 1 because of a child anon_vma, meaning that if ->degree equals 1 there are no VMAs that use the anon_vma as their ->anon_vma. This assumption is wrong because the ->degree optimization leads to leaf nodes being abandoned on anon_vma_clone() - an existing anon_vma is reused and no new parent-child relationship is created. So it is possible to reuse an anon_vma for one VMA while it is still tied to another VMA. This is an issue because is_mergeable_anon_vma() and its callers assume that if two VMAs have the same ->anon_vma, the list of anon_vmas attached to the VMAs is guaranteed to be the same. When this assumption is violated, vma_merge() can merge pages into a VMA that is not attached to the corresponding anon_vma, leading to dangling page->mapping pointers that will be dereferenced during rmap walks. Fix it by separately tracking the number of child anon_vmas and the number of VMAs using the anon_vma as their ->anon_vma. Fixes: 7a3ef208e662 ("mm: prevent endless growth of anon_vma hierarchy") Cc: stable@kernel.org Acked-by: Michal Hocko Acked-by: Vlastimil Babka Signed-off-by: Jann Horn Signed-off-by: Linus Torvalds (cherry picked from commit 2555283eb40df89945557273121e9393ef9b542b) CVE-2022-42703 Signed-off-by: Yuxuan Luo Acked-by: Thadeu Lima de Souza Cascardo --- include/linux/rmap.h | 7 +++++-- mm/rmap.c | 29 ++++++++++++++++------------- 2 files changed, 21 insertions(+), 15 deletions(-) diff --git a/include/linux/rmap.h b/include/linux/rmap.h index c976cc6de257..c29d9c13378b 100644 --- a/include/linux/rmap.h +++ b/include/linux/rmap.h @@ -39,12 +39,15 @@ struct anon_vma { atomic_t refcount; /* - * Count of child anon_vmas and VMAs which points to this anon_vma. + * Count of child anon_vmas. Equals to the count of all anon_vmas that + * have ->parent pointing to this one, including itself. * * This counter is used for making decision about reusing anon_vma * instead of forking new one. See comments in function anon_vma_clone. */ - unsigned degree; + unsigned long num_children; + /* Count of VMAs whose ->anon_vma pointer points to this object. */ + unsigned long num_active_vmas; struct anon_vma *parent; /* Parent of this anon_vma */ diff --git a/mm/rmap.c b/mm/rmap.c index 3e340ee380cb..330b361a460e 100644 --- a/mm/rmap.c +++ b/mm/rmap.c @@ -90,7 +90,8 @@ static inline struct anon_vma *anon_vma_alloc(void) anon_vma = kmem_cache_alloc(anon_vma_cachep, GFP_KERNEL); if (anon_vma) { atomic_set(&anon_vma->refcount, 1); - anon_vma->degree = 1; /* Reference for first vma */ + anon_vma->num_children = 0; + anon_vma->num_active_vmas = 0; anon_vma->parent = anon_vma; /* * Initialise the anon_vma root to point to itself. If called @@ -198,6 +199,7 @@ int __anon_vma_prepare(struct vm_area_struct *vma) anon_vma = anon_vma_alloc(); if (unlikely(!anon_vma)) goto out_enomem_free_avc; + anon_vma->num_children++; /* self-parent link for new root */ allocated = anon_vma; } @@ -207,8 +209,7 @@ int __anon_vma_prepare(struct vm_area_struct *vma) if (likely(!vma->anon_vma)) { vma->anon_vma = anon_vma; anon_vma_chain_link(vma, avc, anon_vma); - /* vma reference or self-parent link for new root */ - anon_vma->degree++; + anon_vma->num_active_vmas++; allocated = NULL; avc = NULL; } @@ -293,19 +294,19 @@ int anon_vma_clone(struct vm_area_struct *dst, struct vm_area_struct *src) anon_vma_chain_link(dst, avc, anon_vma); /* - * Reuse existing anon_vma if its degree lower than two, - * that means it has no vma and only one anon_vma child. + * Reuse existing anon_vma if it has no vma and only one + * anon_vma child. * - * Do not chose parent anon_vma, otherwise first child - * will always reuse it. Root anon_vma is never reused: + * Root anon_vma is never reused: * it has self-parent reference and at least one child. */ if (!dst->anon_vma && src->anon_vma && - anon_vma != src->anon_vma && anon_vma->degree < 2) + anon_vma->num_children < 2 && + anon_vma->num_active_vmas == 0) dst->anon_vma = anon_vma; } if (dst->anon_vma) - dst->anon_vma->degree++; + dst->anon_vma->num_active_vmas++; unlock_anon_vma_root(root); return 0; @@ -355,6 +356,7 @@ int anon_vma_fork(struct vm_area_struct *vma, struct vm_area_struct *pvma) anon_vma = anon_vma_alloc(); if (!anon_vma) goto out_error; + anon_vma->num_active_vmas++; avc = anon_vma_chain_alloc(GFP_KERNEL); if (!avc) goto out_error_free_anon_vma; @@ -375,7 +377,7 @@ int anon_vma_fork(struct vm_area_struct *vma, struct vm_area_struct *pvma) vma->anon_vma = anon_vma; anon_vma_lock_write(anon_vma); anon_vma_chain_link(vma, avc, anon_vma); - anon_vma->parent->degree++; + anon_vma->parent->num_children++; anon_vma_unlock_write(anon_vma); return 0; @@ -407,7 +409,7 @@ void unlink_anon_vmas(struct vm_area_struct *vma) * to free them outside the lock. */ if (RB_EMPTY_ROOT(&anon_vma->rb_root.rb_root)) { - anon_vma->parent->degree--; + anon_vma->parent->num_children--; continue; } @@ -415,7 +417,7 @@ void unlink_anon_vmas(struct vm_area_struct *vma) anon_vma_chain_free(avc); } if (vma->anon_vma) { - vma->anon_vma->degree--; + vma->anon_vma->num_active_vmas--; /* * vma would still be needed after unlink, and anon_vma will be prepared @@ -433,7 +435,8 @@ void unlink_anon_vmas(struct vm_area_struct *vma) list_for_each_entry_safe(avc, next, &vma->anon_vma_chain, same_vma) { struct anon_vma *anon_vma = avc->anon_vma; - VM_WARN_ON(anon_vma->degree); + VM_WARN_ON(anon_vma->num_children); + VM_WARN_ON(anon_vma->num_active_vmas); put_anon_vma(anon_vma); list_del(&avc->same_vma);