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