| // Copyright 2018 The gVisor Authors. |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| package safecopy |
| |
| import ( |
| "bytes" |
| "fmt" |
| "io/ioutil" |
| "math/rand" |
| "os" |
| "runtime/debug" |
| "syscall" |
| "testing" |
| "unsafe" |
| ) |
| |
| // Size of a page in bytes. Cloned from usermem.PageSize to avoid a circular |
| // dependency. |
| const pageSize = 4096 |
| |
| func initRandom(b []byte) { |
| for i := range b { |
| b[i] = byte(rand.Intn(256)) |
| } |
| } |
| |
| func randBuf(size int) []byte { |
| b := make([]byte, size) |
| initRandom(b) |
| return b |
| } |
| |
| func TestCopyInSuccess(t *testing.T) { |
| // Test that CopyIn does not return an error when all pages are accessible. |
| const bufLen = 8192 |
| a := randBuf(bufLen) |
| b := make([]byte, bufLen) |
| |
| n, err := CopyIn(b, unsafe.Pointer(&a[0])) |
| if n != bufLen { |
| t.Errorf("Unexpected copy length, got %v, want %v", n, bufLen) |
| } |
| if err != nil { |
| t.Errorf("Unexpected error: %v", err) |
| } |
| if !bytes.Equal(a, b) { |
| t.Errorf("Buffers are not equal when they should be: %v %v", a, b) |
| } |
| } |
| |
| func TestCopyOutSuccess(t *testing.T) { |
| // Test that CopyOut does not return an error when all pages are |
| // accessible. |
| const bufLen = 8192 |
| a := randBuf(bufLen) |
| b := make([]byte, bufLen) |
| |
| n, err := CopyOut(unsafe.Pointer(&b[0]), a) |
| if n != bufLen { |
| t.Errorf("Unexpected copy length, got %v, want %v", n, bufLen) |
| } |
| if err != nil { |
| t.Errorf("Unexpected error: %v", err) |
| } |
| if !bytes.Equal(a, b) { |
| t.Errorf("Buffers are not equal when they should be: %v %v", a, b) |
| } |
| } |
| |
| func TestCopySuccess(t *testing.T) { |
| // Test that Copy does not return an error when all pages are accessible. |
| const bufLen = 8192 |
| a := randBuf(bufLen) |
| b := make([]byte, bufLen) |
| |
| n, err := Copy(unsafe.Pointer(&b[0]), unsafe.Pointer(&a[0]), bufLen) |
| if n != bufLen { |
| t.Errorf("Unexpected copy length, got %v, want %v", n, bufLen) |
| } |
| if err != nil { |
| t.Errorf("Unexpected error: %v", err) |
| } |
| if !bytes.Equal(a, b) { |
| t.Errorf("Buffers are not equal when they should be: %v %v", a, b) |
| } |
| } |
| |
| func TestZeroOutSuccess(t *testing.T) { |
| // Test that ZeroOut does not return an error when all pages are |
| // accessible. |
| const bufLen = 8192 |
| a := make([]byte, bufLen) |
| b := randBuf(bufLen) |
| |
| n, err := ZeroOut(unsafe.Pointer(&b[0]), bufLen) |
| if n != bufLen { |
| t.Errorf("Unexpected copy length, got %v, want %v", n, bufLen) |
| } |
| if err != nil { |
| t.Errorf("Unexpected error: %v", err) |
| } |
| if !bytes.Equal(a, b) { |
| t.Errorf("Buffers are not equal when they should be: %v %v", a, b) |
| } |
| } |
| |
| func TestSwapUint32Success(t *testing.T) { |
| // Test that SwapUint32 does not return an error when the page is |
| // accessible. |
| before := uint32(rand.Int31()) |
| after := uint32(rand.Int31()) |
| val := before |
| |
| old, err := SwapUint32(unsafe.Pointer(&val), after) |
| if err != nil { |
| t.Errorf("Unexpected error: %v", err) |
| } |
| if old != before { |
| t.Errorf("Unexpected old value: got %v, want %v", old, before) |
| } |
| if val != after { |
| t.Errorf("Unexpected new value: got %v, want %v", val, after) |
| } |
| } |
| |
| func TestSwapUint32AlignmentError(t *testing.T) { |
| // Test that SwapUint32 returns an AlignmentError when passed an unaligned |
| // address. |
| data := make([]byte, 8) // 2 * sizeof(uint32). |
| alignedIndex := uintptr(0) |
| if offset := uintptr(unsafe.Pointer(&data[0])) % 4; offset != 0 { |
| alignedIndex = 4 - offset |
| } |
| ptr := unsafe.Pointer(&data[alignedIndex+1]) |
| want := AlignmentError{Addr: uintptr(ptr), Alignment: 4} |
| if _, err := SwapUint32(ptr, 1); err != want { |
| t.Errorf("Unexpected error: got %v, want %v", err, want) |
| } |
| } |
| |
| func TestSwapUint64Success(t *testing.T) { |
| // Test that SwapUint64 does not return an error when the page is |
| // accessible. |
| before := uint64(rand.Int63()) |
| after := uint64(rand.Int63()) |
| // "The first word in ... an allocated struct or slice can be relied upon |
| // to be 64-bit aligned." - sync/atomic docs |
| data := new(struct{ val uint64 }) |
| data.val = before |
| |
| old, err := SwapUint64(unsafe.Pointer(&data.val), after) |
| if err != nil { |
| t.Errorf("Unexpected error: %v", err) |
| } |
| if old != before { |
| t.Errorf("Unexpected old value: got %v, want %v", old, before) |
| } |
| if data.val != after { |
| t.Errorf("Unexpected new value: got %v, want %v", data.val, after) |
| } |
| } |
| |
| func TestSwapUint64AlignmentError(t *testing.T) { |
| // Test that SwapUint64 returns an AlignmentError when passed an unaligned |
| // address. |
| data := make([]byte, 16) // 2 * sizeof(uint64). |
| alignedIndex := uintptr(0) |
| if offset := uintptr(unsafe.Pointer(&data[0])) % 8; offset != 0 { |
| alignedIndex = 8 - offset |
| } |
| ptr := unsafe.Pointer(&data[alignedIndex+1]) |
| want := AlignmentError{Addr: uintptr(ptr), Alignment: 8} |
| if _, err := SwapUint64(ptr, 1); err != want { |
| t.Errorf("Unexpected error: got %v, want %v", err, want) |
| } |
| } |
| |
| func TestCompareAndSwapUint32Success(t *testing.T) { |
| // Test that CompareAndSwapUint32 does not return an error when the page is |
| // accessible. |
| before := uint32(rand.Int31()) |
| after := uint32(rand.Int31()) |
| val := before |
| |
| old, err := CompareAndSwapUint32(unsafe.Pointer(&val), before, after) |
| if err != nil { |
| t.Errorf("Unexpected error: %v", err) |
| } |
| if old != before { |
| t.Errorf("Unexpected old value: got %v, want %v", old, before) |
| } |
| if val != after { |
| t.Errorf("Unexpected new value: got %v, want %v", val, after) |
| } |
| } |
| |
| func TestCompareAndSwapUint32AlignmentError(t *testing.T) { |
| // Test that CompareAndSwapUint32 returns an AlignmentError when passed an |
| // unaligned address. |
| data := make([]byte, 8) // 2 * sizeof(uint32). |
| alignedIndex := uintptr(0) |
| if offset := uintptr(unsafe.Pointer(&data[0])) % 4; offset != 0 { |
| alignedIndex = 4 - offset |
| } |
| ptr := unsafe.Pointer(&data[alignedIndex+1]) |
| want := AlignmentError{Addr: uintptr(ptr), Alignment: 4} |
| if _, err := CompareAndSwapUint32(ptr, 0, 1); err != want { |
| t.Errorf("Unexpected error: got %v, want %v", err, want) |
| } |
| } |
| |
| // withSegvErrorTestMapping calls fn with a two-page mapping. The first page |
| // contains random data, and the second page generates SIGSEGV when accessed. |
| func withSegvErrorTestMapping(t *testing.T, fn func(m []byte)) { |
| mapping, err := syscall.Mmap(-1, 0, 2*pageSize, syscall.PROT_READ|syscall.PROT_WRITE, syscall.MAP_ANONYMOUS|syscall.MAP_PRIVATE) |
| if err != nil { |
| t.Fatalf("Mmap failed: %v", err) |
| } |
| defer syscall.Munmap(mapping) |
| if err := syscall.Mprotect(mapping[pageSize:], syscall.PROT_NONE); err != nil { |
| t.Fatalf("Mprotect failed: %v", err) |
| } |
| initRandom(mapping[:pageSize]) |
| |
| fn(mapping) |
| } |
| |
| // withBusErrorTestMapping calls fn with a two-page mapping. The first page |
| // contains random data, and the second page generates SIGBUS when accessed. |
| func withBusErrorTestMapping(t *testing.T, fn func(m []byte)) { |
| f, err := ioutil.TempFile("", "sigbus_test") |
| if err != nil { |
| t.Fatalf("TempFile failed: %v", err) |
| } |
| defer f.Close() |
| if err := f.Truncate(pageSize); err != nil { |
| t.Fatalf("Truncate failed: %v", err) |
| } |
| mapping, err := syscall.Mmap(int(f.Fd()), 0, 2*pageSize, syscall.PROT_READ|syscall.PROT_WRITE, syscall.MAP_SHARED) |
| if err != nil { |
| t.Fatalf("Mmap failed: %v", err) |
| } |
| defer syscall.Munmap(mapping) |
| initRandom(mapping[:pageSize]) |
| |
| fn(mapping) |
| } |
| |
| func TestCopyInSegvError(t *testing.T) { |
| // Test that CopyIn returns a SegvError when reaching a page that signals |
| // SIGSEGV. |
| for bytesBeforeFault := 0; bytesBeforeFault <= 2*maxRegisterSize; bytesBeforeFault++ { |
| t.Run(fmt.Sprintf("starting copy %d bytes before SIGSEGV", bytesBeforeFault), func(t *testing.T) { |
| withSegvErrorTestMapping(t, func(mapping []byte) { |
| secondPage := uintptr(unsafe.Pointer(&mapping[pageSize])) |
| src := unsafe.Pointer(&mapping[pageSize-bytesBeforeFault]) |
| dst := randBuf(pageSize) |
| n, err := CopyIn(dst, src) |
| if n != bytesBeforeFault { |
| t.Errorf("Unexpected copy length: got %v, want %v", n, bytesBeforeFault) |
| } |
| if want := (SegvError{secondPage}); err != want { |
| t.Errorf("Unexpected error: got %v, want %v", err, want) |
| } |
| if got, want := dst[:bytesBeforeFault], mapping[pageSize-bytesBeforeFault:pageSize]; !bytes.Equal(got, want) { |
| t.Errorf("Buffers are not equal when they should be: %v %v", got, want) |
| } |
| }) |
| }) |
| } |
| } |
| |
| func TestCopyInBusError(t *testing.T) { |
| // Test that CopyIn returns a BusError when reaching a page that signals |
| // SIGBUS. |
| for bytesBeforeFault := 0; bytesBeforeFault <= 2*maxRegisterSize; bytesBeforeFault++ { |
| t.Run(fmt.Sprintf("starting copy %d bytes before SIGBUS", bytesBeforeFault), func(t *testing.T) { |
| withBusErrorTestMapping(t, func(mapping []byte) { |
| secondPage := uintptr(unsafe.Pointer(&mapping[pageSize])) |
| src := unsafe.Pointer(&mapping[pageSize-bytesBeforeFault]) |
| dst := randBuf(pageSize) |
| n, err := CopyIn(dst, src) |
| if n != bytesBeforeFault { |
| t.Errorf("Unexpected copy length: got %v, want %v", n, bytesBeforeFault) |
| } |
| if want := (BusError{secondPage}); err != want { |
| t.Errorf("Unexpected error: got %v, want %v", err, want) |
| } |
| if got, want := dst[:bytesBeforeFault], mapping[pageSize-bytesBeforeFault:pageSize]; !bytes.Equal(got, want) { |
| t.Errorf("Buffers are not equal when they should be: %v %v", got, want) |
| } |
| }) |
| }) |
| } |
| } |
| |
| func TestCopyOutSegvError(t *testing.T) { |
| // Test that CopyOut returns a SegvError when reaching a page that signals |
| // SIGSEGV. |
| for bytesBeforeFault := 0; bytesBeforeFault <= 2*maxRegisterSize; bytesBeforeFault++ { |
| t.Run(fmt.Sprintf("starting copy %d bytes before SIGSEGV", bytesBeforeFault), func(t *testing.T) { |
| withSegvErrorTestMapping(t, func(mapping []byte) { |
| secondPage := uintptr(unsafe.Pointer(&mapping[pageSize])) |
| dst := unsafe.Pointer(&mapping[pageSize-bytesBeforeFault]) |
| src := randBuf(pageSize) |
| n, err := CopyOut(dst, src) |
| if n != bytesBeforeFault { |
| t.Errorf("Unexpected copy length: got %v, want %v", n, bytesBeforeFault) |
| } |
| if want := (SegvError{secondPage}); err != want { |
| t.Errorf("Unexpected error: got %v, want %v", err, want) |
| } |
| if got, want := mapping[pageSize-bytesBeforeFault:pageSize], src[:bytesBeforeFault]; !bytes.Equal(got, want) { |
| t.Errorf("Buffers are not equal when they should be: %v %v", got, want) |
| } |
| }) |
| }) |
| } |
| } |
| |
| func TestCopyOutBusError(t *testing.T) { |
| // Test that CopyOut returns a BusError when reaching a page that signals |
| // SIGBUS. |
| for bytesBeforeFault := 0; bytesBeforeFault <= 2*maxRegisterSize; bytesBeforeFault++ { |
| t.Run(fmt.Sprintf("starting copy %d bytes before SIGSEGV", bytesBeforeFault), func(t *testing.T) { |
| withBusErrorTestMapping(t, func(mapping []byte) { |
| secondPage := uintptr(unsafe.Pointer(&mapping[pageSize])) |
| dst := unsafe.Pointer(&mapping[pageSize-bytesBeforeFault]) |
| src := randBuf(pageSize) |
| n, err := CopyOut(dst, src) |
| if n != bytesBeforeFault { |
| t.Errorf("Unexpected copy length: got %v, want %v", n, bytesBeforeFault) |
| } |
| if want := (BusError{secondPage}); err != want { |
| t.Errorf("Unexpected error: got %v, want %v", err, want) |
| } |
| if got, want := mapping[pageSize-bytesBeforeFault:pageSize], src[:bytesBeforeFault]; !bytes.Equal(got, want) { |
| t.Errorf("Buffers are not equal when they should be: %v %v", got, want) |
| } |
| }) |
| }) |
| } |
| } |
| |
| func TestCopySourceSegvError(t *testing.T) { |
| // Test that Copy returns a SegvError when copying from a page that signals |
| // SIGSEGV. |
| for bytesBeforeFault := 0; bytesBeforeFault <= 2*maxRegisterSize; bytesBeforeFault++ { |
| t.Run(fmt.Sprintf("starting copy %d bytes before SIGSEGV", bytesBeforeFault), func(t *testing.T) { |
| withSegvErrorTestMapping(t, func(mapping []byte) { |
| secondPage := uintptr(unsafe.Pointer(&mapping[pageSize])) |
| src := unsafe.Pointer(&mapping[pageSize-bytesBeforeFault]) |
| dst := randBuf(pageSize) |
| n, err := Copy(unsafe.Pointer(&dst[0]), src, pageSize) |
| if n != uintptr(bytesBeforeFault) { |
| t.Errorf("Unexpected copy length: got %v, want %v", n, bytesBeforeFault) |
| } |
| if want := (SegvError{secondPage}); err != want { |
| t.Errorf("Unexpected error: got %v, want %v", err, want) |
| } |
| if got, want := dst[:bytesBeforeFault], mapping[pageSize-bytesBeforeFault:pageSize]; !bytes.Equal(got, want) { |
| t.Errorf("Buffers are not equal when they should be: %v %v", got, want) |
| } |
| }) |
| }) |
| } |
| } |
| |
| func TestCopySourceBusError(t *testing.T) { |
| // Test that Copy returns a BusError when copying from a page that signals |
| // SIGBUS. |
| for bytesBeforeFault := 0; bytesBeforeFault <= 2*maxRegisterSize; bytesBeforeFault++ { |
| t.Run(fmt.Sprintf("starting copy %d bytes before SIGBUS", bytesBeforeFault), func(t *testing.T) { |
| withBusErrorTestMapping(t, func(mapping []byte) { |
| secondPage := uintptr(unsafe.Pointer(&mapping[pageSize])) |
| src := unsafe.Pointer(&mapping[pageSize-bytesBeforeFault]) |
| dst := randBuf(pageSize) |
| n, err := Copy(unsafe.Pointer(&dst[0]), src, pageSize) |
| if n != uintptr(bytesBeforeFault) { |
| t.Errorf("Unexpected copy length: got %v, want %v", n, bytesBeforeFault) |
| } |
| if want := (BusError{secondPage}); err != want { |
| t.Errorf("Unexpected error: got %v, want %v", err, want) |
| } |
| if got, want := dst[:bytesBeforeFault], mapping[pageSize-bytesBeforeFault:pageSize]; !bytes.Equal(got, want) { |
| t.Errorf("Buffers are not equal when they should be: %v %v", got, want) |
| } |
| }) |
| }) |
| } |
| } |
| |
| func TestCopyDestinationSegvError(t *testing.T) { |
| // Test that Copy returns a SegvError when copying to a page that signals |
| // SIGSEGV. |
| for bytesBeforeFault := 0; bytesBeforeFault <= 2*maxRegisterSize; bytesBeforeFault++ { |
| t.Run(fmt.Sprintf("starting copy %d bytes before SIGSEGV", bytesBeforeFault), func(t *testing.T) { |
| withSegvErrorTestMapping(t, func(mapping []byte) { |
| secondPage := uintptr(unsafe.Pointer(&mapping[pageSize])) |
| dst := unsafe.Pointer(&mapping[pageSize-bytesBeforeFault]) |
| src := randBuf(pageSize) |
| n, err := Copy(dst, unsafe.Pointer(&src[0]), pageSize) |
| if n != uintptr(bytesBeforeFault) { |
| t.Errorf("Unexpected copy length: got %v, want %v", n, bytesBeforeFault) |
| } |
| if want := (SegvError{secondPage}); err != want { |
| t.Errorf("Unexpected error: got %v, want %v", err, want) |
| } |
| if got, want := mapping[pageSize-bytesBeforeFault:pageSize], src[:bytesBeforeFault]; !bytes.Equal(got, want) { |
| t.Errorf("Buffers are not equal when they should be: %v %v", got, want) |
| } |
| }) |
| }) |
| } |
| } |
| |
| func TestCopyDestinationBusError(t *testing.T) { |
| // Test that Copy returns a BusError when copying to a page that signals |
| // SIGBUS. |
| for bytesBeforeFault := 0; bytesBeforeFault <= 2*maxRegisterSize; bytesBeforeFault++ { |
| t.Run(fmt.Sprintf("starting copy %d bytes before SIGBUS", bytesBeforeFault), func(t *testing.T) { |
| withBusErrorTestMapping(t, func(mapping []byte) { |
| secondPage := uintptr(unsafe.Pointer(&mapping[pageSize])) |
| dst := unsafe.Pointer(&mapping[pageSize-bytesBeforeFault]) |
| src := randBuf(pageSize) |
| n, err := Copy(dst, unsafe.Pointer(&src[0]), pageSize) |
| if n != uintptr(bytesBeforeFault) { |
| t.Errorf("Unexpected copy length: got %v, want %v", n, bytesBeforeFault) |
| } |
| if want := (BusError{secondPage}); err != want { |
| t.Errorf("Unexpected error: got %v, want %v", err, want) |
| } |
| if got, want := mapping[pageSize-bytesBeforeFault:pageSize], src[:bytesBeforeFault]; !bytes.Equal(got, want) { |
| t.Errorf("Buffers are not equal when they should be: %v %v", got, want) |
| } |
| }) |
| }) |
| } |
| } |
| |
| func TestZeroOutSegvError(t *testing.T) { |
| // Test that ZeroOut returns a SegvError when reaching a page that signals |
| // SIGSEGV. |
| for bytesBeforeFault := 0; bytesBeforeFault <= 2*maxRegisterSize; bytesBeforeFault++ { |
| t.Run(fmt.Sprintf("starting write %d bytes before SIGSEGV", bytesBeforeFault), func(t *testing.T) { |
| withSegvErrorTestMapping(t, func(mapping []byte) { |
| secondPage := uintptr(unsafe.Pointer(&mapping[pageSize])) |
| dst := unsafe.Pointer(&mapping[pageSize-bytesBeforeFault]) |
| n, err := ZeroOut(dst, pageSize) |
| if n != uintptr(bytesBeforeFault) { |
| t.Errorf("Unexpected write length: got %v, want %v", n, bytesBeforeFault) |
| } |
| if want := (SegvError{secondPage}); err != want { |
| t.Errorf("Unexpected error: got %v, want %v", err, want) |
| } |
| if got, want := mapping[pageSize-bytesBeforeFault:pageSize], make([]byte, bytesBeforeFault); !bytes.Equal(got, want) { |
| t.Errorf("Non-zero bytes in written part of mapping: %v", got) |
| } |
| }) |
| }) |
| } |
| } |
| |
| func TestZeroOutBusError(t *testing.T) { |
| // Test that ZeroOut returns a BusError when reaching a page that signals |
| // SIGBUS. |
| for bytesBeforeFault := 0; bytesBeforeFault <= 2*maxRegisterSize; bytesBeforeFault++ { |
| t.Run(fmt.Sprintf("starting write %d bytes before SIGBUS", bytesBeforeFault), func(t *testing.T) { |
| withBusErrorTestMapping(t, func(mapping []byte) { |
| secondPage := uintptr(unsafe.Pointer(&mapping[pageSize])) |
| dst := unsafe.Pointer(&mapping[pageSize-bytesBeforeFault]) |
| n, err := ZeroOut(dst, pageSize) |
| if n != uintptr(bytesBeforeFault) { |
| t.Errorf("Unexpected write length: got %v, want %v", n, bytesBeforeFault) |
| } |
| if want := (BusError{secondPage}); err != want { |
| t.Errorf("Unexpected error: got %v, want %v", err, want) |
| } |
| if got, want := mapping[pageSize-bytesBeforeFault:pageSize], make([]byte, bytesBeforeFault); !bytes.Equal(got, want) { |
| t.Errorf("Non-zero bytes in written part of mapping: %v", got) |
| } |
| }) |
| }) |
| } |
| } |
| |
| func TestSwapUint32SegvError(t *testing.T) { |
| // Test that SwapUint32 returns a SegvError when reaching a page that |
| // signals SIGSEGV. |
| withSegvErrorTestMapping(t, func(mapping []byte) { |
| secondPage := uintptr(unsafe.Pointer(&mapping[pageSize])) |
| _, err := SwapUint32(unsafe.Pointer(secondPage), 1) |
| if want := (SegvError{secondPage}); err != want { |
| t.Errorf("Unexpected error: got %v, want %v", err, want) |
| } |
| }) |
| } |
| |
| func TestSwapUint32BusError(t *testing.T) { |
| // Test that SwapUint32 returns a BusError when reaching a page that |
| // signals SIGBUS. |
| withBusErrorTestMapping(t, func(mapping []byte) { |
| secondPage := uintptr(unsafe.Pointer(&mapping[pageSize])) |
| _, err := SwapUint32(unsafe.Pointer(secondPage), 1) |
| if want := (BusError{secondPage}); err != want { |
| t.Errorf("Unexpected error: got %v, want %v", err, want) |
| } |
| }) |
| } |
| |
| func TestSwapUint64SegvError(t *testing.T) { |
| // Test that SwapUint64 returns a SegvError when reaching a page that |
| // signals SIGSEGV. |
| withSegvErrorTestMapping(t, func(mapping []byte) { |
| secondPage := uintptr(unsafe.Pointer(&mapping[pageSize])) |
| _, err := SwapUint64(unsafe.Pointer(secondPage), 1) |
| if want := (SegvError{secondPage}); err != want { |
| t.Errorf("Unexpected error: got %v, want %v", err, want) |
| } |
| }) |
| } |
| |
| func TestSwapUint64BusError(t *testing.T) { |
| // Test that SwapUint64 returns a BusError when reaching a page that |
| // signals SIGBUS. |
| withBusErrorTestMapping(t, func(mapping []byte) { |
| secondPage := uintptr(unsafe.Pointer(&mapping[pageSize])) |
| _, err := SwapUint64(unsafe.Pointer(secondPage), 1) |
| if want := (BusError{secondPage}); err != want { |
| t.Errorf("Unexpected error: got %v, want %v", err, want) |
| } |
| }) |
| } |
| |
| func TestCompareAndSwapUint32SegvError(t *testing.T) { |
| // Test that CompareAndSwapUint32 returns a SegvError when reaching a page |
| // that signals SIGSEGV. |
| withSegvErrorTestMapping(t, func(mapping []byte) { |
| secondPage := uintptr(unsafe.Pointer(&mapping[pageSize])) |
| _, err := CompareAndSwapUint32(unsafe.Pointer(secondPage), 0, 1) |
| if want := (SegvError{secondPage}); err != want { |
| t.Errorf("Unexpected error: got %v, want %v", err, want) |
| } |
| }) |
| } |
| |
| func TestCompareAndSwapUint32BusError(t *testing.T) { |
| // Test that CompareAndSwapUint32 returns a BusError when reaching a page |
| // that signals SIGBUS. |
| withBusErrorTestMapping(t, func(mapping []byte) { |
| secondPage := uintptr(unsafe.Pointer(&mapping[pageSize])) |
| _, err := CompareAndSwapUint32(unsafe.Pointer(secondPage), 0, 1) |
| if want := (BusError{secondPage}); err != want { |
| t.Errorf("Unexpected error: got %v, want %v", err, want) |
| } |
| }) |
| } |
| |
| func testCopy(dst, src []byte) (panicked bool) { |
| defer func() { |
| if r := recover(); r != nil { |
| panicked = true |
| } |
| }() |
| debug.SetPanicOnFault(true) |
| copy(dst, src) |
| return |
| } |
| |
| func TestSegVOnMemmove(t *testing.T) { |
| // Test that SIGSEGVs received by runtime.memmove when *not* doing |
| // CopyIn or CopyOut work gets propagated to the runtime. |
| const bufLen = pageSize |
| a, err := syscall.Mmap(-1, 0, bufLen, syscall.PROT_NONE, syscall.MAP_ANON|syscall.MAP_PRIVATE) |
| if err != nil { |
| t.Fatalf("Mmap failed: %v", err) |
| |
| } |
| defer syscall.Munmap(a) |
| b := randBuf(bufLen) |
| |
| if !testCopy(b, a) { |
| t.Fatalf("testCopy didn't panic when it should have") |
| } |
| |
| if !testCopy(a, b) { |
| t.Fatalf("testCopy didn't panic when it should have") |
| } |
| } |
| |
| func TestSigbusOnMemmove(t *testing.T) { |
| // Test that SIGBUS received by runtime.memmove when *not* doing |
| // CopyIn or CopyOut work gets propagated to the runtime. |
| const bufLen = pageSize |
| f, err := ioutil.TempFile("", "sigbus_test") |
| if err != nil { |
| t.Fatalf("TempFile failed: %v", err) |
| } |
| os.Remove(f.Name()) |
| defer f.Close() |
| |
| a, err := syscall.Mmap(int(f.Fd()), 0, bufLen, syscall.PROT_READ|syscall.PROT_WRITE, syscall.MAP_SHARED) |
| if err != nil { |
| t.Fatalf("Mmap failed: %v", err) |
| |
| } |
| defer syscall.Munmap(a) |
| b := randBuf(bufLen) |
| |
| if !testCopy(b, a) { |
| t.Fatalf("testCopy didn't panic when it should have") |
| } |
| |
| if !testCopy(a, b) { |
| t.Fatalf("testCopy didn't panic when it should have") |
| } |
| } |