From e0c3b6e6ae3bf6387b753f5062211beed2e71ded Mon Sep 17 00:00:00 2001 From: Anton Kuklin Date: Fri, 16 Jun 2023 19:17:12 +0000 Subject: [PATCH] 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 TryBot-Result: Gopher Robot Reviewed-by: Bryan Mills Reviewed-by: Ian Lance Taylor Run-TryBot: Ian Lance Taylor --- unix/mkerrors.sh | 2 +- unix/mremap.go | 40 +++++++++++++++++++++++++++++++++++ unix/mremap_test.go | 47 ++++++++++++++++++++++++++++++++++++++++++ unix/syscall_linux.go | 17 ++++++++++----- unix/zerrors_linux.go | 3 +++ unix/zsyscall_linux.go | 11 ++++++++++ 6 files changed, 114 insertions(+), 6 deletions(-) create mode 100644 unix/mremap.go create mode 100644 unix/mremap_test.go diff --git a/unix/mkerrors.sh b/unix/mkerrors.sh index 315646271..0c4d14929 100755 --- a/unix/mkerrors.sh +++ b/unix/mkerrors.sh @@ -519,7 +519,7 @@ ccflags="$@" $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 000000000..86213c05d --- /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 000000000..7b1655b81 --- /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 6de486bef..39de5f143 100644 --- a/unix/syscall_linux.go +++ b/unix/syscall_linux.go @@ -2124,11 +2124,15 @@ func writevRacedetect(iovecs []Iovec, n int) { // 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 @@ func Munmap(b []byte) (err error) { 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 @@ func Getresgid() (rgid, egid, sgid int) { // MqTimedreceive // MqTimedsend // MqUnlink -// Mremap // Msgctl // Msgget // Msgrcv diff --git a/unix/zerrors_linux.go b/unix/zerrors_linux.go index bd4a0fcf1..c097b48da 100644 --- a/unix/zerrors_linux.go +++ b/unix/zerrors_linux.go @@ -1904,6 +1904,9 @@ const ( 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 722c29a00..7ceec233f 100644 --- a/unix/zsyscall_linux.go +++ b/unix/zsyscall_linux.go @@ -1868,6 +1868,17 @@ func munmap(addr uintptr, length uintptr) (err error) { // 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 {