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
