From mboxrd@z Thu Jan 1 00:00:00 1970 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 1BFE8C36002 for ; Mon, 7 Apr 2025 02:55:02 +0000 (UTC) Received: by kanga.kvack.org (Postfix) id B180D280001; Sun, 6 Apr 2025 22:55:00 -0400 (EDT) Received: by kanga.kvack.org (Postfix, from userid 40) id AC60A6B0007; Sun, 6 Apr 2025 22:55:00 -0400 (EDT) X-Delivered-To: int-list-linux-mm@kvack.org Received: by kanga.kvack.org (Postfix, from userid 63042) id 93FFA280001; Sun, 6 Apr 2025 22:55:00 -0400 (EDT) X-Delivered-To: linux-mm@kvack.org Received: from relay.hostedemail.com (smtprelay0017.hostedemail.com [216.40.44.17]) by kanga.kvack.org (Postfix) with ESMTP id 713C36B0005 for ; Sun, 6 Apr 2025 22:55:00 -0400 (EDT) Received: from smtpin28.hostedemail.com (a10.router.float.18 [10.200.18.1]) by unirelay03.hostedemail.com (Postfix) with ESMTP id 4D38EAD0ED for ; Mon, 7 Apr 2025 02:55:01 +0000 (UTC) X-FDA: 83305730802.28.697A2E3 Received: from mail-ed1-f43.google.com (mail-ed1-f43.google.com [209.85.208.43]) by imf16.hostedemail.com (Postfix) with ESMTP id 2A870180008 for ; Mon, 7 Apr 2025 02:54:58 +0000 (UTC) Authentication-Results: imf16.hostedemail.com; dkim=pass header.d=gmail.com header.s=20230601 header.b=CMy9sm4S; dmarc=pass (policy=none) header.from=gmail.com; spf=pass (imf16.hostedemail.com: domain of richard.weiyang@gmail.com designates 209.85.208.43 as permitted sender) smtp.mailfrom=richard.weiyang@gmail.com ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=hostedemail.com; s=arc-20220608; t=1743994499; h=from:from:sender:reply-to:reply-to:subject:subject:date:date: message-id:message-id:to:to:cc:cc:mime-version:mime-version: content-type:content-type:content-transfer-encoding: in-reply-to:in-reply-to:references:references:dkim-signature; bh=Bl6aLTFU+MzMGcQGZ+J3WrHMYXUZpVFn3O0zDOI2smo=; b=eiKXYu38BJrv5TFr2qV+KtGPfL7RGCr8Y7QfYgsbNEyCEr91MCS4SoqcBfu4OEJ25SgRwp z8BXriVwFPazylxCWWtAHL5TqYiGk1CUQafnAVfs9q2Bqzx/6XQ/fO2csAzK0s+XwceLNr nfZxn5CtmU7LQ3hqPTKOQTtBeTEn+UU= ARC-Seal: i=1; s=arc-20220608; d=hostedemail.com; t=1743994499; a=rsa-sha256; cv=none; b=Ncr1BFvk9FGHRAMdL4J/AFczYVtZ2mkUVPpBiiXd+bMAF2qxkduNQTPeN3CSZz+ML4YPbq dbaLQsEbZADKBGEDtStm1ahrE+KFFKTIDHlVZGxArGeM1/bef2nF1N/tk22mJ52tkjBhyq 2u9Yz+9coxKtw5WxzMkH+2iffGknHiM= ARC-Authentication-Results: i=1; imf16.hostedemail.com; dkim=pass header.d=gmail.com header.s=20230601 header.b=CMy9sm4S; dmarc=pass (policy=none) header.from=gmail.com; spf=pass (imf16.hostedemail.com: domain of richard.weiyang@gmail.com designates 209.85.208.43 as permitted sender) smtp.mailfrom=richard.weiyang@gmail.com Received: by mail-ed1-f43.google.com with SMTP id 4fb4d7f45d1cf-5e6194e9d2cso7370909a12.2 for ; Sun, 06 Apr 2025 19:54:58 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1743994497; x=1744599297; darn=kvack.org; h=user-agent:in-reply-to:content-disposition:mime-version:references :reply-to:message-id:subject:cc:to:from:date:from:to:cc:subject:date :message-id:reply-to; bh=Bl6aLTFU+MzMGcQGZ+J3WrHMYXUZpVFn3O0zDOI2smo=; b=CMy9sm4S2Qjk1Mb5IKeVC5EzKiXy9X50uBsc7yGS7n4v6iSImCroTrnISMsTXVHOWL zkAT86RtTY+eAqXS746m57klPwrJsDuALppVj0NXKhftEqBguuCc6wxIeYOuNvKLcjab CT/Ymex4tdSsPDrA6OPDJOQNEOqbnPNcxyaYXM6ua2dBkdH2lk9hDxjraQ3nANSn9qwk Wsbum5J3i2ZGQB7bWRQKW6wqyEhtcOCZ/B5cbkH+UG2cfD2YVwmZEwgzpyei/FvsWRqL SRdIv303D1wknJmNaWgtZkFnCTpDEiH75gAtnbQ0XwalL3EUmrGWnK4ub8wClRq0W17w 9D0w== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1743994497; x=1744599297; h=user-agent:in-reply-to:content-disposition:mime-version:references :reply-to:message-id:subject:cc:to:from:date:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=Bl6aLTFU+MzMGcQGZ+J3WrHMYXUZpVFn3O0zDOI2smo=; b=M0I1ZvfwSPuRyHDyoAbk/AR+a/7scA+yxZob0oZwpnGun5/l5r8cr0nusJ4vBENlPY QkeEDsJNs0/TdutBeAJ7jedqWKSuDuTwu9EgBWTbIpA4n7eOuaM0KgOQdfVpC+KfG0GD OjKutF5vdEIqbfIQlkZdM9wi3BQRMtnbXDi/s/Rj1RcdbMDKYnldIELRSJyJheO9oOq6 dX/eijg+8htv4/qtAQ28PVZrFTuszbJGC7u9CS81i7jpQpey4+7y6+BSyRMoF27fY1yb +5pDrl56K3W7tK/HN6Ij6HguZqScRvoZDIybwZrHN/K4ZrQb84YtIZffugLq2mjT9jDi yydw== X-Forwarded-Encrypted: i=1; AJvYcCWp3710YuT0b29vqQd1qUdeEQrtBYJqDrOVfSUraPlnZrnJAbUqi6TWnOEcEY97NaiwFHaqJhxXqw==@kvack.org X-Gm-Message-State: AOJu0Yxxm7tDHRDhm3qHOAKejcGbPL6BIsXDSi8pMgT/vVEvot9EXDSK E6RLf6K3IK4ydrYbfeUWJK3XVX7SZqax8+1xPXZArraVub7xQbp+ X-Gm-Gg: ASbGncuX5WIXa4TTc3wYcD7Lph+efapJZWIIbAUwNyNMyH+pwSklqpRsWV5B4tgJzce SxD3AzcqwYxCYe7LClIcBUJHzQxmZi2Ems5ae1Ch3rO8iM4Faw/Dmisxm4BIHtU9xdFI+bwz1ID FJzU4yTkZOv+CuPX+BDCYvja5kKZYm8p3cpoaVpGRwsTyMNkigxjVRprQ3u2VBn9GWEoUzet6ZE DyCdb6AUablOIMQjZSU7dnL2+gkZpl/+Sb1mFH4Z4rcesd0P4j/RJ/OxGo1wCNDgDgK1naDriOr Od1novQy2NKhB7JB7j/F00gbXo0bF0JGOA3BOsc2GSYL X-Google-Smtp-Source: AGHT+IG6VJPOQELgVQs5gKuf++fq8aYY/Q8dCrFAAzQ/YMedhN7VVD7vW16xy6SI86TkSh8Jg46EjQ== X-Received: by 2002:a05:6402:280b:b0:5e6:44d9:57f7 with SMTP id 4fb4d7f45d1cf-5f0b3e4732amr8812253a12.26.1743994497443; Sun, 06 Apr 2025 19:54:57 -0700 (PDT) Received: from localhost ([185.92.221.13]) by smtp.gmail.com with ESMTPSA id 4fb4d7f45d1cf-5f088084e49sm5881725a12.50.2025.04.06.19.54.56 (version=TLS1_2 cipher=ECDHE-ECDSA-CHACHA20-POLY1305 bits=256/256); Sun, 06 Apr 2025 19:54:56 -0700 (PDT) Date: Mon, 7 Apr 2025 02:54:56 +0000 From: Wei Yang To: Lorenzo Stoakes Cc: Andrew Morton , Vlastimil Babka , Jann Horn , "Liam R . Howlett" , Suren Baghdasaryan , David Hildenbrand , Matthew Wilcox , Rik van Riel , linux-mm@kvack.org, linux-kernel@vger.kernel.org Subject: Re: [PATCH 3/3] tools/testing/selftests: assert that anon merge cases behave as expected Message-ID: <20250407025455.67nhchndedotn57g@master> Reply-To: Wei Yang References: MIME-Version: 1.0 Content-Type: text/plain; charset=us-ascii Content-Disposition: inline In-Reply-To: User-Agent: NeoMutt/20170113 (1.7.2) X-Rspamd-Server: rspam07 X-Rspamd-Queue-Id: 2A870180008 X-Stat-Signature: 9dcke33rep795soajkeiyn17iaani84n X-Rspam-User: X-HE-Tag: 1743994498-313808 X-HE-Meta: U2FsdGVkX1+7fYea4nVsQyjw0L12K/7BrZIcjyTj10h0BfYjRP+FjOzvl5WJu+HQM0ltPWgJ3+1L7KbzXj6XIX3kHq4NDEhUjCu2vsn5tquw8l19+9h/tD13AkLCsB8oj1oFPjnO3f+55xK14Rwg3U0863cw7HknRJsVtAemJKdWqoEMVcO09i7gupDYQr6vXu9WyicdXqqsf0Wd3oxTHue9ZzjO9AaGrJOj5VQ/jXNLIi+st5mWGoeQtmz8h3UgAhOi7CrSttyQFeygza4sw4iBw3QhkV7YYNA2WY7L49vc7qK8OUDv3lgALWsPHu7yMBNwaWfEVQ0GdW0+do0SyQHu2oV4O43dNbyig1rlPagZIH6hZ6SowrySCWloGuxqWTFY2Z9Y3d8sY8yfNeerKz0SVql9R3W7WBTJlLCWuRBaPnjhIFD/I2Bo7VnUxlbVkBcOWecNrVjYT965WVWq3R/QAEyL3auCh9wmepVyljAOv/pqBal0id2qdncsuMdYPyA2z+K1KNSTY7W4l4o1peT5YJXhDpHFEevZdk8y/r16gqJIafSMmT4VpPFdF5o98/+VVsWhYj4jDfawarOvimUBfzTu/3d+RQxOMuII4ClgG5HpIURf+Z6UhibJ98+kahvVQl896HMYOpAi7DIxxUkkCxhzI6Cp/edoymuLJQcRsQe3m342AKUcEj1StXH6hyOJZgKoKeF5iqecKpfdWY8E27KYNmztQaXAOSGGv7L2p5VLcOsmQ3ZUwhDYo9N4BUz9m3k7zqEyxlX5KON+iWhYfcxEQDxC0bgEUaNG9w1BGRe3i6FcMnI9+v9bKH3nDa8I1xYxp/wFts1UvEewFfJSmuymEk8IVOXI/8Sr2/4ZYTEFfnRga051L2bj9txp87dT9OsMUytQOOOETolnAZhdbrQdnkf0a03L75lF0vuwXITH9B1SnpHPEbvmeoAk67tutm6tsqBtLkHMkBY fVbOwbYr YHCi/MTobNJSHkqe5cXG6oCCsYW/pYlpAiXFwzWcF2iRAyFXZG1Q9NnMB4Lb1Iv9R1xHcC1xScNvtpAHHCVtGHeqa1eyHLVijUaF6lkvVxnIr+cnW56DZNOPlNPcyEMOzck+rRAEGcR3ksiduH7lY0ZohR/78tmZYn1dA8Nsfx8rZ4rkRsE8T9Myh5DbUimSSIKn0JP9XDzkdj8iN7N2NuPrj9pFTDTsheq+74ZM6bX4LiwV69sH7Bq0vQuLlfDWcWL3qtaXr/BSE4BVKXdOF2APTHyqiZH4T+64cXsBp8QSbnZJuQ5EJFMKHPFugnote3uVJa65KMoUkKcL6B8T3rIHRyTXabH3k6sCBFA0vNp50Y/vdnp0YuTBuLiZLDjxiygF7hjSaW5yvFiRLFKW8B+wMW1Vb9pNQ6IcCykMGstPp4C9pxGe7RU2hJQkM63D2Lzf7i0hHXsawl+OcxPp6rtAy45jGtEPgDc8lBmVPnracBvdOvGD5YWJGT6Y3SJkb9qM3SYacfgkeb/1MoWeQTRGAFChMUOjHTQQbV+43KIUo6YSsGmLCS188NtNanJbYQM03LsX4Y68tWLWfd1zE5daKqb1QkC8l/F84dph1AqCGZEZRGsU7b+fW/Q== 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: List-Subscribe: List-Unsubscribe: On Mon, Mar 17, 2025 at 09:15:05PM +0000, Lorenzo Stoakes wrote: >Prior to the recently applied commit that permits this merge, >mprotect()'ing a faulted VMA, adjacent to an unfaulted VMA, such that the >two share characteristics would fail to merge due to what appear to be >unintended consequences of commit 965f55dea0e3 ("mmap: avoid merging cloned >VMAs"). > >Now we have fixed this bug, assert that we can indeed merge anonymous VMAs >this way. > >Also assert that forked source/target VMAs are equally >rejected. Previously, all empty target anon merges with one VMA faulted and >the other unfaulted would be rejected incorrectly, now we ensure that >unforked merge, but forked do not. > >Signed-off-by: Lorenzo Stoakes >--- > tools/testing/selftests/mm/.gitignore | 1 + > tools/testing/selftests/mm/Makefile | 1 + > tools/testing/selftests/mm/merge.c | 454 ++++++++++++++++++++++ > tools/testing/selftests/mm/run_vmtests.sh | 2 + > 4 files changed, 458 insertions(+) > create mode 100644 tools/testing/selftests/mm/merge.c > >diff --git a/tools/testing/selftests/mm/.gitignore b/tools/testing/selftests/mm/.gitignore >index c5241b193db8..91db34941a14 100644 >--- a/tools/testing/selftests/mm/.gitignore >+++ b/tools/testing/selftests/mm/.gitignore >@@ -58,3 +58,4 @@ hugetlb_dio > pkey_sighandler_tests_32 > pkey_sighandler_tests_64 > guard-regions >+merge >diff --git a/tools/testing/selftests/mm/Makefile b/tools/testing/selftests/mm/Makefile >index 8270895039d1..ad4d6043a60f 100644 >--- a/tools/testing/selftests/mm/Makefile >+++ b/tools/testing/selftests/mm/Makefile >@@ -98,6 +98,7 @@ TEST_GEN_FILES += hugetlb_madv_vs_map > TEST_GEN_FILES += hugetlb_dio > TEST_GEN_FILES += droppable > TEST_GEN_FILES += guard-regions >+TEST_GEN_FILES += merge > > ifneq ($(ARCH),arm64) > TEST_GEN_FILES += soft-dirty >diff --git a/tools/testing/selftests/mm/merge.c b/tools/testing/selftests/mm/merge.c >new file mode 100644 >index 000000000000..9cc61bdbfba8 >--- /dev/null >+++ b/tools/testing/selftests/mm/merge.c >@@ -0,0 +1,454 @@ >+// SPDX-License-Identifier: GPL-2.0-or-later >+ >+#define _GNU_SOURCE >+#include "../kselftest_harness.h" >+#include >+#include >+#include >+#include >+#include >+#include "vm_util.h" >+ >+FIXTURE(merge) >+{ >+ unsigned int page_size; >+ char *carveout; >+ struct procmap_fd procmap; >+}; >+ >+FIXTURE_SETUP(merge) >+{ >+ self->page_size = psize(); >+ /* Carve out PROT_NONE region to map over. */ >+ self->carveout = mmap(NULL, 12 * self->page_size, PROT_NONE, >+ MAP_ANON | MAP_PRIVATE, -1, 0); >+ ASSERT_NE(self->carveout, MAP_FAILED); >+ /* Setup PROCMAP_QUERY interface. */ >+ ASSERT_EQ(open_self_procmap(&self->procmap), 0); >+} >+ >+FIXTURE_TEARDOWN(merge) >+{ >+ ASSERT_EQ(munmap(self->carveout, 12 * self->page_size), 0); >+ ASSERT_EQ(close_procmap(&self->procmap), 0); >+} >+ >+TEST_F(merge, mprotect_unfaulted_left) >+{ >+ unsigned int page_size = self->page_size; >+ char *carveout = self->carveout; >+ struct procmap_fd *procmap = &self->procmap; >+ char *ptr; >+ >+ /* >+ * Map 10 pages of R/W memory within. MAP_NORESERVE so we don't hit >+ * merge failure due to lack of VM_ACCOUNT flag by mistake. >+ * >+ * |-----------------------| >+ * | unfaulted | >+ * |-----------------------| >+ */ >+ ptr = mmap(&carveout[page_size], 10 * page_size, PROT_READ | PROT_WRITE, >+ MAP_ANON | MAP_PRIVATE | MAP_FIXED | MAP_NORESERVE, -1, 0); >+ ASSERT_NE(ptr, MAP_FAILED); >+ /* >+ * Now make the first 5 pages read-only, splitting the VMA: >+ * >+ * RO RW >+ * |-----------|-----------| >+ * | unfaulted | unfaulted | >+ * |-----------|-----------| >+ */ >+ ASSERT_EQ(mprotect(ptr, 5 * page_size, PROT_READ), 0); >+ /* >+ * Fault in the first of the last 5 pages so it gets an anon_vma and >+ * thus the whole VMA becomes 'faulted': >+ * >+ * RO RW >+ * |-----------|-----------| >+ * | unfaulted | faulted | >+ * |-----------|-----------| >+ */ >+ ptr[5 * page_size] = 'x'; >+ /* >+ * Now mprotect() the RW region read-only, we should merge (though for >+ * ~15 years we did not! :): >+ * >+ * RO >+ * |-----------------------| >+ * | faulted | >+ * |-----------------------| >+ */ >+ ASSERT_EQ(mprotect(&ptr[5 * page_size], 5 * page_size, PROT_READ), 0); >+ >+ /* Assert that the merge succeeded using PROCMAP_QUERY. */ >+ ASSERT_TRUE(find_vma_procmap(procmap, ptr)); >+ ASSERT_EQ(procmap->query.vma_start, (unsigned long)ptr); >+ ASSERT_EQ(procmap->query.vma_end, (unsigned long)ptr + 10 * page_size); >+} >+ >+TEST_F(merge, mprotect_unfaulted_right) >+{ >+ unsigned int page_size = self->page_size; >+ char *carveout = self->carveout; >+ struct procmap_fd *procmap = &self->procmap; >+ char *ptr; >+ >+ /* >+ * |-----------------------| >+ * | unfaulted | >+ * |-----------------------| >+ */ >+ ptr = mmap(&carveout[page_size], 10 * page_size, PROT_READ | PROT_WRITE, >+ MAP_ANON | MAP_PRIVATE | MAP_FIXED | MAP_NORESERVE, -1, 0); >+ ASSERT_NE(ptr, MAP_FAILED); >+ /* >+ * Now make the last 5 pages read-only, splitting the VMA: >+ * >+ * RW RO >+ * |-----------|-----------| >+ * | unfaulted | unfaulted | >+ * |-----------|-----------| >+ */ >+ ASSERT_EQ(mprotect(&ptr[5 * page_size], 5 * page_size, PROT_READ), 0); >+ /* >+ * Fault in the first of the first 5 pages so it gets an anon_vma and >+ * thus the whole VMA becomes 'faulted': >+ * >+ * RW RO >+ * |-----------|-----------| >+ * | faulted | unfaulted | >+ * |-----------|-----------| >+ */ >+ ptr[0] = 'x'; >+ /* >+ * Now mprotect() the RW region read-only, we should merge: >+ * >+ * RO >+ * |-----------------------| >+ * | faulted | >+ * |-----------------------| >+ */ >+ ASSERT_EQ(mprotect(ptr, 5 * page_size, PROT_READ), 0); >+ >+ /* Assert that the merge succeeded using PROCMAP_QUERY. */ >+ ASSERT_TRUE(find_vma_procmap(procmap, ptr)); >+ ASSERT_EQ(procmap->query.vma_start, (unsigned long)ptr); >+ ASSERT_EQ(procmap->query.vma_end, (unsigned long)ptr + 10 * page_size); >+} >+ >+TEST_F(merge, mprotect_unfaulted_both) >+{ >+ unsigned int page_size = self->page_size; >+ char *carveout = self->carveout; >+ struct procmap_fd *procmap = &self->procmap; >+ char *ptr; >+ >+ /* >+ * |-----------------------| >+ * | unfaulted | >+ * |-----------------------| >+ */ >+ ptr = mmap(&carveout[2 * page_size], 9 * page_size, PROT_READ | PROT_WRITE, >+ MAP_ANON | MAP_PRIVATE | MAP_FIXED | MAP_NORESERVE, -1, 0); >+ ASSERT_NE(ptr, MAP_FAILED); >+ /* >+ * Now make the first and last 3 pages read-only, splitting the VMA: >+ * >+ * RO RW RO >+ * |-----------|-----------|-----------| >+ * | unfaulted | unfaulted | unfaulted | >+ * |-----------|-----------|-----------| >+ */ >+ ASSERT_EQ(mprotect(ptr, 3 * page_size, PROT_READ), 0); >+ ASSERT_EQ(mprotect(&ptr[6 * page_size], 3 * page_size, PROT_READ), 0); >+ /* >+ * Fault in the first of the middle 3 pages so it gets an anon_vma and >+ * thus the whole VMA becomes 'faulted': >+ * >+ * RO RW RO >+ * |-----------|-----------|-----------| >+ * | unfaulted | faulted | unfaulted | >+ * |-----------|-----------|-----------| >+ */ >+ ptr[3 * page_size] = 'x'; >+ /* >+ * Now mprotect() the RW region read-only, we should merge: >+ * >+ * RO >+ * |-----------------------| >+ * | faulted | >+ * |-----------------------| >+ */ >+ ASSERT_EQ(mprotect(&ptr[3 * page_size], 3 * page_size, PROT_READ), 0); >+ >+ /* Assert that the merge succeeded using PROCMAP_QUERY. */ >+ ASSERT_TRUE(find_vma_procmap(procmap, ptr)); >+ ASSERT_EQ(procmap->query.vma_start, (unsigned long)ptr); >+ ASSERT_EQ(procmap->query.vma_end, (unsigned long)ptr + 9 * page_size); >+} >+ >+TEST_F(merge, mprotect_faulted_left_unfaulted_right) >+{ >+ unsigned int page_size = self->page_size; >+ char *carveout = self->carveout; >+ struct procmap_fd *procmap = &self->procmap; >+ char *ptr; >+ >+ /* >+ * |-----------------------| >+ * | unfaulted | >+ * |-----------------------| >+ */ >+ ptr = mmap(&carveout[2 * page_size], 9 * page_size, PROT_READ | PROT_WRITE, >+ MAP_ANON | MAP_PRIVATE | MAP_FIXED | MAP_NORESERVE, -1, 0); >+ ASSERT_NE(ptr, MAP_FAILED); >+ /* >+ * Now make the last 3 pages read-only, splitting the VMA: >+ * >+ * RW RO >+ * |-----------------------|-----------| >+ * | unfaulted | unfaulted | >+ * |-----------------------|-----------| >+ */ >+ ASSERT_EQ(mprotect(&ptr[6 * page_size], 3 * page_size, PROT_READ), 0); >+ /* >+ * Fault in the first of the first 6 pages so it gets an anon_vma and >+ * thus the whole VMA becomes 'faulted': >+ * >+ * RW RO >+ * |-----------------------|-----------| >+ * | unfaulted | unfaulted | ^^^ According to your previous comment convention, the comment here describe the result after ptr[0] = 'x'. faulted is the correct description here? >+ * |-----------------------|-----------| >+ */ >+ ptr[0] = 'x'; >+ /* >+ * Now make the first 3 pages read-only, splitting the VMA: >+ * >+ * RO RW RO >+ * |-----------|-----------|-----------| >+ * | faulted | faulted | unfaulted | >+ * |-----------|-----------|-----------| >+ */ >+ ASSERT_EQ(mprotect(ptr, 3 * page_size, PROT_READ), 0); >+ /* >+ * Now mprotect() the RW region read-only, we should merge: >+ * >+ * RO >+ * |-----------------------| >+ * | faulted | >+ * |-----------------------| >+ */ >+ ASSERT_EQ(mprotect(&ptr[3 * page_size], 3 * page_size, PROT_READ), 0); >+ >+ /* Assert that the merge succeeded using PROCMAP_QUERY. */ >+ ASSERT_TRUE(find_vma_procmap(procmap, ptr)); >+ ASSERT_EQ(procmap->query.vma_start, (unsigned long)ptr); >+ ASSERT_EQ(procmap->query.vma_end, (unsigned long)ptr + 9 * page_size); >+} >+ >+TEST_F(merge, mprotect_unfaulted_left_faulted_right) >+{ >+ unsigned int page_size = self->page_size; >+ char *carveout = self->carveout; >+ struct procmap_fd *procmap = &self->procmap; >+ char *ptr; >+ >+ /* >+ * |-----------------------| >+ * | unfaulted | >+ * |-----------------------| >+ */ >+ ptr = mmap(&carveout[2 * page_size], 9 * page_size, PROT_READ | PROT_WRITE, >+ MAP_ANON | MAP_PRIVATE | MAP_FIXED | MAP_NORESERVE, -1, 0); >+ ASSERT_NE(ptr, MAP_FAILED); >+ /* >+ * Now make the first 3 pages read-only, splitting the VMA: >+ * >+ * RO RW >+ * |-----------|-----------------------| >+ * | unfaulted | unfaulted | >+ * |-----------|-----------------------| >+ */ >+ ASSERT_EQ(mprotect(ptr, 3 * page_size, PROT_READ), 0); >+ /* >+ * FAult in the first of the last 6 pages so it gets an anon_vma and ^ s/A/a/ >+ * thus the whole VMA becomes 'faulted': >+ * >+ * RO RW >+ * |-----------|-----------------------| >+ * | unfaulted | faulted | >+ * |-----------|-----------------------| >+ */ >+ ptr[3 * page_size] = 'x'; >+ /* >+ * Now make the last 3 pages read-only, splitting the VMA: >+ * >+ * RO RW RO >+ * |-----------|-----------|-----------| >+ * | unfaulted | faulted | faulted | >+ * |-----------|-----------|-----------| >+ */ >+ ASSERT_EQ(mprotect(&ptr[6 * page_size], 3 * page_size, PROT_READ), 0); >+ /* >+ * Now mprotect() the RW region read-only, we should merge: >+ * >+ * RO >+ * |-----------------------| >+ * | faulted | >+ * |-----------------------| >+ */ >+ ASSERT_EQ(mprotect(&ptr[3 * page_size], 3 * page_size, PROT_READ), 0); >+ >+ /* Assert that the merge succeeded using PROCMAP_QUERY. */ >+ ASSERT_TRUE(find_vma_procmap(procmap, ptr)); >+ ASSERT_EQ(procmap->query.vma_start, (unsigned long)ptr); >+ ASSERT_EQ(procmap->query.vma_end, (unsigned long)ptr + 9 * page_size); >+} >+ >+TEST_F(merge, forked_target_vma) >+{ >+ unsigned int page_size = self->page_size; >+ char *carveout = self->carveout; >+ struct procmap_fd *procmap = &self->procmap; >+ pid_t pid; >+ char *ptr, *ptr2; >+ int i; >+ >+ /* >+ * |-----------| >+ * | unfaulted | >+ * |-----------| >+ */ >+ ptr = mmap(&carveout[page_size], 5 * page_size, PROT_READ | PROT_WRITE, >+ MAP_ANON | MAP_PRIVATE | MAP_FIXED, -1, 0); >+ ASSERT_NE(ptr, MAP_FAILED); >+ >+ /* >+ * Fault in process. >+ * >+ * |-----------| >+ * | faulted | >+ * |-----------| >+ */ >+ ptr[0] = 'x'; >+ >+ pid = fork(); >+ ASSERT_NE(pid, -1); >+ >+ if (pid != 0) { >+ wait(NULL); >+ return; >+ } >+ >+ /* Child process below: */ >+ >+ /* Reopen for child. */ >+ ASSERT_EQ(close_procmap(&self->procmap), 0); >+ ASSERT_EQ(open_self_procmap(&self->procmap), 0); >+ >+ /* unCOWing everything does not cause the AVC to go away. */ ^^^ Before ptr[i] = 'x', we have unCOWed pages in vma. What we are doing here is COWing, right? >+ for (i = 0; i < 5 * page_size; i += page_size) >+ ptr[i] = 'x'; >+ >+ /* >+ * Map in adjacent VMA in child. >+ * >+ * forked >+ * |-----------|-----------| >+ * | faulted | unfaulted | >+ * |-----------|-----------| >+ * ptr ptr2 >+ */ >+ ptr2 = mmap(&ptr[5 * page_size], 5 * page_size, PROT_READ | PROT_WRITE, >+ MAP_ANON | MAP_PRIVATE | MAP_FIXED, -1, 0); >+ ASSERT_NE(ptr2, MAP_FAILED); >+ >+ /* Make sure not merged. */ >+ ASSERT_TRUE(find_vma_procmap(procmap, ptr)); >+ ASSERT_EQ(procmap->query.vma_start, (unsigned long)ptr); >+ ASSERT_EQ(procmap->query.vma_end, (unsigned long)ptr + 5 * page_size); >+} >+ >+TEST_F(merge, forked_source_vma) >+{ >+ unsigned int page_size = self->page_size; >+ char *carveout = self->carveout; >+ struct procmap_fd *procmap = &self->procmap; >+ pid_t pid; >+ char *ptr, *ptr2; >+ int i; >+ >+ /* >+ * |............|-----------| >+ * | | unfaulted | >+ * |............|-----------| I am not sure "unmapped" is correct here. The range has already been mapped by FIXTURE_SETUP(merge). >+ */ >+ ptr = mmap(&carveout[page_size], 5 * page_size, PROT_READ | PROT_WRITE, >+ MAP_ANON | MAP_PRIVATE | MAP_FIXED | MAP_NORESERVE, -1, 0); >+ ASSERT_NE(ptr, MAP_FAILED); >+ >+ /* >+ * Fault in process. >+ * >+ * |............||-----------| >+ * | || faulted | >+ * |............||-----------| ^ Extra line here? >+ */ >+ ptr[0] = 'x'; >+ >+ pid = fork(); >+ ASSERT_NE(pid, -1); >+ >+ if (pid != 0) { >+ wait(NULL); >+ return; >+ } >+ >+ /* Child process below: */ >+ >+ /* Reopen for child. */ >+ ASSERT_EQ(close_procmap(&self->procmap), 0); >+ ASSERT_EQ(open_self_procmap(&self->procmap), 0); >+ >+ /* unCOWing everything does not cause the AVC to go away. */ Same as above. >+ for (i = 0; i < 5 * page_size; i += page_size) >+ ptr[i] = 'x'; >+ >+ /* >+ * Map in adjacent VMA in child, ptr2 before ptr, but incompatible. >+ * >+ * RWX forked RW >+ * |-----------|-----------| >+ * | unfaulted | faulted | >+ * |-----------|-----------| >+ * ptr2 ptr >+ */ >+ ptr2 = mmap(&carveout[6 * page_size], 5 * page_size, PROT_READ | PROT_WRITE | PROT_EXEC, >+ MAP_ANON | MAP_PRIVATE | MAP_FIXED | MAP_NORESERVE, -1, 0); >+ ASSERT_NE(ptr2, MAP_FAILED); >+ I think pt2 is after ptr. Do I miss something? >+ >+ /* Make sure not merged. */ >+ ASSERT_TRUE(find_vma_procmap(procmap, ptr2)); >+ ASSERT_EQ(procmap->query.vma_start, (unsigned long)ptr2); >+ ASSERT_EQ(procmap->query.vma_end, (unsigned long)ptr2 + 5 * page_size); >+ >+ /* >+ * Now mprotect forked region to RWX so it becomes the source for the >+ * merge to unfaulted region: >+ * >+ * RWX forked RWX >+ * |-----------|-----------| >+ * | unfaulted | faulted | >+ * |-----------|-----------| >+ * ptr2 ptr >+ */ >+ ASSERT_EQ(mprotect(ptr, 5 * page_size, PROT_READ | PROT_WRITE | PROT_EXEC), 0); >+ /* Again, make sure not merged. */ >+ ASSERT_TRUE(find_vma_procmap(procmap, ptr2)); >+ ASSERT_EQ(procmap->query.vma_start, (unsigned long)ptr2); >+ ASSERT_EQ(procmap->query.vma_end, (unsigned long)ptr2 + 5 * page_size); >+} >+ >+TEST_HARNESS_MAIN >diff --git a/tools/testing/selftests/mm/run_vmtests.sh b/tools/testing/selftests/mm/run_vmtests.sh >index 9aff33b10999..0b2f8bb91433 100755 >--- a/tools/testing/selftests/mm/run_vmtests.sh >+++ b/tools/testing/selftests/mm/run_vmtests.sh >@@ -421,6 +421,8 @@ CATEGORY="madv_guard" run_test ./guard-regions > # MADV_POPULATE_READ and MADV_POPULATE_WRITE tests > CATEGORY="madv_populate" run_test ./madv_populate > >+CATEGORY="vma_merge" run_test ./merge >+ ./run_vmtests.sh -h would show a list of categories. How about add the new category "vma_merge" into the list. > if [ -x ./memfd_secret ] > then > (echo 0 > /proc/sys/kernel/yama/ptrace_scope 2>&1) | tap_prefix >-- >2.48.1 > -- Wei Yang Help you, Help me