]> Cypherpunks.ru repositories - gostls13.git/commitdiff
hash/crc32: fix race between lazy Castagnoli init and Update/Write
authorRuss Cox <rsc@golang.org>
Mon, 12 Oct 2020 19:41:14 +0000 (15:41 -0400)
committerRuss Cox <rsc@golang.org>
Tue, 13 Oct 2020 00:31:47 +0000 (00:31 +0000)
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>
src/hash/crc32/crc32.go
src/hash/crc32/crc32_test.go

index 908b84adcb290b95be2f070775b01eaffb17c33b..f330fdb77a5272d8cd5dc77750649b79b0239977 100644 (file)
@@ -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)
index 4bdafaf8f522f7bfe4443cdc95b3e3ffcc006992..cbb869dfd641db9a4f912d010da2876092135427 100644 (file)
@@ -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