1// run
2
3//go:build amd64 && (linux || darwin)
4
5// Copyright 2016 The Go Authors. All rights reserved.
6// Use of this source code is governed by a BSD-style
7// license that can be found in the LICENSE file.
8
9package main
10
11import (
12	"fmt"
13	"syscall"
14)
15
16// Use global variables so the compiler
17// doesn't know that they are constants.
18var p = syscall.Getpagesize()
19var zero = 0
20var one = 1
21
22func main() {
23	// Allocate 2 pages of memory.
24	b, err := syscall.Mmap(-1, 0, 2*p, syscall.PROT_READ|syscall.PROT_WRITE, syscall.MAP_ANON|syscall.MAP_PRIVATE)
25	if err != nil {
26		panic(err)
27	}
28	// Mark the second page as faulting.
29	err = syscall.Mprotect(b[p:], syscall.PROT_NONE)
30	if err != nil {
31		panic(err)
32	}
33	// Get a slice pointing to the last byte of the good page.
34	x := b[p-one : p]
35
36	test16(x)
37	test16i(x, 0)
38	test32(x)
39	test32i(x, 0)
40	test64(x)
41	test64i(x, 0)
42}
43
44func test16(x []byte) uint16 {
45	defer func() {
46		r := recover()
47		if r == nil {
48			panic("no fault or bounds check failure happened")
49		}
50		s := fmt.Sprintf("%s", r)
51		if s != "runtime error: index out of range [1] with length 1" {
52			panic("bad panic: " + s)
53		}
54	}()
55	// Try to read 2 bytes from x.
56	return uint16(x[0]) | uint16(x[1])<<8
57
58	// We expect to get an "index out of range" error from x[1].
59	// If we promote the first load to a 2-byte load, it will segfault, which we don't want.
60}
61
62func test16i(x []byte, i int) uint16 {
63	defer func() {
64		r := recover()
65		if r == nil {
66			panic("no fault or bounds check failure happened")
67		}
68		s := fmt.Sprintf("%s", r)
69		if s != "runtime error: index out of range [1] with length 1" {
70			panic("bad panic: " + s)
71		}
72	}()
73	return uint16(x[i]) | uint16(x[i+1])<<8
74}
75
76func test32(x []byte) uint32 {
77	defer func() {
78		r := recover()
79		if r == nil {
80			panic("no fault or bounds check failure happened")
81		}
82		s := fmt.Sprintf("%s", r)
83		if s != "runtime error: index out of range [1] with length 1" {
84			panic("bad panic: " + s)
85		}
86	}()
87	return uint32(x[0]) | uint32(x[1])<<8 | uint32(x[2])<<16 | uint32(x[3])<<24
88}
89
90func test32i(x []byte, i int) uint32 {
91	defer func() {
92		r := recover()
93		if r == nil {
94			panic("no fault or bounds check failure happened")
95		}
96		s := fmt.Sprintf("%s", r)
97		if s != "runtime error: index out of range [1] with length 1" {
98			panic("bad panic: " + s)
99		}
100	}()
101	return uint32(x[i]) | uint32(x[i+1])<<8 | uint32(x[i+2])<<16 | uint32(x[i+3])<<24
102}
103
104func test64(x []byte) uint64 {
105	defer func() {
106		r := recover()
107		if r == nil {
108			panic("no fault or bounds check failure happened")
109		}
110		s := fmt.Sprintf("%s", r)
111		if s != "runtime error: index out of range [1] with length 1" {
112			panic("bad panic: " + s)
113		}
114	}()
115	return uint64(x[0]) | uint64(x[1])<<8 | uint64(x[2])<<16 | uint64(x[3])<<24 |
116		uint64(x[4])<<32 | uint64(x[5])<<40 | uint64(x[6])<<48 | uint64(x[7])<<56
117}
118
119func test64i(x []byte, i int) uint64 {
120	defer func() {
121		r := recover()
122		if r == nil {
123			panic("no fault or bounds check failure happened")
124		}
125		s := fmt.Sprintf("%s", r)
126		if s != "runtime error: index out of range [1] with length 1" {
127			panic("bad panic: " + s)
128		}
129	}()
130	return uint64(x[i+0]) | uint64(x[i+1])<<8 | uint64(x[i+2])<<16 | uint64(x[i+3])<<24 |
131		uint64(x[i+4])<<32 | uint64(x[i+5])<<40 | uint64(x[i+6])<<48 | uint64(x[i+7])<<56
132}
133