qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 0/2] linux-user: mmap/mprotect prot values
@ 2020-05-19 18:56 Richard Henderson
  2020-05-19 18:56 ` [PATCH 1/2] linux-user: Validate mmap/mprotect prot value Richard Henderson
  2020-05-19 18:56 ` [PATCH 2/2] linux-user: Adjust guest page protection for the host Richard Henderson
  0 siblings, 2 replies; 8+ messages in thread
From: Richard Henderson @ 2020-05-19 18:56 UTC (permalink / raw)
  To: qemu-devel; +Cc: laurent

The new validation hook will give me a place to add extra
hooks to handle BTI and MTE.  But in the meantime, we can
adjust the host page protections to make PROT_EXEC reliable.


r~


Richard Henderson (2):
  linux-user: Validate mmap/mprotect prot value
  linux-user: Adjust guest page protection for the host

 linux-user/mmap.c | 110 ++++++++++++++++++++++++++++++++--------------
 1 file changed, 77 insertions(+), 33 deletions(-)

-- 
2.20.1



^ permalink raw reply	[flat|nested] 8+ messages in thread

* [PATCH 1/2] linux-user: Validate mmap/mprotect prot value
  2020-05-19 18:56 [PATCH 0/2] linux-user: mmap/mprotect prot values Richard Henderson
@ 2020-05-19 18:56 ` Richard Henderson
  2020-07-06 11:33   ` Peter Maydell
  2020-08-07  9:44   ` Laurent Vivier
  2020-05-19 18:56 ` [PATCH 2/2] linux-user: Adjust guest page protection for the host Richard Henderson
  1 sibling, 2 replies; 8+ messages in thread
From: Richard Henderson @ 2020-05-19 18:56 UTC (permalink / raw)
  To: qemu-devel; +Cc: laurent

The kernel will return -EINVAL for bits set in the prot argument
that are unknown or invalid.  Previously we were simply cropping
out the bits that we care about.

Introduce validate_prot_to_pageflags to perform this check in a
single place between the two syscalls.  Differentiate between
the target and host versions of prot.  Compute the qemu internal
page_flags value at the same time.

Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
---
 linux-user/mmap.c | 106 +++++++++++++++++++++++++++++++---------------
 1 file changed, 73 insertions(+), 33 deletions(-)

diff --git a/linux-user/mmap.c b/linux-user/mmap.c
index e378033797..36fd1e2250 100644
--- a/linux-user/mmap.c
+++ b/linux-user/mmap.c
@@ -59,64 +59,96 @@ void mmap_fork_end(int child)
         pthread_mutex_unlock(&mmap_mutex);
 }
 
+/*
+ * Validate target prot bitmask.
+ * Return the prot bitmask for the host in *HOST_PROT.
+ * Return 0 if the target prot bitmask is invalid, otherwise
+ * the internal qemu page_flags (which will include PAGE_VALID).
+ */
+static int validate_prot_to_pageflags(int *host_prot, int prot)
+{
+    int valid = PROT_READ | PROT_WRITE | PROT_EXEC | TARGET_PROT_SEM;
+    int page_flags = (prot & PAGE_BITS) | PAGE_VALID;
+
+    /*
+     * For the host, we need not pass anything except read/write/exec.
+     * While PROT_SEM is allowed by all hosts, it is also ignored, so
+     * don't bother transforming guest bit to host bit.  Any other
+     * target-specific prot bits will not be understood by the host
+     * and will need to be encoded into page_flags for qemu emulation.
+     */
+    *host_prot = prot & (PROT_READ | PROT_WRITE | PROT_EXEC);
+
+    return prot & ~valid ? 0 : page_flags;
+}
+
 /* NOTE: all the constants are the HOST ones, but addresses are target. */
