Per madvise(2) and the Linux kernel implementation (madvise_walk_vmas),
madvise() must validate that the requested range is currently mapped
and return -ENOMEM if any page in the range is unmapped.

Add a page_check_range(start, len, PAGE_VALID) check for valid advice
values before proceeding with the advice actions. In addition, extend the
tcg multiarch test linux-madvise.c to test this behaviour.

Resolves: https://gitlab.com/qemu-project/qemu/-/issues/4382
AI-used-for: importing and validating test case
Signed-off-by: Alex Bennée <[email protected]>

---
NOTE
  - again testing the minimal agents which did stop and say:

*(Note: Per user policy, git commits are never executed automatically by the 
agent. Please review the diff with `git diff` and commit the changes if you are 
satisfied.)*

  but non-the-less imported the test and wrote a crap patch which I
  have re-done dropping a load of unneeded verbosity.
---
 linux-user/mmap.c                         | 12 ++++++++++++
 tests/tcg/multiarch/linux/linux-madvise.c | 20 ++++++++++++++++++++
 2 files changed, 32 insertions(+)

diff --git a/linux-user/mmap.c b/linux-user/mmap.c
index cc0c2ee6c27..4066072ff45 100644
--- a/linux-user/mmap.c
+++ b/linux-user/mmap.c
@@ -1307,6 +1307,16 @@ abi_long target_madvise(abi_ulong start, abi_ulong 
len_in, int advice)
      * though.
      */
     mmap_lock();
+
+    /*
+     * Whatever advice if the pages are not currently mapped, or are
+     * outside the address space of the process.
+     */
+    if (!page_check_range(start, len, PAGE_VALID)) {
+        ret = -TARGET_ENOMEM;
+        goto unlock;
+    }
+
     switch (advice) {
     case MADV_NORMAL:
     case MADV_RANDOM:
@@ -1358,6 +1368,8 @@ abi_long target_madvise(abi_ulong start, abi_ulong 
len_in, int advice)
         ret = -EINVAL; /* not yet known advise */
         break;
     }
+
+ unlock:
     mmap_unlock();
 
     return ret;
diff --git a/tests/tcg/multiarch/linux/linux-madvise.c 
b/tests/tcg/multiarch/linux/linux-madvise.c
index 539fb3b7726..ebb9666c919 100644
--- a/tests/tcg/multiarch/linux/linux-madvise.c
+++ b/tests/tcg/multiarch/linux/linux-madvise.c
@@ -1,4 +1,5 @@
 #include <assert.h>
+#include <errno.h>
 #include <stdlib.h>
 #include <sys/mman.h>
 #include <unistd.h>
@@ -63,10 +64,29 @@ static void test_file(void)
     assert(ret == 0);
 }
 
+static void test_unmapped(void)
+{
+    int pagesize = getpagesize();
+    void *page;
+    int ret;
+
+    page = mmap(NULL, pagesize, PROT_READ, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
+    assert(page != MAP_FAILED);
+
+    ret = munmap(page, pagesize);
+    assert(ret == 0);
+
+    errno = 0;
+    ret = madvise(page, pagesize, MADV_NORMAL);
+    assert(ret == -1);
+    assert(errno == ENOMEM);
+}
+
 int main(void)
 {
     test_anonymous();
     test_file();
+    test_unmapped();
 
     return EXIT_SUCCESS;
 }
-- 
2.47.3


Reply via email to