From patchwork Mon May 15 19:32:32 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Lorenzo Stoakes X-Patchwork-Id: 13242134 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from kanga.kvack.org (kanga.kvack.org [205.233.56.17]) by smtp.lore.kernel.org (Postfix) with ESMTP id 89B60C77B7D for ; Mon, 15 May 2023 19:32:40 +0000 (UTC) Received: by kanga.kvack.org (Postfix) id 09829900003; Mon, 15 May 2023 15:32:40 -0400 (EDT) Received: by kanga.kvack.org (Postfix, from userid 40) id 020FC900002; Mon, 15 May 2023 15:32:39 -0400 (EDT) X-Delivered-To: int-list-linux-mm@kvack.org Received: by kanga.kvack.org (Postfix, from userid 63042) id DB64A900003; Mon, 15 May 2023 15:32:39 -0400 (EDT) X-Delivered-To: linux-mm@kvack.org Received: from relay.hostedemail.com (smtprelay0014.hostedemail.com [216.40.44.14]) by kanga.kvack.org (Postfix) with ESMTP id C5C0F900002 for ; Mon, 15 May 2023 15:32:39 -0400 (EDT) Received: from smtpin30.hostedemail.com (a10.router.float.18 [10.200.18.1]) by unirelay09.hostedemail.com (Postfix) with ESMTP id 8411380CFA for ; Mon, 15 May 2023 19:32:39 +0000 (UTC) X-FDA: 80793486438.30.FE2C129 Received: from mail-wm1-f41.google.com (mail-wm1-f41.google.com [209.85.128.41]) by imf28.hostedemail.com (Postfix) with ESMTP id B7033C0006 for ; Mon, 15 May 2023 19:32:37 +0000 (UTC) Authentication-Results: imf28.hostedemail.com; dkim=pass header.d=gmail.com header.s=20221208 header.b=P9bxXw3d; spf=pass (imf28.hostedemail.com: domain of lstoakes@gmail.com designates 209.85.128.41 as permitted sender) smtp.mailfrom=lstoakes@gmail.com; dmarc=pass (policy=none) header.from=gmail.com ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=hostedemail.com; s=arc-20220608; t=1684179157; h=from:from:sender:reply-to:subject:subject:date:date: message-id:message-id:to:to:cc:cc:mime-version:mime-version: content-type:content-transfer-encoding:content-transfer-encoding: in-reply-to:references:dkim-signature; bh=iMVbs9NK56mDlrRWLRnqErXBKtSiemN+W66NFlThQDE=; b=4cSiuwNOA6tFR4D/qSjVrZctJUqGJNYapCmTVaQCPthTY5gXp/3FWnwt1lQV92CPqG/7Te knXb4wumINSD9/RLUNo/Y21I9Ug3Oc0LPG+8hA4TQFuLz4nc9s6g1NVyIMZbO2sK8WlyZA +6oL1hZDNjZTaGRXD7iHL7HTqEjisJI= ARC-Seal: i=1; s=arc-20220608; d=hostedemail.com; t=1684179157; a=rsa-sha256; cv=none; b=19hxDpvzyULotkHltVxDleYZw1qx6Xt3D1xtypS2ZkIDCBCByjEyjr2A5HGpMgTERa2WWu NYeYUXhRrxkRgOJv3pcxgM0ApbkNo9E/0Jy1jhYvIPb6azbrVy2Paa8LZ2zxuNYDqlX0jb fCWA0r9uU0UAvYGXOmjxXnxJHiXg63A= ARC-Authentication-Results: i=1; imf28.hostedemail.com; dkim=pass header.d=gmail.com header.s=20221208 header.b=P9bxXw3d; spf=pass (imf28.hostedemail.com: domain of lstoakes@gmail.com designates 209.85.128.41 as permitted sender) smtp.mailfrom=lstoakes@gmail.com; dmarc=pass (policy=none) header.from=gmail.com Received: by mail-wm1-f41.google.com with SMTP id 5b1f17b1804b1-3f4271185daso88623455e9.2 for ; Mon, 15 May 2023 12:32:37 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20221208; t=1684179156; x=1686771156; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:from:to:cc:subject:date:message-id:reply-to; bh=iMVbs9NK56mDlrRWLRnqErXBKtSiemN+W66NFlThQDE=; b=P9bxXw3djQUnMK5xW1pLdlUA8Ij+PtYZWG4sHvQI2BNygUMvJN2plCHkznD2xjDJOB mKGeQ92r6fCtQFfyMGsXxHZEpOw7B9m8Iht6Ery6cxDC+TtZ8fnurONh3BED6/KqXbK2 GfTf2WbiuRPfQVUBZK+e1znftLIpqqanF3pH8OsNscOxA1IccX9DUQ5MWelKVSuPM43c TeFyJaXrc9k5Gq43+HHkE42UAtAhbx2rs7m/W0lNNz3PyQfs6qF5CtEHIK5VemCeWlsp yQs0DwO12DcMnzMBGIQ9D4GyBIOUTKluP16E6IrrN8F09HJk4VMEgh82/LxbymDPd79q dKfA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20221208; t=1684179156; x=1686771156; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:x-gm-message-state:from:to:cc:subject:date:message-id :reply-to; bh=iMVbs9NK56mDlrRWLRnqErXBKtSiemN+W66NFlThQDE=; b=dNUro+KlikCqmQRSCL+Z+vgZZffVB40EwmiMhGUMEHTOL3gCgQj06pzB/T4vF3ys4S JcNrO+NcuyYDULHcql2v2Su7J6R+pjCEntmdQvsyx5P2SIXqNzNZVyHdzuWGV0L1+SaC 4kiDo9E3vvOvZInk6gj6aRQ0vuOegXqHB9YFHcuA3z3m/vaCyO6nybBnArBo7fUUCIpq ziA1UaEZ5dty2SvlguueUr9+icw3KzECd17e9gJWVB1OhEJ4JEmaFDW6VcqXjxhXiXN7 EgggvJfh5qz0KDodxgzYhO2NH5JGf/PEVxPDh7rvcA9EMOYu+SiFOjcSlrGZquVsrkgO I8Nw== X-Gm-Message-State: AC+VfDxMDYtG5bJvCWHkLBQ3zlZu6TGRAeb+1sOvqTeX3KNO67VHNg9A HY1kXhaqEGiF0I7oV3NwsR3/uLixeSqvPw== X-Google-Smtp-Source: ACHHUZ4UsCdj61BRCDmXcIh843nDA3Pl62oXnQgyZVpTOsOw5ivHuPdJEc6oYGemw3Iz8KxWxMge7g== X-Received: by 2002:a7b:c357:0:b0:3ee:19b4:a2e6 with SMTP id l23-20020a7bc357000000b003ee19b4a2e6mr21990651wmj.19.1684179155596; Mon, 15 May 2023 12:32:35 -0700 (PDT) Received: from lucifer.home ([2a00:23c5:dc8c:8701:1663:9a35:5a7b:1d76]) by smtp.googlemail.com with ESMTPSA id e12-20020adffd0c000000b003062ad45243sm124315wrr.14.2023.05.15.12.32.34 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 15 May 2023 12:32:34 -0700 (PDT) From: Lorenzo Stoakes To: linux-mm@kvack.org, linux-kernel@vger.kernel.org, Andrew Morton Cc: Mike Rapoport , Peter Xu , Mark Rutland , "Liam R . Howlett" , Alexander Viro , Christian Brauner , Lorenzo Stoakes Subject: [PATCH v2] mm: userfaultfd: avoid passing an invalid range to vma_merge() Date: Mon, 15 May 2023 20:32:32 +0100 Message-Id: <20230515193232.67552-1-lstoakes@gmail.com> X-Mailer: git-send-email 2.40.1 MIME-Version: 1.0 X-Stat-Signature: q8bh3zscaz9nrumt3uo7boj34wn7oczq X-Rspamd-Server: rspam03 X-Rspam-User: X-Rspamd-Queue-Id: B7033C0006 X-HE-Tag: 1684179157-29953 X-HE-Meta: U2FsdGVkX18NFQU0CA9CX5oaosbt9WtQyZcHSD067+8ArFvInIXItJs2Cvq9wZesNY1+1ymEHZocyqmTBKMe5R4NUKG8ZM372aUC9jCrPdZikvt7Qf7uxOrflyOmVC4h2j64S8yl9Y7P/DrhtUSdt3NNxztr+t3CKgDJH8C94QfV6jhhdKW4F9ibRGU7+0HBwnu5tyZw7yR176qr0fOvTCduXEmqFV/V6AXZkTK3nyGBxEjYYV7BoV9whuXgt/9jtq/mw2xFJ42tKJM9gkM9DnirAvhRpFGvtKdmjxwXIdpoWRQPGl/5NIo+Td+/YJXC9pF2dsJ5cGmZO9kJ8ud3jTfuYrB724iVYsAkmWCAvt7A8Wp3xbO7AfHnBZkkWHWSp8USCQoCumh8YaE0tATWyxz4CvyUlSZnQBfnnMk7ahD0F2QCTkq9VGTavbqgh39YRxLLaYhCiJGBdEXC3HLTCOoKQSBtO0nO9GVUPB8zxHl1Fr8O1Qq5jL1sDAotYd4Lt16ve2lfP8ZnXRzGQSInSqb6tsdDuB2V8Mevg5d7YJmoQzzPxzTImeHRbtmE02OpQTxOO/HBheXoS3inOW3nccBz8fHqXSUdonF0DHTAmQy6K+FqEc9VFfUcMls71WTWbwuoLg3MGE5Veb8z3rRHZKg0zezds5XairJf9AwkGWwavXHF5aQZcG614bZ9FZmUwmmnN/zqfGRoXTXRi9CaWxN0mc18ldmXDEhwUV9TEWq5QVi28AboBoaiaacr5JblQapVn/+S5QmGHsOdyGNyeUGD9kUoI8XfAf1yVgiKaCtqSJrhTpQhGthruwHjKTt1i1XH5Khflv/8v931mZk8b2d3g0osqNpLSQzrCu0Go7/HB90uhbkVgcSAcKFy4PRMwnHseXHGbu4eOMgZBQDyYNbP+OtHW1suTJDhfzrZ593l3VRfJ/Fg+6wOYl99eL0xHYax6XbEPoUrQTmgNIn 2FhZongc itHAWukxh88Heq3jBGfAwJrr0VnGYAAFFgbhPiT5lKW/OPuJAz8H/WElGnJoIm8bAMKDaxQuBh4BsuCwxrHuigcHmRCDD+6WFIB3YNfa2aFseoCg6PRvJmli19jxoe6SffcbCVlIDXSL4mIo5tfxcILo2yyKq+IGxnXQXzrs374X4NSaUWAnSbHOLNE5++4P2+dN1vbziFG2yRsMhDaytuay5ZjMhz8AFFSaRYUDbJhmxYbvpgKDl/Ly5zmR8Eg+b9MIo8xcnDefQKv2jwNrSZnWXk6/+vbr33jepYKzh/D1cQ08s2BfYsf9Uljf5WKTroZwcT4ZU6bmP5Xmf1IMe6l7zrYuFyJUym87IcQmMoBXSBOp9TiarqU6MUrkZCeTuUpAmj9nX+acfDALhZfvovTzeqbv8R5tluJcX/wSAyX0LrqjrfFW5kHmeEVr3WMzigPP6k2qk0f1wRruFPRPiaNjS/Duva1DSTN6zZS2RX+1HUj40s3vUz/lTMvlKp+gWT7rDDaLlV0TRJUYshm1ICPWvde8bdW9hA+o+f1ST+JxJVmZkFVYpSpFelQ== X-Bogosity: Ham, tests=bogofilter, spamicity=0.000000, version=1.2.4 Sender: owner-linux-mm@kvack.org Precedence: bulk X-Loop: owner-majordomo@kvack.org List-ID: The userfaultfd_[un]register() functions will knowingly pass an invalid address range to vma_merge(), then rely on it failing to merge to indicate that the VMA should be split into a valid one. This is not something that should be relied upon, as vma_merge() implicitly assumes in cases 5-8 that curr->vm_start == addr. This is now enforced since commit b0729ae0ae67 ("mm/mmap/vma_merge: explicitly assign res, vma, extend invariants") with an explicit VM_WARN_ON() check. Since commit 29417d292bd0 ("mm/mmap/vma_merge: always check invariants") this check is performed unconditionally, which caused this assert to arise in tests performed by Mark [1]. This patch fixes the issue by performing the split operations before attempting to merge VMAs in both instances. The problematic operation is splitting the start of the VMA since we were clamping to the end of the VMA in any case, however it is useful to group both of the split operations together to avoid egregious goto's and to abstract the code between the functions. As well as fixing the repro described in [1] this also continues to pass uffd unit tests. [1]:https://lore.kernel.org/all/ZFunF7DmMdK05MoF@FVFF77S0Q05N.cambridge.arm.com Reported-by: Mark Rutland Closes: https://lore.kernel.org/all/ZFunF7DmMdK05MoF@FVFF77S0Q05N.cambridge.arm.com/ Signed-off-by: Lorenzo Stoakes Reviewed-by: Liam R. Howlett Signed-off-by: Peter Xu --- fs/userfaultfd.c | 108 ++++++++++++++++++++++++++--------------------- 1 file changed, 60 insertions(+), 48 deletions(-) diff --git a/fs/userfaultfd.c b/fs/userfaultfd.c index 0fd96d6e39ce..ef5d667ea804 100644 --- a/fs/userfaultfd.c +++ b/fs/userfaultfd.c @@ -1319,6 +1319,32 @@ static __always_inline int validate_range(struct mm_struct *mm, return 0; } +static int clamp_range(struct vma_iterator *vmi, struct vm_area_struct *vma, + unsigned long start, unsigned long end, bool *can_merge) +{ + int ret; + bool merge = true; + + /* The range must always be clamped to the start of a VMA. */ + if (vma->vm_start < start) { + ret = split_vma(vmi, vma, start, 1); + if (ret) + return ret; + + merge = false; + } + + /* It must also be clamped to the end of a VMA. */ + if (vma->vm_end > end) { + ret = split_vma(vmi, vma, end, 0); + if (ret) + return ret; + } + + *can_merge = merge; + return 0; +} + static int userfaultfd_register(struct userfaultfd_ctx *ctx, unsigned long arg) { @@ -1330,7 +1356,7 @@ static int userfaultfd_register(struct userfaultfd_ctx *ctx, unsigned long vm_flags, new_flags; bool found; bool basic_ioctls; - unsigned long start, end, vma_end; + unsigned long start, end; struct vma_iterator vmi; user_uffdio_register = (struct uffdio_register __user *) arg; @@ -1462,6 +1488,8 @@ static int userfaultfd_register(struct userfaultfd_ctx *ctx, ret = 0; for_each_vma_range(vmi, vma, end) { + bool can_merge; + cond_resched(); BUG_ON(!vma_can_userfault(vma, vm_flags)); @@ -1477,32 +1505,22 @@ static int userfaultfd_register(struct userfaultfd_ctx *ctx, (vma->vm_flags & vm_flags) == vm_flags) goto skip; - if (vma->vm_start > start) - start = vma->vm_start; - vma_end = min(end, vma->vm_end); + ret = clamp_range(&vmi, vma, start, end, &can_merge); + if (ret) + break; new_flags = (vma->vm_flags & ~__VM_UFFD_FLAGS) | vm_flags; - prev = vma_merge(&vmi, mm, prev, start, vma_end, new_flags, - vma->anon_vma, vma->vm_file, vma->vm_pgoff, - vma_policy(vma), - ((struct vm_userfaultfd_ctx){ ctx }), - anon_vma_name(vma)); - if (prev) { + if (can_merge) { + prev = vma_merge(&vmi, mm, prev, vma->vm_start, vma->vm_end, new_flags, + vma->anon_vma, vma->vm_file, vma->vm_pgoff, + vma_policy(vma), + ((struct vm_userfaultfd_ctx){ ctx }), + anon_vma_name(vma)); + /* vma_merge() invalidated the mas */ - vma = prev; - goto next; - } - if (vma->vm_start < start) { - ret = split_vma(&vmi, vma, start, 1); - if (ret) - break; - } - if (vma->vm_end > end) { - ret = split_vma(&vmi, vma, end, 0); - if (ret) - break; + if (prev) + vma = prev; } - next: /* * In the vma_merge() successful mprotect-like case 8: * the next vma was merged into the current one and @@ -1560,7 +1578,7 @@ static int userfaultfd_unregister(struct userfaultfd_ctx *ctx, struct uffdio_range uffdio_unregister; unsigned long new_flags; bool found; - unsigned long start, end, vma_end; + unsigned long start, end; const void __user *buf = (void __user *)arg; struct vma_iterator vmi; @@ -1627,6 +1645,8 @@ static int userfaultfd_unregister(struct userfaultfd_ctx *ctx, prev = vma_prev(&vmi); ret = 0; for_each_vma_range(vmi, vma, end) { + bool can_merge; + cond_resched(); BUG_ON(!vma_can_userfault(vma, vma->vm_flags)); @@ -1640,9 +1660,9 @@ static int userfaultfd_unregister(struct userfaultfd_ctx *ctx, WARN_ON(!(vma->vm_flags & VM_MAYWRITE)); - if (vma->vm_start > start) - start = vma->vm_start; - vma_end = min(end, vma->vm_end); + ret = clamp_range(&vmi, vma, start, end, &can_merge); + if (ret) + break; if (userfaultfd_missing(vma)) { /* @@ -1652,35 +1672,27 @@ static int userfaultfd_unregister(struct userfaultfd_ctx *ctx, * UFFDIO_WAKE explicitly. */ struct userfaultfd_wake_range range; - range.start = start; - range.len = vma_end - start; + range.start = vma->vm_start; + range.len = vma->vm_end - vma->vm_start; wake_userfault(vma->vm_userfaultfd_ctx.ctx, &range); } /* Reset ptes for the whole vma range if wr-protected */ if (userfaultfd_wp(vma)) - uffd_wp_range(vma, start, vma_end - start, false); + uffd_wp_range(vma, vma->vm_start, + vma->vm_end - vma->vm_start, false); new_flags = vma->vm_flags & ~__VM_UFFD_FLAGS; - prev = vma_merge(&vmi, mm, prev, start, vma_end, new_flags, - vma->anon_vma, vma->vm_file, vma->vm_pgoff, - vma_policy(vma), - NULL_VM_UFFD_CTX, anon_vma_name(vma)); - if (prev) { - vma = prev; - goto next; - } - if (vma->vm_start < start) { - ret = split_vma(&vmi, vma, start, 1); - if (ret) - break; - } - if (vma->vm_end > end) { - ret = split_vma(&vmi, vma, end, 0); - if (ret) - break; + if (can_merge) { + prev = vma_merge(&vmi, mm, prev, vma->vm_start, + vma->vm_end, new_flags, vma->anon_vma, + vma->vm_file, vma->vm_pgoff, + vma_policy(vma), + NULL_VM_UFFD_CTX, anon_vma_name(vma)); + /* vma_merge() invalidated the mas */ + if (prev) + vma = prev; } - next: /* * In the vma_merge() successful mprotect-like case 8: * the next vma was merged into the current one and