-int target_mprotect(abi_ulong start, abi_ulong len, int prot)
+int target_mprotect(abi_ulong start, abi_ulong len, int target_prot)
 {
     abi_ulong end, host_start, host_end, addr;
-    int prot1, ret;
+    int prot1, ret, page_flags, host_prot;
 
-    trace_target_mprotect(start, len, prot);
+    trace_target_mprotect(start, len, target_prot);
 
-    if ((start & ~TARGET_PAGE_MASK) != 0)
+    if ((start & ~TARGET_PAGE_MASK) != 0) {
         return -TARGET_EINVAL;
+    }
+    page_flags = validate_prot_to_pageflags(&host_prot, target_prot);
+    if (!page_flags) {
+        return -TARGET_EINVAL;
+    }
     len = TARGET_PAGE_ALIGN(len);
     end = start + len;
     if (!guest_range_valid(start, len)) {
         return -TARGET_ENOMEM;
     }
-    prot &= PROT_READ | PROT_WRITE | PROT_EXEC;
-    if (len == 0)
+    if (len == 0) {
         return 0;
+    }
 
     mmap_lock();
     host_start = start & qemu_host_page_mask;
     host_end = HOST_PAGE_ALIGN(end);
     if (start > host_start) {
         /* handle host page containing start */
-        prot1 = prot;
-        for(addr = host_start; addr < start; addr += TARGET_PAGE_SIZE) {
+        prot1 = host_prot;
+        for (addr = host_start; addr < start; addr += TARGET_PAGE_SIZE) {
             prot1 |= page_get_flags(addr);
         }
         if (host_end == host_start + qemu_host_page_size) {
-            for(addr = end; addr < host_end; addr += TARGET_PAGE_SIZE) {
+            for (addr = end; addr < host_end; addr += TARGET_PAGE_SIZE) {
                 prot1 |= page_get_flags(addr);
             }
             end = host_end;
         }
-        ret = mprotect(g2h(host_start), qemu_host_page_size, prot1 & PAGE_BITS);
-        if (ret != 0)
+        ret = mprotect(g2h(host_start), qemu_host_page_size,
+                       prot1 & PAGE_BITS);
+        if (ret != 0) {
             goto error;
+        }
         host_start += qemu_host_page_size;
     }
     if (end < host_end) {
-        prot1 = prot;
-        for(addr = end; addr < host_end; addr += TARGET_PAGE_SIZE) {
+        prot1 = host_prot;
+        for (addr = end; addr < host_end; addr += TARGET_PAGE_SIZE) {
             prot1 |= page_get_flags(addr);
         }
-        ret = mprotect(g2h(host_end - qemu_host_page_size), qemu_host_page_size,
-                       prot1 & PAGE_BITS);
-        if (ret != 0)
+        ret = mprotect(g2h(host_end - qemu_host_page_size),
+                       qemu_host_page_size, prot1 & PAGE_BITS);
+        if (ret != 0) {
             goto error;
+        }
         host_end -= qemu_host_page_size;
     }
 
     /* handle the pages in the middle */
     if (host_start < host_end) {
-        ret = mprotect(g2h(host_start), host_end - host_start, prot);
-        if (ret != 0)
+        ret = mprotect(g2h(host_start), host_end - host_start, host_prot);
+        if (ret != 0) {
             goto error;
+        }
     }
-    page_set_flags(start, start + len, prot | PAGE_VALID);
+    page_set_flags(start, start + len, page_flags);
     mmap_unlock();
     return 0;
 error:
@@ -360,19 +392,26 @@ abi_ulong mmap_find_vma(abi_ulong start, abi_ulong size, abi_ulong align)
 }
 
 /* NOTE: all the constants are the HOST ones */
-abi_long target_mmap(abi_ulong start, abi_ulong len, int prot,
+abi_long target_mmap(abi_ulong start, abi_ulong len, int target_prot,
                      int flags, int fd, abi_ulong offset)
 {
     abi_ulong ret, end, real_start, real_end, retaddr, host_offset, host_len;
+    int page_flags, host_prot;
 
     mmap_lock();
-    trace_target_mmap(start, len, prot, flags, fd, offset);
+    trace_target_mmap(start, len, target_prot, flags, fd, offset);
 
     if (!len) {
         errno = EINVAL;
         goto fail;
     }
 
+    page_flags = validate_prot_to_pageflags(&host_prot, target_prot);
+    if (!page_flags) {
+        errno = EINVAL;
+        goto fail;
+    }
+
     /* Also check for overflows... */
     len = TARGET_PAGE_ALIGN(len);
     if (!len) {
@@ -438,14 +477,15 @@ abi_long target_mmap(abi_ulong start, abi_ulong len, int prot,
         /* Note: we prefer to control the mapping address. It is
            especially important if qemu_host_page_size >
            qemu_real_host_page_size */
-        p = mmap(g2h(start), host_len, prot,
+        p = mmap(g2h(start), host_len, host_prot,
                  flags | MAP_FIXED | MAP_ANONYMOUS, -1, 0);
-        if (p == MAP_FAILED)
+        if (p == MAP_FAILED) {
             goto fail;
+        }
         /* update start so that it points to the file position at 'offset' */
         host_start = (unsigned long)p;
         if (!(flags & MAP_ANONYMOUS)) {
-            p = mmap(g2h(start), len, prot,
+            p = mmap(g2h(start), len, host_prot,
                      flags | MAP_FIXED, fd, host_offset);
             if (p == MAP_FAILED) {
                 munmap(g2h(start), host_len);
@@ -479,19 +519,19 @@ abi_long target_mmap(abi_ulong start, abi_ulong len, int prot,
             /* msync() won't work here, so we return an error if write is
                possible while it is a shared mapping */
             if ((flags & MAP_TYPE) == MAP_SHARED &&
-                (prot & PROT_WRITE)) {
+                (host_prot & PROT_WRITE)) {
                 errno = EINVAL;
                 goto fail;
             }
-            retaddr = target_mmap(start, len, prot | PROT_WRITE,
+            retaddr = target_mmap(start, len, target_prot | PROT_WRITE,
                                   MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS,
                                   -1, 0);
             if (retaddr == -1)
                 goto fail;
             if (pread(fd, g2h(start), len, offset) == -1)
                 goto fail;
-            if (!(prot & PROT_WRITE)) {
-                ret = target_mprotect(start, len, prot);
+            if (!(host_prot & PROT_WRITE)) {
+                ret = target_mprotect(start, len, target_prot);
                 assert(ret == 0);
             }
             goto the_end;
@@ -502,13 +542,13 @@ abi_long target_mmap(abi_ulong start, abi_ulong len, int prot,
             if (real_end == real_start + qemu_host_page_size) {
                 /* one single host page */
                 ret = mmap_frag(real_start, start, end,
-                                prot, flags, fd, offset);
+                                host_prot, flags, fd, offset);
                 if (ret == -1)
                     goto fail;
                 goto the_end1;
             }
             ret = mmap_frag(real_start, start, real_start + qemu_host_page_size,
-                            prot, flags, fd, offset);
+                            host_prot, flags, fd, offset);
             if (ret == -1)
                 goto fail;
             real_start += qemu_host_page_size;
@@ -517,7 +557,7 @@ abi_long target_mmap(abi_ulong start, abi_ulong len, int prot,
         if (end < real_end) {
             ret = mmap_frag(real_end - qemu_host_page_size,
                             real_end - qemu_host_page_size, end,
-                            prot, flags, fd,
+                            host_prot, flags, fd,
                             offset + real_end - qemu_host_page_size - start);
             if (ret == -1)
                 goto fail;
@@ -533,13 +573,13 @@ abi_long target_mmap(abi_ulong start, abi_ulong len, int prot,
             else
                 offset1 = offset + real_start - start;
             p = mmap(g2h(real_start), real_end - real_start,
-                     prot, flags, fd, offset1);
+                     host_prot, flags, fd, offset1);
             if (p == MAP_FAILED)
                 goto fail;
         }
     }
  the_end1:
-    page_set_flags(start, start + len, prot | PAGE_VALID);
+    page_set_flags(start, start + len, page_flags);
  the_end:
     trace_target_mmap_complete(start);
     if (qemu_loglevel_mask(CPU_LOG_PAGE)) {
-- 
2.20.1



^ permalink raw reply related	[flat|nested] 8+ messages in thread

* [PATCH 2/2] linux-user: Adjust guest page protection for the host
  2020-05-19 18:56 [PATCH 0/2] linux-user: mmap/mprotect prot values Richard Henderson
  2020-05-19 18:56 ` [PATCH 1/2] linux-user: Validate mmap/mprotect prot value Richard Henderson
@ 2020-05-19 18:56 ` Richard Henderson
  2020-05-20  5:54   ` Philippe Mathieu-Daudé
                     ` (2 more replies)
  1 sibling, 3 replies; 8+ messages in thread
From: Richard Henderson @ 2020-05-19 18:56 UTC (permalink / raw)
  To: qemu-devel; +Cc: laurent

Executable guest pages are never directly executed by
the host, but do need to be readable for translation.

Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
---
 linux-user/mmap.c | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/linux-user/mmap.c b/linux-user/mmap.c
index 36fd1e2250..84662c3311 100644
--- a/linux-user/mmap.c
+++ b/linux-user/mmap.c
@@ -76,8 +76,12 @@ static int validate_prot_to_pageflags(int *host_prot, int prot)
      * don't bother transforming guest bit to host bit.  Any other
      * target-specific prot bits will not be understood by the host
      * and will need to be encoded into page_flags for qemu emulation.
+     *
+     * Pages that are executable by the guest will never be executed
+     * by the host, but the host will need to be able to read them.
      */
-    *host_prot = prot & (PROT_READ | PROT_WRITE | PROT_EXEC);
+    *host_prot = (prot & (PROT_READ | PROT_WRITE))
+               | (prot & PROT_EXEC ? PROT_READ : 0);
 
     return prot & ~valid ? 0 : page_flags;
 }
-- 
2.20.1



^ permalink raw reply related	[flat|nested] 8+ messages in thread

* Re: [PATCH 2/2] linux-user: Adjust guest page protection for the host
  2020-05-19 18:56 ` [PATCH 2/2] linux-user: Adjust guest page protection for the host Richard Henderson
@ 2020-05-20  5:54   ` Philippe Mathieu-Daudé
  2020-07-06 11:34   ` Peter Maydell
  2020-08-07  9:44   ` Laurent Vivier
  2 siblings, 0 replies; 8+ messages in thread
From: Philippe Mathieu-Daudé @ 2020-05-20  5:54 UTC (permalink / raw)
  To: Richard Henderson, qemu-devel; +Cc: laurent

On 5/19/20 8:56 PM, Richard Henderson wrote:
> Executable guest pages are never directly executed by
> the host, but do need to be readable for translation.
> 
> Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
> ---
>   linux-user/mmap.c | 6 +++++-
>   1 file changed, 5 insertions(+), 1 deletion(-)
> 
> diff --git a/linux-user/mmap.c b/linux-user/mmap.c
> index 36fd1e2250..84662c3311 100644
> --- a/linux-user/mmap.c
> +++ b/linux-user/mmap.c
> @@ -76,8 +76,12 @@ static int validate_prot_to_pageflags(int *host_prot, int prot)
>        * don't bother transforming guest bit to host bit.  Any other
>        * target-specific prot bits will not be understood by the host
>        * and will need to be encoded into page_flags for qemu emulation.
> +     *
> +     * Pages that are executable by the guest will never be executed
> +     * by the host, but the host will need to be able to read them.
>        */
> -    *host_prot = prot & (PROT_READ | PROT_WRITE | PROT_EXEC);
> +    *host_prot = (prot & (PROT_READ | PROT_WRITE))
> +               | (prot & PROT_EXEC ? PROT_READ : 0);
>   
>       return prot & ~valid ? 0 : page_flags;
>   }
> 

Reviewed-by: Philippe Mathieu-Daudé <f4bug@amsat.org>


^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH 1/2] linux-user: Validate mmap/mprotect prot value
  2020-05-19 18:56 ` [PATCH 1/2] linux-user: Validate mmap/mprotect prot value Richard Henderson
@ 2020-07-06 11:33   ` Peter Maydell
  2020-08-07  9:44   ` Laurent Vivier
  1 sibling, 0 replies; 8+ messages in thread
From: Peter Maydell @ 2020-07-06 11:33 UTC (permalink / raw)
  To: Richard Henderson; +Cc: QEMU Developers, Laurent Vivier

On Tue, 19 May 2020 at 19:57, Richard Henderson
<richard.henderson@linaro.org> wrote:
>
> The kernel will return -EINVAL for bits set in the prot argument
> that are unknown or invalid.  Previously we were simply cropping
> out the bits that we care about.
>
> Introduce validate_prot_to_pageflags to perform this check in a
> single place between the two syscalls.  Differentiate between
> the target and host versions of prot.  Compute the qemu internal
> page_flags value at the same time.
>
> Signed-off-by: Richard Henderson <richard.henderson@linaro.org>

Reviewed-by: Peter Maydell <peter.maydell@linaro.org>

thanks
-- PMM


^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH 2/2] linux-user: Adjust guest page protection for the host
  2020-05-19 18:56 ` [PATCH 2/2] linux-user: Adjust guest page protection for the host Richard Henderson
  2020-05-20  5:54   ` Philippe Mathieu-Daudé
@ 2020-07-06 11:34   ` Peter Maydell
  2020-08-07  9:44   ` Laurent Vivier
  2 siblings, 0 replies; 8+ messages in thread
From: Peter Maydell @ 2020-07-06 11:34 UTC (permalink / raw)
  To: Richard Henderson; +Cc: QEMU Developers, Laurent Vivier

On Tue, 19 May 2020 at 19:57, Richard Henderson
<richard.henderson@linaro.org> wrote:
>
> Executable guest pages are never directly executed by
> the host, but do need to be readable for translation.
>
> Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
> ---
>  linux-user/mmap.c | 6 +++++-
>  1 file changed, 5 insertions(+), 1 deletion(-)
>

Reviewed-by: Peter Maydell <peter.maydell@linaro.org>

thanks
-- PMM


^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH 1/2] linux-user: Validate mmap/mprotect prot value
  2020-05-19 18:56 ` [PATCH 1/2] linux-user: Validate mmap/mprotect prot value Richard Henderson
  2020-07-06 11:33   ` Peter Maydell
@ 2020-08-07  9:44   ` Laurent Vivier
  1 sibling, 0 replies; 8+ messages in thread
From: Laurent Vivier @ 2020-08-07  9:44 UTC (permalink / raw)
  To: Richard Henderson, qemu-devel

Le 19/05/2020 à 20:56, Richard Henderson a écrit :
> The kernel will return -EINVAL for bits set in the prot argument
> that are unknown or invalid.  Previously we were simply cropping
> out the bits that we care about.
> 
> Introduce validate_prot_to_pageflags to perform this check in a
> single place between the two syscalls.  Differentiate between
> the target and host versions of prot.  Compute the qemu internal
> page_flags value at the same time.
> 
> Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
> ---
>  linux-user/mmap.c | 106 +++++++++++++++++++++++++++++++---------------
>  1 file changed, 73 insertions(+), 33 deletions(-)
> 

Applied to my linux-user-for-5.2 branch.

Thanks,
Laurent



^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH 2/2] linux-user: Adjust guest page protection for the host
  2020-05-19 18:56 ` [PATCH 2/2] linux-user: Adjust guest page protection for the host Richard Henderson
  2020-05-20  5:54   ` Philippe Mathieu-Daudé
  2020-07-06 11:34   ` Peter Maydell
@ 2020-08-07  9:44   ` Laurent Vivier
  2 siblings, 0 replies; 8+ messages in thread
From: Laurent Vivier @ 2020-08-07  9:44 UTC (permalink / raw)
  To: Richard Henderson, qemu-devel

Le 19/05/2020 à 20:56, Richard Henderson a écrit :
> Executable guest pages are never directly executed by
> the host, but do need to be readable for translation.
> 
> Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
> ---
>  linux-user/mmap.c | 6 +++++-
>  1 file changed, 5 insertions(+), 1 deletion(-)
> 
> diff --git a/linux-user/mmap.c b/linux-user/mmap.c
> index 36fd1e2250..84662c3311 100644
> --- a/linux-user/mmap.c
> +++ b/linux-user/mmap.c
> @@ -76,8 +76,12 @@ static int validate_prot_to_pageflags(int *host_prot, int prot)
>       * don't bother transforming guest bit to host bit.  Any other
>       * target-specific prot bits will not be understood by the host
>       * and will need to be encoded into page_flags for qemu emulation.
> +     *
> +     * Pages that are executable by the guest will never be executed
> +     * by the host, but the host will need to be able to read them.
>       */
> -    *host_prot = prot & (PROT_READ | PROT_WRITE | PROT_EXEC);
> +    *host_prot = (prot & (PROT_READ | PROT_WRITE))
> +               | (prot & PROT_EXEC ? PROT_READ : 0);
>  
>      return prot & ~valid ? 0 : page_flags;
>  }
> 

Applied to my linux-user-for-5.2 branch.

Thanks,
Laurent


^ permalink raw reply	[flat|nested] 8+ messages in thread

end of thread, other threads:[~2020-08-07  9:45 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2020-05-19 18:56 [PATCH 0/2] linux-user: mmap/mprotect prot values Richard Henderson
2020-05-19 18:56 ` [PATCH 1/2] linux-user: Validate mmap/mprotect prot value Richard Henderson
2020-07-06 11:33   ` Peter Maydell
2020-08-07  9:44   ` Laurent Vivier
2020-05-19 18:56 ` [PATCH 2/2] linux-user: Adjust guest page protection for the host Richard Henderson
2020-05-20  5:54   ` Philippe Mathieu-Daudé
2020-07-06 11:34   ` Peter Maydell
2020-08-07  9:44   ` Laurent Vivier

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).