mirror of
https://github.com/golang/go
synced 2024-11-17 12:04:43 -07:00
hash/crc32: fix race between lazy Castagnoli init and Update/Write
The switch on tab is checking tab == castagnoliTable, but castagnoliTable can change value during a concurrent call to MakeTable. Fixes #41911. Change-Id: I6124dcdbf33e17fe302baa3e1aa03202dec61b4c Reviewed-on: https://go-review.googlesource.com/c/go/+/261639 Trust: Russ Cox <rsc@golang.org> Run-TryBot: Russ Cox <rsc@golang.org> TryBot-Result: Go Bot <gobot@golang.org> Reviewed-by: Ian Lance Taylor <iant@golang.org>
This commit is contained in:
parent
7bda6154ca
commit
5ef78c4d84
@ -16,6 +16,7 @@ import (
|
||||
"errors"
|
||||
"hash"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
)
|
||||
|
||||
// The size of a CRC-32 checksum in bytes.
|
||||
@ -78,6 +79,7 @@ var castagnoliTable8 *slicing8Table
|
||||
var castagnoliArchImpl bool
|
||||
var updateCastagnoli func(crc uint32, p []byte) uint32
|
||||
var castagnoliOnce sync.Once
|
||||
var haveCastagnoli uint32
|
||||
|
||||
func castagnoliInit() {
|
||||
castagnoliTable = simpleMakeTable(Castagnoli)
|
||||
@ -93,6 +95,8 @@ func castagnoliInit() {
|
||||
return slicingUpdate(crc, castagnoliTable8, p)
|
||||
}
|
||||
}
|
||||
|
||||
atomic.StoreUint32(&haveCastagnoli, 1)
|
||||
}
|
||||
|
||||
// IEEETable is the table for the IEEE polynomial.
|
||||
@ -208,10 +212,10 @@ func readUint32(b []byte) uint32 {
|
||||
|
||||
// Update returns the result of adding the bytes in p to the crc.
|
||||
func Update(crc uint32, tab *Table, p []byte) uint32 {
|
||||
switch tab {
|
||||
case castagnoliTable:
|
||||
switch {
|
||||
case atomic.LoadUint32(&haveCastagnoli) != 0 && tab == castagnoliTable:
|
||||
return updateCastagnoli(crc, p)
|
||||
case IEEETable:
|
||||
case tab == IEEETable:
|
||||
// Unfortunately, because IEEETable is exported, IEEE may be used without a
|
||||
// call to MakeTable. We have to make sure it gets initialized in that case.
|
||||
ieeeOnce.Do(ieeeInit)
|
||||
@ -222,10 +226,10 @@ func Update(crc uint32, tab *Table, p []byte) uint32 {
|
||||
}
|
||||
|
||||
func (d *digest) Write(p []byte) (n int, err error) {
|
||||
switch d.tab {
|
||||
case castagnoliTable:
|
||||
switch {
|
||||
case atomic.LoadUint32(&haveCastagnoli) != 0 && d.tab == castagnoliTable:
|
||||
d.crc = updateCastagnoli(d.crc, p)
|
||||
case IEEETable:
|
||||
case d.tab == IEEETable:
|
||||
// We only create digest objects through New() which takes care of
|
||||
// initialization in this case.
|
||||
d.crc = updateIEEE(d.crc, p)
|
||||
|
@ -13,6 +13,16 @@ import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
// First test, so that it can be the one to initialize castagnoliTable.
|
||||
func TestCastagnoliRace(t *testing.T) {
|
||||
// The MakeTable(Castagnoli) lazily initializes castagnoliTable,
|
||||
// which races with the switch on tab during Write to check
|
||||
// whether tab == castagnoliTable.
|
||||
ieee := NewIEEE()
|
||||
go MakeTable(Castagnoli)
|
||||
ieee.Write([]byte("hello"))
|
||||
}
|
||||
|
||||
type test struct {
|
||||
ieee, castagnoli uint32
|
||||
in string
|
||||
|
Loading…
Reference in New Issue
Block a user