From: Jeff Xu jeffxu@google.com
By default, memfd_create() creates a non-sealable MFD, unless the MFD_ALLOW_SEALING flag is set.
When the MFD_NOEXEC_SEAL flag is initially introduced, the MFD created with that flag is sealable, even though MFD_ALLOW_SEALING is not set. This patch changes MFD_NOEXEC_SEAL to be non-sealable by default, unless MFD_ALLOW_SEALING is explicitly set.
This is a non-backward compatible change. However, as MFD_NOEXEC_SEAL is new, we expect not many applications will rely on the nature of MFD_NOEXEC_SEAL being sealable. In most cases, the application already sets MFD_ALLOW_SEALING if they need a sealable MFD.
Additionally, this enhances the useability of pid namespace sysctl vm.memfd_noexec. When vm.memfd_noexec equals 1 or 2, the kernel will add MFD_NOEXEC_SEAL if mfd_create does not specify MFD_EXEC or MFD_NOEXEC_SEAL, and the addition of MFD_NOEXEC_SEAL enables the MFD to be sealable. This means, any application that does not desire this behavior will be unable to utilize vm.memfd_noexec = 1 or 2 to migrate/enforce non-executable MFD. This adjustment ensures that applications can anticipate that the sealable characteristic will remain unmodified by vm.memfd_noexec.
This patch was initially developed by Barnabás Pőcze, and Barnabás used Debian Code Search and GitHub to try to find potential breakages and could only find a single one. Dbus-broker's memfd_create() wrapper is aware of this implicit `MFD_ALLOW_SEALING` behavior, and tries to work around it [1]. This workaround will break. Luckily, this only affects the test suite, it does not affect the normal operations of dbus-broker. There is a PR with a fix[2]. In addition, David Rheinsberg also raised similar fix in [3]
[1]: https://github.com/bus1/dbus-broker/blob/9eb0b7e5826fc76cad7b025bc46f267d4a8... [2]: https://github.com/bus1/dbus-broker/pull/366 [3]: https://lore.kernel.org/lkml/20230714114753.170814-1-david@readahead.eu/
Cc: stable@vger.kernel.org Fixes: 105ff5339f498a ("mm/memfd: add MFD_NOEXEC_SEAL and MFD_EXEC") Signed-off-by: Barnabás Pőcze pobrn@protonmail.com Signed-off-by: Jeff Xu jeffxu@google.com Reviewed-by: David Rheinsberg david@readahead.eu --- mm/memfd.c | 9 ++++---- tools/testing/selftests/memfd/memfd_test.c | 26 +++++++++++++++++++++- 2 files changed, 29 insertions(+), 6 deletions(-)
diff --git a/mm/memfd.c b/mm/memfd.c index 7d8d3ab3fa37..8b7f6afee21d 100644 --- a/mm/memfd.c +++ b/mm/memfd.c @@ -356,12 +356,11 @@ SYSCALL_DEFINE2(memfd_create,
inode->i_mode &= ~0111; file_seals = memfd_file_seals_ptr(file); - if (file_seals) { - *file_seals &= ~F_SEAL_SEAL; + if (file_seals) *file_seals |= F_SEAL_EXEC; - } - } else if (flags & MFD_ALLOW_SEALING) { - /* MFD_EXEC and MFD_ALLOW_SEALING are set */ + } + + if (flags & MFD_ALLOW_SEALING) { file_seals = memfd_file_seals_ptr(file); if (file_seals) *file_seals &= ~F_SEAL_SEAL; diff --git a/tools/testing/selftests/memfd/memfd_test.c b/tools/testing/selftests/memfd/memfd_test.c index 95af2d78fd31..8579a93d006b 100644 --- a/tools/testing/selftests/memfd/memfd_test.c +++ b/tools/testing/selftests/memfd/memfd_test.c @@ -1151,7 +1151,7 @@ static void test_noexec_seal(void) mfd_def_size, MFD_CLOEXEC | MFD_NOEXEC_SEAL); mfd_assert_mode(fd, 0666); - mfd_assert_has_seals(fd, F_SEAL_EXEC); + mfd_assert_has_seals(fd, F_SEAL_SEAL | F_SEAL_EXEC); mfd_fail_chmod(fd, 0777); close(fd); } @@ -1169,6 +1169,14 @@ static void test_sysctl_sysctl0(void) mfd_assert_has_seals(fd, 0); mfd_assert_chmod(fd, 0644); close(fd); + + fd = mfd_assert_new("kern_memfd_sysctl_0_dfl", + mfd_def_size, + MFD_CLOEXEC); + mfd_assert_mode(fd, 0777); + mfd_assert_has_seals(fd, F_SEAL_SEAL); + mfd_assert_chmod(fd, 0644); + close(fd); }
static void test_sysctl_set_sysctl0(void) @@ -1206,6 +1214,14 @@ static void test_sysctl_sysctl1(void) mfd_assert_has_seals(fd, F_SEAL_EXEC); mfd_fail_chmod(fd, 0777); close(fd); + + fd = mfd_assert_new("kern_memfd_sysctl_1_noexec_nosealable", + mfd_def_size, + MFD_CLOEXEC | MFD_NOEXEC_SEAL); + mfd_assert_mode(fd, 0666); + mfd_assert_has_seals(fd, F_SEAL_EXEC | F_SEAL_SEAL); + mfd_fail_chmod(fd, 0777); + close(fd); }
static void test_sysctl_set_sysctl1(void) @@ -1238,6 +1254,14 @@ static void test_sysctl_sysctl2(void) mfd_assert_has_seals(fd, F_SEAL_EXEC); mfd_fail_chmod(fd, 0777); close(fd); + + fd = mfd_assert_new("kern_memfd_sysctl_2_noexec_notsealable", + mfd_def_size, + MFD_CLOEXEC | MFD_NOEXEC_SEAL); + mfd_assert_mode(fd, 0666); + mfd_assert_has_seals(fd, F_SEAL_EXEC | F_SEAL_SEAL); + mfd_fail_chmod(fd, 0777); + close(fd); }
static void test_sysctl_set_sysctl2(void)