From: TAKANO Mitsuhiro <[email protected]>

The kernel never looks at the buffer of a zero-length request, so
getrandom(NULL, 0, 0) returns 0.  qemu-user hands the request to
lock_user() unconditionally, and a NULL or otherwise unusable address
fails there with EFAULT even though nothing is copied.

read(), write(), pread64() and pwrite64() already special-case the
zero-length request; do the same here.  The address plays no part in it:
with a zero length it is never dereferenced, whatever value the caller
passes.

Fixes: f894efd19917 ("linux-user: add getrandom() syscall")
Signed-off-by: TAKANO Mitsuhiro <[email protected]>
Signed-off-by: Helge Deller <[email protected]>
---
 linux-user/syscall.c                          |  8 ++++++++
 tests/tcg/multiarch/linux/linux-null-buffer.c | 19 +++++++++++++++++++
 tests/tcg/multiarch/meson.build               |  1 +
 3 files changed, 28 insertions(+)
 create mode 100644 tests/tcg/multiarch/linux/linux-null-buffer.c

diff --git a/linux-user/syscall.c b/linux-user/syscall.c
index 9d85a193ef..59cbf5feaa 100644
--- a/linux-user/syscall.c
+++ b/linux-user/syscall.c
@@ -11456,6 +11456,14 @@ static abi_long do_syscall1(CPUArchState *cpu_env, int 
num, abi_long arg1,
 #endif
 #if defined(TARGET_NR_getrandom) && defined(__NR_getrandom)
     case TARGET_NR_getrandom:
+        if (arg2 == 0) {
+            /*
+             * A zero-length request has no buffer to lock, and the kernel
+             * never looks at the pointer for it, so pass the call through
+             * instead of failing on a NULL or otherwise unusable address.
+             */
+            return get_errno(getrandom(NULL, 0, arg3));
+        }
         p = lock_user(VERIFY_WRITE, arg1, arg2, 0);
         if (!p) {
             return -TARGET_EFAULT;
diff --git a/tests/tcg/multiarch/linux/linux-null-buffer.c 
b/tests/tcg/multiarch/linux/linux-null-buffer.c
new file mode 100644
index 0000000000..6246e47707
--- /dev/null
+++ b/tests/tcg/multiarch/linux/linux-null-buffer.c
@@ -0,0 +1,19 @@
+/*
+ * A zero-length getrandom() needs no valid buffer: the kernel never looks at
+ * the pointer, so getrandom(NULL, 0, 0) succeeds.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+#define _GNU_SOURCE
+#include <assert.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <sys/random.h>
+
+int main(void)
+{
+    errno = 0;
+    assert(getrandom(NULL, 0, 0) == 0);
+
+    return EXIT_SUCCESS;
+}
diff --git a/tests/tcg/multiarch/meson.build b/tests/tcg/multiarch/meson.build
index e60a052700..8f2f57d9ab 100644
--- a/tests/tcg/multiarch/meson.build
+++ b/tests/tcg/multiarch/meson.build
@@ -57,6 +57,7 @@ tests += {
 # Linux tests
 tests += {
   multiarch/'linux/linux-madvise.c': {},
+  multiarch/'linux/linux-null-buffer.c': {},
   multiarch/'linux/linux-shmat-maps.c': {},
   multiarch/'linux/linux-shmat-null.c': {},
   multiarch/'linux/linux-sigrtminmax.c': {},
-- 
2.54.0


Reply via email to