Răsfoiți Sursa

mips: fix mmap64() for large offsets on n32

mmap64() of /dev/mem at a >2GB offset (tst-mmap2, the off_t
sign-extension regression test) failed with EINVAL on n32.

n32 has no mmap2 syscall, so mmap64() takes the generic
"#ifndef __NR_mmap2" path in libc/sysdeps/linux/common/mmap64.c.  That
path rejects any offset that does not fit in a (32-bit on n32) off_t:

	if (offset != (off_t) offset || ...) { errno = EINVAL; ... }
	return mmap(addr, len, prot, flags, fd, (off_t) offset);

For 0xfffff000 the (off_t) cast truncates and sign-extends, so the
comparison is true and mmap64() bails out in userspace before any
syscall.  o32 is unaffected (it has mmap2) and n64 is unaffected (off_t
is 64-bit), so only n32 hit this.

The n32 mmap syscall takes a full 64-bit byte offset, so add a mips
mmap64.c that passes the offset straight to __NR_mmap.  Verified on
qemu-system-mips64 (mips64-be-n32): tst-mmap2 now passes; o32 and n64
keep the common implementation.

Signed-off-by: Ramin Moussavi <ramin.moussavi@yacoub.de>
ramin 4 zile în urmă
părinte
comite
304b850bb2
2 a modificat fișierele cu 32 adăugiri și 1 ștergeri
  1. 1 1
      libc/sysdeps/linux/mips/Makefile.arch
  2. 31 0
      libc/sysdeps/linux/mips/mmap64.c

+ 1 - 1
libc/sysdeps/linux/mips/Makefile.arch

@@ -8,7 +8,7 @@
 CSRC-y := \
 	__longjmp.c  brk.c setjmp_aux.c \
 	pread_write.c sigaction.c _test_and_set.c \
-	fallocate64.c
+	fallocate64.c mmap64.c
 
 SSRC-y := bsd-_setjmp.S bsd-setjmp.S setjmp.S syscall.S pipe.S syscall_error.S \
 	  vfork.S clone.S

+ 31 - 0
libc/sysdeps/linux/mips/mmap64.c

@@ -0,0 +1,31 @@
+/*
+ * mmap64() for MIPS uClibc
+ *
+ * Copyright (C) 2026 Ramin Moussavi <ramin.moussavi@yacoub.de>
+ *
+ * Licensed under the LGPL v2.1, see the file COPYING.LIB in this tarball.
+ */
+
+#include <_lfs_64.h>
+#include <sys/syscall.h>
+#include <sgidefs.h>
+
+#if _MIPS_SIM == _MIPS_SIM_NABI32 && defined __NR_mmap
+
+# include <errno.h>
+# include <sys/mman.h>
+
+/* n32 has no mmap2 and a 32-bit off_t, so the generic mmap64() (which either
+ * needs mmap2 or truncates the offset to off_t and rejects anything that does
+ * not fit) cannot express a >2GB offset.  But the n32 mmap syscall takes a
+ * full 64-bit byte offset, so pass it straight through.  */
+void *mmap64(void *addr, size_t len, int prot, int flags, int fd,
+	     __off64_t offset)
+{
+	return (void *) INLINE_SYSCALL(mmap, 6, addr, len, prot, flags, fd,
+				       offset);
+}
+
+#else
+# include "../common/mmap64.c"
+#endif