Skip to content

Commit

Permalink
🧇 mmap: fix size handling
Browse files Browse the repository at this point in the history
- Fix nil close on empty files.
- Handle size overflow.
- Handle incomplete mappings on Windows.

ref: https://github.com/golang/go/blob/master/src/cmd/go/internal/mmap/mmap_windows.go
  • Loading branch information
database64128 committed Jan 13, 2025
1 parent aff7d0a commit eac8b12
Show file tree
Hide file tree
Showing 2 changed files with 25 additions and 7 deletions.
19 changes: 13 additions & 6 deletions mmap/mmap.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,24 +7,31 @@ import (
"unsafe"
)

var ErrFileTooLarge = errors.New("file too large")

// ReadFile maps the named file into memory for reading.
// On success, it returns the mapped data as a byte slice or a string,
// and a function that unmaps the data.
func ReadFile[T ~[]byte | ~string](name string) (data T, close func() error, err error) {
f, err := os.Open(name)
if err != nil {
return
return data, nil, err
}
defer f.Close()

fs, err := f.Stat()
if err != nil {
return
return data, nil, err
}

size := fs.Size()
if size == 0 {
return
size64 := fs.Size()
if size64 == 0 {
return data, func() error { return nil }, nil
}

size := int(size64)
if int64(size) != size64 {
return data, nil, ErrFileTooLarge
}

addr, close, err := readFile(f, uintptr(size))
Expand All @@ -39,7 +46,7 @@ func ReadFile[T ~[]byte | ~string](name string) (data T, close func() error, err
return *(*T)(unsafe.Pointer(&b)), close, nil
}

func readFileFallback[T ~[]byte | ~string](f *os.File, size int64) (data T, close func() error, err error) {
func readFileFallback[T ~[]byte | ~string](f *os.File, size int) (data T, close func() error, err error) {
b := make([]byte, size)
if _, err = io.ReadFull(f, b); err != nil {
return
Expand Down
13 changes: 12 additions & 1 deletion mmap/mmap_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import (
"golang.org/x/sys/windows"
)

func readFile(f *os.File, _ uintptr) (addr unsafe.Pointer, close func() error, err error) {
func readFile(f *os.File, size uintptr) (addr unsafe.Pointer, close func() error, err error) {
handle, err := windows.CreateFileMapping(windows.Handle(f.Fd()), nil, windows.PAGE_READONLY, 0, 0, nil)
if err != nil {
return nil, nil, os.NewSyscallError("CreateFileMappingW", err)
Expand All @@ -18,6 +18,17 @@ func readFile(f *os.File, _ uintptr) (addr unsafe.Pointer, close func() error, e
if err != nil {
return nil, nil, os.NewSyscallError("MapViewOfFile", err)
}

var info windows.MemoryBasicInformation
if err := windows.VirtualQuery(addrUintptr, &info, unsafe.Sizeof(info)); err != nil {
_ = windows.UnmapViewOfFile(addrUintptr)
return nil, nil, os.NewSyscallError("VirtualQuery", err)
}
if info.RegionSize < size {
_ = windows.UnmapViewOfFile(addrUintptr)
return nil, nil, ErrFileTooLarge
}

return *(*unsafe.Pointer)(unsafe.Pointer(&addrUintptr)), // workaround for unsafeptr check in go vet, see https://github.com/golang/go/issues/58625
func() error {
return windows.UnmapViewOfFile(addrUintptr)
Expand Down

0 comments on commit eac8b12

Please sign in to comment.