unix: add Mremap for linux

For golang/go#60409

Change-Id: I75a9732ee996f0aeb91599d80803f96ada468c27
GitHub-Last-Rev: c348b6194d9fbe92f4f912c66709b0af75810e99
GitHub-Pull-Request: golang/sys#164
Reviewed-on: https://go-review.googlesource.com/c/sys/+/502715
Auto-Submit: Ian Lance Taylor <iant@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Bryan Mills <bcmills@google.com>
Reviewed-by: Ian Lance Taylor <iant@google.com>
Run-TryBot: Ian Lance Taylor <iant@google.com>
diff --git a/unix/mkerrors.sh b/unix/mkerrors.sh
index 3156462..0c4d149 100755
--- a/unix/mkerrors.sh
+++ b/unix/mkerrors.sh
@@ -519,7 +519,7 @@
 		$2 ~ /^LOCK_(SH|EX|NB|UN)$/ ||
 		$2 ~ /^LO_(KEY|NAME)_SIZE$/ ||
 		$2 ~ /^LOOP_(CLR|CTL|GET|SET)_/ ||
-		$2 ~ /^(AF|SOCK|SO|SOL|IPPROTO|IP|IPV6|TCP|MCAST|EVFILT|NOTE|SHUT|PROT|MAP|MFD|T?PACKET|MSG|SCM|MCL|DT|MADV|PR|LOCAL|TCPOPT|UDP)_/ ||
+		$2 ~ /^(AF|SOCK|SO|SOL|IPPROTO|IP|IPV6|TCP|MCAST|EVFILT|NOTE|SHUT|PROT|MAP|MREMAP|MFD|T?PACKET|MSG|SCM|MCL|DT|MADV|PR|LOCAL|TCPOPT|UDP)_/ ||
 		$2 ~ /^NFC_(GENL|PROTO|COMM|RF|SE|DIRECTION|LLCP|SOCKPROTO)_/ ||
 		$2 ~ /^NFC_.*_(MAX)?SIZE$/ ||
 		$2 ~ /^RAW_PAYLOAD_/ ||
diff --git a/unix/mremap.go b/unix/mremap.go
new file mode 100644
index 0000000..86213c0
--- /dev/null
+++ b/unix/mremap.go
@@ -0,0 +1,40 @@
+// Copyright 2023 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+//go:build linux
+// +build linux
+
+package unix
+
+import "unsafe"
+
+type mremapMmapper struct {
+	mmapper
+	mremap func(oldaddr uintptr, oldlength uintptr, newlength uintptr, flags int, newaddr uintptr) (xaddr uintptr, err error)
+}
+
+func (m *mremapMmapper) Mremap(oldData []byte, newLength int, flags int) (data []byte, err error) {
+	if newLength <= 0 || len(oldData) == 0 || len(oldData) != cap(oldData) || flags&MREMAP_FIXED != 0 {
+		return nil, EINVAL
+	}
+
+	pOld := &oldData[cap(oldData)-1]
+	m.Lock()
+	defer m.Unlock()
+	bOld := m.active[pOld]
+	if bOld == nil || &bOld[0] != &oldData[0] {
+		return nil, EINVAL
+	}
+	newAddr, errno := m.mremap(uintptr(unsafe.Pointer(&bOld[0])), uintptr(len(bOld)), uintptr(newLength), flags, 0)
+	if errno != nil {
+		return nil, errno
+	}
+	bNew := unsafe.Slice((*byte)(unsafe.Pointer(newAddr)), newLength)
+	pNew := &bNew[cap(bNew)-1]
+	if flags&MREMAP_DONTUNMAP == 0 {
+		delete(m.active, pOld)
+	}
+	m.active[pNew] = bNew
+	return bNew, nil
+}
diff --git a/unix/mremap_test.go b/unix/mremap_test.go
new file mode 100644
index 0000000..7b1655b
--- /dev/null
+++ b/unix/mremap_test.go
@@ -0,0 +1,47 @@
+// Copyright 2023 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+//go:build linux
+// +build linux
+
+package unix_test
+
+import (
+	"testing"
+
+	"golang.org/x/sys/unix"
+)
+
+func TestMremap(t *testing.T) {
+	b, err := unix.Mmap(-1, 0, unix.Getpagesize(), unix.PROT_NONE, unix.MAP_ANON|unix.MAP_PRIVATE)
+	if err != nil {
+		t.Fatalf("Mmap: %v", err)
+	}
+	if err := unix.Mprotect(b, unix.PROT_READ|unix.PROT_WRITE); err != nil {
+		t.Fatalf("Mprotect: %v", err)
+	}
+
+	b[0] = 42
+
+	bNew, err := unix.Mremap(b, unix.Getpagesize()*2, unix.MREMAP_MAYMOVE)
+	if err != nil {
+		t.Fatalf("Mremap2: %v", err)
+	}
+	bNew[unix.Getpagesize()+1] = 84 // checks
+
+	if bNew[0] != 42 {
+		t.Fatal("first element value was changed")
+	}
+	if len(bNew) != unix.Getpagesize()*2 {
+		t.Fatal("new memory len not equal to specified len")
+	}
+	if cap(bNew) != unix.Getpagesize()*2 {
+		t.Fatal("new memory cap not equal to specified len")
+	}
+
+	_, err = unix.Mremap(b, unix.Getpagesize(), unix.MREMAP_FIXED)
+	if err != unix.EINVAL {
+		t.Fatalf("unix.MREMAP_FIXED should be forbidden")
+	}
+}
diff --git a/unix/syscall_linux.go b/unix/syscall_linux.go
index 6de486b..39de5f1 100644
--- a/unix/syscall_linux.go
+++ b/unix/syscall_linux.go
@@ -2124,11 +2124,15 @@
 
 // mmap varies by architecture; see syscall_linux_*.go.
 //sys	munmap(addr uintptr, length uintptr) (err error)
