diff --git a/pkg/sentry/syscalls/linux/sys_mempolicy.go b/pkg/sentry/syscalls/linux/sys_mempolicy.go index 6e7bcb868..841383c47 100644 --- a/pkg/sentry/syscalls/linux/sys_mempolicy.go +++ b/pkg/sentry/syscalls/linux/sys_mempolicy.go @@ -294,16 +294,20 @@ func copyInMempolicyNodemask(t *kernel.Task, modeWithFlags linux.NumaPolicy, nod } case linux.MPOL_PREFERRED: // This permits an empty nodemask, as long as no flags are set. - if nodemaskVal == 0 && flags != 0 { - return 0, 0, linuxerr.EINVAL + if nodemaskVal == 0 { + if flags != 0 { + return 0, 0, linuxerr.EINVAL + } + // On newer Linux versions, MPOL_PREFERRED is implemented as MPOL_LOCAL + // when node set is empty. See 7858d7bca7fb ("mm/mempolicy: don't handle + // MPOL_LOCAL like a fake MPOL_PREFERRED policy"). + mode = linux.MPOL_LOCAL } case linux.MPOL_LOCAL: - // This requires an empty nodemask and no flags set ... + // This requires an empty nodemask and no flags set. if nodemaskVal != 0 || flags != 0 { return 0, 0, linuxerr.EINVAL } - // ... and is implemented as MPOL_PREFERRED. - mode = linux.MPOL_PREFERRED default: // Unknown mode, which we should have rejected above. panic(fmt.Sprintf("unknown mode: %v", mode)) diff --git a/test/syscalls/linux/mempolicy.cc b/test/syscalls/linux/mempolicy.cc index 059fad598..fa8e5fcda 100644 --- a/test/syscalls/linux/mempolicy.cc +++ b/test/syscalls/linux/mempolicy.cc @@ -260,9 +260,11 @@ TEST(MempolicyTest, GetMempolicyNextInterleaveNode) { } TEST(MempolicyTest, Mbind) { + uint64_t nodemask = 0x1; // Temporarily set the thread policy to MPOL_PREFERRED. const auto cleanup_thread_policy = - ASSERT_NO_ERRNO_AND_VALUE(ScopedSetMempolicy(MPOL_PREFERRED, nullptr, 0)); + ASSERT_NO_ERRNO_AND_VALUE(ScopedSetMempolicy( + MPOL_PREFERRED, &nodemask, sizeof(nodemask) * BITS_PER_BYTE)); const auto mapping = ASSERT_NO_ERRNO_AND_VALUE( MmapAnon(kPageSize, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS)); @@ -274,10 +276,12 @@ TEST(MempolicyTest, Mbind) { SyscallSucceeds()); EXPECT_EQ(mode, MPOL_DEFAULT); - // Set MPOL_PREFERRED for the vma and read it back. - ASSERT_THAT( - mbind(mapping.ptr(), mapping.len(), MPOL_PREFERRED, nullptr, 0, 0), - SyscallSucceeds()); + // Set MPOL_PREFERRED for the vma and read it back. Note that setting + // MPOL_PREFERRED with an empty node set will set mode to MPOL_LOCAL on newer + // Linux releases. + ASSERT_THAT(mbind(mapping.ptr(), mapping.len(), MPOL_PREFERRED, &nodemask, + sizeof(nodemask) * BITS_PER_BYTE, 0), + SyscallSucceeds()); ASSERT_THAT(get_mempolicy(&mode, nullptr, 0, mapping.ptr(), MPOL_F_ADDR), SyscallSucceeds()); EXPECT_EQ(mode, MPOL_PREFERRED);