diff options
author | Russ Cox <rsc@golang.org> | 2020-10-12 15:41:14 -0400 |
---|---|---|
committer | Russ Cox <rsc@golang.org> | 2020-10-13 00:31:47 +0000 |
commit | 5ef78c4d84620f6594ad63cbd1b3b43f41670636 (patch) | |
tree | 49e4184d9a4e630369d036e8d3880e75da4cd7d8 /src/hash | |
parent | 7bda6154caa6f0c527f4a8302e38d450b44ae68b (diff) | |
download | go-5ef78c4d84620f6594ad63cbd1b3b43f41670636.tar.gz go-5ef78c4d84620f6594ad63cbd1b3b43f41670636.zip |
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>
Diffstat (limited to 'src/hash')
-rw-r--r-- | src/hash/crc32/crc32.go | 16 | ||||
-rw-r--r-- | src/hash/crc32/crc32_test.go | 10 |
2 files changed, 20 insertions, 6 deletions
diff --git a/src/hash/crc32/crc32.go b/src/hash/crc32/crc32.go index 908b84adcb..f330fdb77a 100644 --- a/src/hash/crc32/crc32.go +++ b/src/hash/crc32/crc32.go @@ -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) diff --git a/src/hash/crc32/crc32_test.go b/src/hash/crc32/crc32_test.go index 4bdafaf8f5..cbb869dfd6 100644 --- a/src/hash/crc32/crc32_test.go +++ b/src/hash/crc32/crc32_test.go @@ -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 |