From 3043c355f430bb304bc0d09d8162632baa028a83 Mon Sep 17 00:00:00 2001 From: Keith Randall Date: Fri, 3 Nov 2017 10:30:14 -0700 Subject: [PATCH] bytes: add more page boundary tests Make sure Index and IndexByte don't read past the queried byte slice. Hopefully will be helpful for CL 33597. Also remove the code which maps/unmaps the Go heap. Much safer to play with protection bits off-heap. Change-Id: I50d73e879b2d83285e1bc7c3e810efe4c245fe75 Reviewed-on: https://go-review.googlesource.com/75890 Reviewed-by: Ian Lance Taylor --- src/bytes/boundary_test.go | 80 ++++++++++++++++++++++++++++++++++++++ src/bytes/equal_test.go | 47 ---------------------- 2 files changed, 80 insertions(+), 47 deletions(-) create mode 100644 src/bytes/boundary_test.go delete mode 100644 src/bytes/equal_test.go diff --git a/src/bytes/boundary_test.go b/src/bytes/boundary_test.go new file mode 100644 index 0000000000..f9e20e36c7 --- /dev/null +++ b/src/bytes/boundary_test.go @@ -0,0 +1,80 @@ +// Copyright 2017 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. +// +// +build linux + +package bytes_test + +import ( + . "bytes" + "syscall" + "testing" +) + +// This file tests the situation where byte operations are checking +// data very near to a page boundary. We want to make sure those +// operations do not read across the boundary and cause a page +// fault where they shouldn't. + +// These tests run only on linux. The code being tested is +// not OS-specific, so it does not need to be tested on all +// operating systems. + +// dangerousSlice returns a slice which is immediately +// preceded and followed by a faulting page. +func dangerousSlice(t *testing.T) []byte { + pagesize := syscall.Getpagesize() + b, err := syscall.Mmap(0, 0, 3*pagesize, syscall.PROT_READ|syscall.PROT_WRITE, syscall.MAP_ANONYMOUS|syscall.MAP_PRIVATE) + if err != nil { + t.Fatalf("mmap failed %s", err) + } + err = syscall.Mprotect(b[:pagesize], syscall.PROT_NONE) + if err != nil { + t.Fatalf("mprotect low failed %s\n", err) + } + err = syscall.Mprotect(b[2*pagesize:], syscall.PROT_NONE) + if err != nil { + t.Fatalf("mprotect high failed %s\n", err) + } + return b[pagesize : 2*pagesize] +} + +func TestEqualNearPageBoundary(t *testing.T) { + t.Parallel() + b := dangerousSlice(t) + for i := range b { + b[i] = 'A' + } + for i := 0; i <= len(b); i++ { + Equal(b[:i], b[len(b)-i:]) + Equal(b[len(b)-i:], b[:i]) + } +} + +func TestIndexByteNearPageBoundary(t *testing.T) { + t.Parallel() + b := dangerousSlice(t) + for i := range b { + idx := IndexByte(b[i:], 1) + if idx != -1 { + t.Fatalf("IndexByte(b[%d:])=%d, want -1\n", i, idx) + } + } +} + +func TestIndexNearPageBoundary(t *testing.T) { + t.Parallel() + var q [64]byte + b := dangerousSlice(t) + for j := 1; j < len(q); j++ { + q[j-1] = 1 // difference is only found on the last byte + for i := range b { + idx := Index(b[i:], q[:j]) + if idx != -1 { + t.Fatalf("Index(b[%d:], q[:%d])=%d, want -1\n", i, j, idx) + } + } + q[j-1] = 0 + } +} diff --git a/src/bytes/equal_test.go b/src/bytes/equal_test.go deleted file mode 100644 index 9fdead8a60..0000000000 --- a/src/bytes/equal_test.go +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright 2013 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. -// -// +build linux - -package bytes_test - -import ( - . "bytes" - "syscall" - "testing" - "unsafe" -) - -// This file tests the situation where memeq is checking -// data very near to a page boundary. We want to make sure -// equal does not read across the boundary and cause a page -// fault where it shouldn't. - -// This test runs only on linux. The code being tested is -// not OS-specific, so it does not need to be tested on all -// operating systems. - -func TestEqualNearPageBoundary(t *testing.T) { - pagesize := syscall.Getpagesize() - b := make([]byte, 4*pagesize) - i := pagesize - for ; uintptr(unsafe.Pointer(&b[i]))%uintptr(pagesize) != 0; i++ { - } - syscall.Mprotect(b[i-pagesize:i], 0) - syscall.Mprotect(b[i+pagesize:i+2*pagesize], 0) - defer syscall.Mprotect(b[i-pagesize:i], syscall.PROT_READ|syscall.PROT_WRITE) - defer syscall.Mprotect(b[i+pagesize:i+2*pagesize], syscall.PROT_READ|syscall.PROT_WRITE) - - // both of these should fault - //pagesize += int(b[i-1]) - //pagesize += int(b[i+pagesize]) - - for j := 0; j < pagesize; j++ { - b[i+j] = 'A' - } - for j := 0; j <= pagesize; j++ { - Equal(b[i:i+j], b[i+pagesize-j:i+pagesize]) - Equal(b[i+pagesize-j:i+pagesize], b[i:i+j]) - } -} -- 2.48.1