linux-user: madvise() returns 0 on unmapped ranges instead of ENOMEM

Host environment

  • Operating system: Arch Linux
  • OS/kernel version: 7.1.8-arch1-3
  • Architecture: x86_64
  • QEMU flavor: qemu-user (I've tested on qemu-x86_64)
  • QEMU version: upstream (tested against 0a1ebd44)

Emulated/Virtualized environment

qemu-user

  • Architecture: all

Description of problem

Currently, under qemu linux-user, calling madvise() with any hint advice value on a range that is partially or fully unmapped returns 0. This differs from the documented and expected behavior per madvise(2) and per the kernel code, which is to fail with ENOMEM.

Steps to reproduce

Here is a code snippet that is affected by the bug:

#define _GNU_SOURCE
#include <assert.h>
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <sys/mman.h>
#include <unistd.h>

int main(void)
{
    int pagesize = getpagesize();

    void *p = mmap(NULL, pagesize, PROT_READ,
                   MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
    assert(p != MAP_FAILED);
    int ret = munmap(p, pagesize);
    assert(ret == 0);

    errno = 0;
    ret = madvise(p, pagesize, MADV_NORMAL);
    int err = errno;
    printf("ret: %d errno: %s (%d)\n", ret, strerrorname_np(err), err);

    return 0;
}

Running locally results in ret: -1 errno: ENOMEM (12) while running under qemu-x86_64 results in ret: 0 errno: 0 (0).

Additional information

The bug is in target_madvise under linux-user/mmap.c which returns 0 for hint advice values without checking if the address range is mapped. I wrote a fix and some test cases and will send the patches soon, referencing this issue.