From: Igor Shevlyakov <[email protected]>

mincore(2) rejects a misaligned start address before it looks the range
up, so a start that is both misaligned and unmapped is EINVAL.  Here
lock_user() runs first and fails, and the call reports ENOMEM instead.

When the range is mapped this changes nothing -- the host syscall
receives the misaligned pointer and answers EINVAL by itself -- which is
why only the unmapped case is visibly wrong.

Caught by glibc's misc/test-errno-linux.

Signed-off-by: Igor Shevlyakov <[email protected]>
Reviewed-by: Helge Deller <[email protected]>
Signed-off-by: Helge Deller <[email protected]>
---
 linux-user/syscall.c | 12 +++++++++++-
 1 file changed, 11 insertions(+), 1 deletion(-)

diff --git a/linux-user/syscall.c b/linux-user/syscall.c
index e7c9fb33a3..0380691a8b 100644
--- a/linux-user/syscall.c
+++ b/linux-user/syscall.c
@@ -13064,7 +13064,17 @@ static abi_long do_syscall1(CPUArchState *cpu_env, int 
num, abi_long arg1,
 #ifdef TARGET_NR_mincore
     case TARGET_NR_mincore:
         {
-            void *a = lock_user(VERIFY_NONE, arg1, arg2, 0);
+            void *a;
+
+            /*
+             * The kernel rejects a misaligned start before it looks the range
+             * up, so a start that is both misaligned and unmapped is EINVAL,
+             * not the ENOMEM that a failing lock_user() would report.
+             */
+            if (arg1 & ~TARGET_PAGE_MASK) {
+                return -TARGET_EINVAL;
+            }
+            a = lock_user(VERIFY_NONE, arg1, arg2, 0);
             if (!a) {
                 return -TARGET_ENOMEM;
             }
-- 
2.54.0


Reply via email to