* [PULL 1/2] linux-user: allow a zero-length getrandom with a NULL buffer
2026-11-05 22:55 [PULL 0/2] Linux user next patches Helge Deller
@ 2026-11-05 22:55 ` Helge Deller
2026-11-05 22:55 ` [PULL 2/2] linux-user: report EFAULT for a NULL sysinfo/getitimer buffer Helge Deller
2026-11-06 20:25 ` [PULL 0/2] Linux user next patches Richard Henderson
2 siblings, 0 replies; 4+ messages in thread
From: Helge Deller @ 2026-11-05 22:55 UTC (permalink / raw)
To: qemu-devel; +Cc: Pierrick Bouvier, Laurent Vivier, Helge Deller
From: TAKANO Mitsuhiro <takano32@gmail.com>
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 <takano32@gmail.com>
Signed-off-by: Helge Deller <deller@gmx.de>
---
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
^ permalink raw reply related [flat|nested] 4+ messages in thread* [PULL 2/2] linux-user: report EFAULT for a NULL sysinfo/getitimer buffer
2026-11-05 22:55 [PULL 0/2] Linux user next patches Helge Deller
2026-11-05 22:55 ` [PULL 1/2] linux-user: allow a zero-length getrandom with a NULL buffer Helge Deller
@ 2026-11-05 22:55 ` Helge Deller
2026-11-06 20:25 ` [PULL 0/2] Linux user next patches Richard Henderson
2 siblings, 0 replies; 4+ messages in thread
From: Helge Deller @ 2026-11-05 22:55 UTC (permalink / raw)
To: qemu-devel; +Cc: Pierrick Bouvier, Laurent Vivier, Helge Deller
From: TAKANO Mitsuhiro <takano32@gmail.com>
Both handlers skip the copy-out when the guest pointer is zero and return
success, so a caller that forgets the buffer gets an uninitialised result
instead of the EFAULT the kernel reports for a NULL address.
Copy unconditionally: a NULL pointer then fails in lock_user_struct() and
in copy_to_user_timeval() like any other unusable address.
Signed-off-by: TAKANO Mitsuhiro <takano32@gmail.com>
Signed-off-by: Helge Deller <deller@gmx.de>
---
linux-user/syscall.c | 4 +--
tests/tcg/multiarch/linux/linux-null-buffer.c | 35 +++++++++++++++++--
2 files changed, 34 insertions(+), 5 deletions(-)
diff --git a/linux-user/syscall.c b/linux-user/syscall.c
index 59cbf5feaa..2fc61b3eab 100644
--- a/linux-user/syscall.c
+++ b/linux-user/syscall.c
@@ -11551,7 +11551,7 @@ static abi_long do_syscall1(CPUArchState *cpu_env, int num, abi_long arg1,
struct itimerval value;
ret = get_errno(getitimer(arg1, &value));
- if (!is_error(ret) && arg2) {
+ if (!is_error(ret)) {
if (copy_to_user_timeval(arg2,
&value.it_interval)
|| copy_to_user_timeval(arg2 + sizeof(struct target_timeval),
@@ -11666,7 +11666,7 @@ static abi_long do_syscall1(CPUArchState *cpu_env, int num, abi_long arg1,
struct target_sysinfo *target_value;
struct sysinfo value;
ret = get_errno(sysinfo(&value));
- if (!is_error(ret) && arg1)
+ if (!is_error(ret))
{
if (!lock_user_struct(VERIFY_WRITE, target_value, arg1, 0))
return -TARGET_EFAULT;
diff --git a/tests/tcg/multiarch/linux/linux-null-buffer.c b/tests/tcg/multiarch/linux/linux-null-buffer.c
index 6246e47707..b099803c57 100644
--- a/tests/tcg/multiarch/linux/linux-null-buffer.c
+++ b/tests/tcg/multiarch/linux/linux-null-buffer.c
@@ -1,6 +1,15 @@
/*
- * A zero-length getrandom() needs no valid buffer: the kernel never looks at
- * the pointer, so getrandom(NULL, 0, 0) succeeds.
+ * Degenerate buffer pointers must behave like they do in the kernel.
+ *
+ * A zero-length getrandom() needs no valid buffer: the pointer is never
+ * used, so getrandom(NULL, 0, 0) succeeds. A required output buffer, on
+ * the other hand, must not be silently skipped: sysinfo() and getitimer()
+ * have to report EFAULT for a NULL pointer.
+ *
+ * The NULL cases go through syscall() rather than the libc wrappers: on
+ * 32-bit x86, for example, getitimer() hands the kernel a local structure
+ * and copies the result out itself, so it would follow the NULL pointer
+ * before the kernel's answer could be seen.
*
* SPDX-License-Identifier: GPL-2.0-or-later
*/
@@ -9,11 +18,31 @@
#include <errno.h>
#include <stdlib.h>
#include <sys/random.h>
+#include <sys/syscall.h>
+#include <sys/sysinfo.h>
+#include <sys/time.h>
+#include <unistd.h>
int main(void)
{
+ char buf[8];
+ struct itimerval it;
+ struct sysinfo si;
+
+ /* A zero-length request is not rejected for a NULL buffer. */
errno = 0;
- assert(getrandom(NULL, 0, 0) == 0);
+ assert(syscall(SYS_getrandom, NULL, 0, 0) == 0);
+
+ /* A NULL output pointer is reported, not silently skipped. */
+ errno = 0;
+ assert(syscall(SYS_sysinfo, NULL) == -1 && errno == EFAULT);
+ errno = 0;
+ assert(syscall(SYS_getitimer, ITIMER_REAL, NULL) == -1 && errno == EFAULT);
+
+ /* The ordinary calls still work. */
+ assert(getrandom(buf, sizeof(buf), 0) == sizeof(buf));
+ assert(sysinfo(&si) == 0);
+ assert(getitimer(ITIMER_REAL, &it) == 0);
return EXIT_SUCCESS;
}
--
2.54.0
^ permalink raw reply related [flat|nested] 4+ messages in thread