+//sys	mremap(oldaddr uintptr, oldlength uintptr, newlength uintptr, flags int, newaddr uintptr) (xaddr uintptr, err error)
 
-var mapper = &mmapper{
-	active: make(map[*byte][]byte),
-	mmap:   mmap,
-	munmap: munmap,
+var mapper = &mremapMmapper{
+	mmapper: mmapper{
+		active: make(map[*byte][]byte),
+		mmap:   mmap,
+		munmap: munmap,
+	},
+	mremap: mremap,
 }
 
 func Mmap(fd int, offset int64, length int, prot int, flags int) (data []byte, err error) {
@@ -2139,6 +2143,10 @@
 	return mapper.Munmap(b)
 }
 
+func Mremap(oldData []byte, newLength int, flags int) (data []byte, err error) {
+	return mapper.Mremap(oldData, newLength, flags)
+}
+
 //sys	Madvise(b []byte, advice int) (err error)
 //sys	Mprotect(b []byte, prot int) (err error)
 //sys	Mlock(b []byte) (err error)
@@ -2487,7 +2495,6 @@
 // MqTimedreceive
 // MqTimedsend
 // MqUnlink
-// Mremap
 // Msgctl
 // Msgget
 // Msgrcv
diff --git a/unix/zerrors_linux.go b/unix/zerrors_linux.go
index bd4a0fc..c097b48 100644
--- a/unix/zerrors_linux.go
+++ b/unix/zerrors_linux.go
@@ -1904,6 +1904,9 @@
 	MOUNT_ATTR_SIZE_VER0                        = 0x20
 	MOUNT_ATTR_STRICTATIME                      = 0x20
 	MOUNT_ATTR__ATIME                           = 0x70
+	MREMAP_DONTUNMAP                            = 0x4
+	MREMAP_FIXED                                = 0x2
+	MREMAP_MAYMOVE                              = 0x1
 	MSDOS_SUPER_MAGIC                           = 0x4d44
 	MSG_BATCH                                   = 0x40000
 	MSG_CMSG_CLOEXEC                            = 0x40000000
diff --git a/unix/zsyscall_linux.go b/unix/zsyscall_linux.go
index 722c29a..7ceec23 100644
--- a/unix/zsyscall_linux.go
+++ b/unix/zsyscall_linux.go
@@ -1868,6 +1868,17 @@
 
 // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
 
+func mremap(oldaddr uintptr, oldlength uintptr, newlength uintptr, flags int, newaddr uintptr) (xaddr uintptr, err error) {
+	r0, _, e1 := Syscall6(SYS_MREMAP, uintptr(oldaddr), uintptr(oldlength), uintptr(newlength), uintptr(flags), uintptr(newaddr), 0)
+	xaddr = uintptr(r0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
 func Madvise(b []byte, advice int) (err error) {
 	var _p0 unsafe.Pointer
 	if len(b) > 0 {