Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions cl/persistence/base_encoding/rabbit.go
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ func ReadRabbits(out []uint64, r io.Reader) ([]uint64, error) {
if err != nil {
return nil, err
}
// overflow_false_positive
if current+count < current {
return nil, fmt.Errorf("rabbit: index overflow at current=%d count=%d", current, count)
}
Expand Down
1 change: 1 addition & 0 deletions cl/phase1/core/state/cache.go
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,7 @@ func (b *CachingBeaconState) _refreshActiveBalancesIfNeeded() {
*b.totalActiveBalanceCache = 0

// Check global cache using block root at beginning of previous epoch
// overflow_false_positive
blockRootAtBegginingPrevEpoch, err := b.GetBlockRootAtSlot(((epoch - 1) * b.BeaconConfig().SlotsPerEpoch) - 1)
if err == nil {
if _, cachedBalance, ok := caches.ActiveValidatorsCacheGlobal.Get(epoch, blockRootAtBegginingPrevEpoch); ok && cachedBalance != 0 {
Expand Down
1 change: 1 addition & 0 deletions cl/phase1/core/state/cache_accessors.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ func (b *CachingBeaconState) GetActiveValidatorsIndices(epoch uint64) []uint64 {
}

// Check global cache using block root at beginning of previous epoch
// overflow_false_positive
blockRootAtBegginingPrevEpoch, err := b.GetBlockRootAtSlot(((epoch - 1) * b.BeaconConfig().SlotsPerEpoch) - 1)
if err == nil {
if cachedIndicies, _, ok := caches.ActiveValidatorsCacheGlobal.Get(epoch, blockRootAtBegginingPrevEpoch); ok && len(cachedIndicies) > 0 {
Expand Down
1 change: 1 addition & 0 deletions cl/phase1/network/backward_beacon_downloader.go
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,7 @@ func (b *BackwardBeaconDownloader) RequestMore(ctx context.Context) error {
// Falls back to the beacon API when P2P is unavailable and an HTTP URL is configured.
func (b *BackwardBeaconDownloader) fetchBlockRange(ctx context.Context) ([]*cltypes.SignedBeaconBlock, error) {
const count = uint64(64)
// overflow_false_positive
start := b.slotToDownload.Load() - count + 1
if start > b.slotToDownload.Load() { // overflow check
start = 0
Expand Down
27 changes: 21 additions & 6 deletions common/bitutil/select.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,21 @@ const (
*
*/

// wmul, wadd and wsub are this package's deliberately modular arithmetic. The
// wraparound is load-bearing, so it is expressed here once rather than at each
// use — the same operations Rust spells wrapping_mul/wrapping_add/wrapping_sub.
// Package-level -overflowdetect exemption does not cover these, because they
// are inlined into instrumented callers.

// overflow_false_positive
func wmul(a, b uint64) uint64 { return a * b }

// overflow_false_positive
func wadd(a, b uint64) uint64 { return a + b }

// overflow_false_positive
func wsub(a, b uint64) uint64 { return a - b }

func Select64(x uint64, k int) (place int) {
/* Original implementation - a bit obfuscated to satisfy Golang's inlining costs
s := x
Expand All @@ -91,16 +106,16 @@ func Select64(x uint64, k int) (place int) {
s = (s + (s >> 4)) & (0xF * kOnesStep8)
byteSums := s * kOnesStep8
*/
s := x - ((x & kOnesStep4xA) >> 1)
s = (s & kOnesStep4x3) + ((s >> 2) & kOnesStep4x3)
byteSums := ((s + (s >> 4)) & kOnesStep8xF) * kOnesStep8
s := wsub(x, (x&kOnesStep4xA)>>1)
s = wadd(s&kOnesStep4x3, (s>>2)&kOnesStep4x3)
byteSums := wmul(wadd(s, s>>4)&kOnesStep8xF, kOnesStep8)
/* Original implementation:
kStep8 := uint64(k) * kOnesStep8
geqKStep8 := ((kStep8 | kLAMBDAsStep8) - byteSums) & kLAMBDAsStep8
place = bits.OnesCount64(geqKStep8) * 8
byteRank := uint64(k) - (((byteSums << 8) >> place) & uint64(0xFF))
byteRank := wsub(uint64(k), ((byteSums<<8)>>place)&uint64(0xFF))
*/
place = bits.OnesCount64((((uint64(k)*kOnesStep8)|kLAMBDAsStep8)-byteSums)&kLAMBDAsStep8) * 8
byteRank := uint64(k) - (((byteSums << 8) >> place) & uint64(0xFF))
place = bits.OnesCount64(wsub(wmul(uint64(k), kOnesStep8)|kLAMBDAsStep8, byteSums)&kLAMBDAsStep8) * 8
byteRank := wsub(uint64(k), ((byteSums<<8)>>place)&uint64(0xFF))
return place + int(kSelectInByte[((x>>place)&0xFF)|(byteRank<<8)])
}
1 change: 1 addition & 0 deletions db/kv/kvcache/cache.go
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,7 @@ func (c *Coherent) advanceRoot(stateVersionID uint64) (r *CoherentRoot) {
c.roots[stateVersionID] = r
}

// overflow_false_positive
if prevView, ok := c.roots[stateVersionID-1]; ok && prevView.isCanonical {
//log.Info("advance: clone", "from", viewID-1, "to", viewID)
r.cache = prevView.cache.Copy()
Expand Down
1 change: 1 addition & 0 deletions db/recsplit/eliasfano32/elias_fano.go
Original file line number Diff line number Diff line change
Expand Up @@ -736,6 +736,7 @@ func (efi *EliasFanoIter) decrement() {

// note: there can be an underflow here after the last Next()
// but that is ok since we are protected from ErrEliasFanoIterExhausted
// overflow_false_positive
efi.lowerIdx -= efi.l
efi.upperMask >>= 1
}
Expand Down
13 changes: 11 additions & 2 deletions db/recsplit/recsplit.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,9 +82,18 @@ func newExistenceFilterWriter(filePath string, v version.DataStructureVersion) (
* @return a 64-bit integer obtained by mixing the bits of `z`.
*/

// wmul, wadd and wsub are this package's deliberately modular arithmetic. The
// wraparound is load-bearing, so it is expressed here once rather than at each
// use — the same operations Rust spells wrapping_mul/wrapping_add/wrapping_sub.
// Package-level -overflowdetect exemption does not cover these, because they
// are inlined into instrumented callers.

// overflow_false_positive
func wmul(a, b uint64) uint64 { return a * b }

func remix(z uint64) uint64 {
z = (z ^ (z >> 30)) * 0xbf58476d1ce4e5b9
z = (z ^ (z >> 27)) * 0x94d049bb133111eb
z = wmul(z^(z>>30), 0xbf58476d1ce4e5b9)
z = wmul(z^(z>>27), 0x94d049bb133111eb)
return z ^ (z >> 31)
}

Expand Down
16 changes: 14 additions & 2 deletions db/seg/patricia/aho_corasick.go
Original file line number Diff line number Diff line change
Expand Up @@ -295,11 +295,23 @@ const (
// predictable iterations instead of one data-dependent branch per label.
// labels carries eight bytes of tail padding to keep the last word in bounds;
// a hit in that padding, or in the next state's labels, lands at k >= hi.
// wmul, wadd and wsub are this package's deliberately modular arithmetic. The
// wraparound is load-bearing, so it is expressed here once rather than at each
// use — the same operations Rust spells wrapping_mul/wrapping_add/wrapping_sub.
// Package-level -overflowdetect exemption does not cover these, because they
// are inlined into instrumented callers.

// overflow_false_positive
func wmul(a, b uint64) uint64 { return a * b }

// overflow_false_positive
func wsub(a, b uint64) uint64 { return a - b }

func swarEdge(labels []byte, children []int32, lo, hi int32, b byte) int32 {
bcast := uint64(b) * swarOnes
bcast := wmul(uint64(b), swarOnes)
for i := lo; i < hi; i += 8 {
v := binary.LittleEndian.Uint64(labels[i:]) ^ bcast
if z := (v - swarOnes) &^ v & swarHighs; z != 0 {
if z := wsub(v, swarOnes) &^ v & swarHighs; z != 0 {
// borrows only propagate up, so the lowest flagged byte is a real hit
if k := i + int32(bits.TrailingZeros64(z)>>3); k < hi {
return children[k]
Expand Down
1 change: 1 addition & 0 deletions execution/commitment/prefix_trie.go
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ func popcount(n *prefixNode) int {

func childIndex(n *prefixNode, nib byte) (int, bool) {
mask := uint16(1) << nib
// overflow_false_positive
idx := bits.OnesCount16(n.bitmap & (mask - 1))
return idx, n.bitmap&mask != 0
}
Expand Down
1 change: 1 addition & 0 deletions execution/protocol/evm.go
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@ func NewEVMTxContext(msg Message) evmtypes.TxContext {

// GetHashFn returns a GetHashFunc which retrieves header hashes by number
func GetHashFn(ref *types.Header, getHeader func(hash common.Hash, number uint64) (*types.Header, error)) func(n uint64) (common.Hash, error) {
// overflow_false_positive
refNumber := ref.Number.Uint64() - 1
refHash := ref.ParentHash
lastKnownNumber := refNumber
Expand Down
1 change: 1 addition & 0 deletions execution/protocol/txn_executor.go
Original file line number Diff line number Diff line change
Expand Up @@ -304,6 +304,7 @@ func (st *TxnExecutor) preCheck(gasBailout bool, intrinsicGasResult mdgas.Intrin
return upfrontTxnFees{}, &nonceError{err: ErrNonceTooHigh, from: from, txNonce: msgNonce, stateNonce: stNonce}
} else if stNonce > msgNonce {
return upfrontTxnFees{}, &nonceError{err: ErrNonceTooLow, from: from, txNonce: msgNonce, stateNonce: stNonce}
// overflow_false_positive
} else if stNonce+1 < stNonce {
return upfrontTxnFees{}, fmt.Errorf("%w: address %v, nonce: %d", ErrNonceMax,
from, stNonce)
Expand Down
1 change: 1 addition & 0 deletions execution/stagedsync/stage_senders.go
Original file line number Diff line number Diff line change
Expand Up @@ -332,6 +332,7 @@ Loop:
if err := s.Update(tx, to); err != nil {
return err
}
// overflow_false_positive
log.Debug(fmt.Sprintf("[%s] Recovery done", logPrefix), "from", startFrom, "to", to, "blocks", to-startFrom+1, "took", time.Since(recoveryStart))
}
return nil
Expand Down
1 change: 1 addition & 0 deletions execution/types/transaction_signing.go
Original file line number Diff line number Diff line change
Expand Up @@ -418,6 +418,7 @@ func recoverPlain(context *secp256k1.Context, sighash common.Hash, r, s, vb *uin
if vb.BitLen() > 8 {
return accounts.NilAddress, ErrInvalidSig
}
// overflow_false_positive
vByte := byte(vb.Uint64() - 27)
if !crypto.TransactionSignatureIsValid(vByte, r, s, !homestead) {
return accounts.NilAddress, ErrInvalidSig
Expand Down
1 change: 1 addition & 0 deletions execution/vm/analysis.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ func codeBitmap(code []byte) bitvec {
if pc+8 <= codeLen {
w := binary.LittleEndian.Uint64(code[pc : pc+8])
t := (w & swarPushHi) ^ swarPushPat
// overflow_false_positive
if (t-swarLow)&^t&swarHigh == 0 { // no PUSH byte in this word
pc += 8
continue
Expand Down
1 change: 1 addition & 0 deletions execution/vm/evm.go
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,7 @@ func (evm *EVM) handleFrameRevert(gasRemaining *mdgas.MdGas, err error, snapshot
// and subtracting it wraps mod 2^64 into the correct positive sum. Safe at
// any gas magnitude.
func deriveFrameExecutionGasUsed(inputTotal, gasRemainingTotal uint64, stateGasUsed int64) uint64 {
// overflow_false_positive
return inputTotal - gasRemainingTotal - uint64(stateGasUsed)
}

Expand Down
62 changes: 62 additions & 0 deletions misc/gosentry-run.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
#!/usr/bin/env bash
# Build and run erigon under the gosentry toolchain, which panics on integer
# overflow/underflow at runtime. https://github.com/trailofbits/gosentry
#
# Two kinds of deliberate wraparound have to be silenced or the node cannot
# start:
#
# * whole packages whose purpose is modular arithmetic (below) — exempted
# here, because marking every operation would mean 40 markers in murmur3
# alone;
# * single sites inside packages worth instrumenting — marked in the source
# with `// overflow_false_positive`, so the rest of the package keeps its
# checks.
#
# Anything that panics after that is a finding.
set -euo pipefail

GOSENTRY=${GOSENTRY:-$HOME/fzz/gosentry}
P=github.com/erigontech/erigon

# entire package is modular arithmetic by design
EXEMPT=(
"$P/common/murmur3" # MurmurHash3 mixing
"$P/common/crypto/blake2b" # BLAKE2b compression
"$P/common/bitutil" # SWAR select/popcount
"$P/db/seg/patricia" # SWAR edge lookup
"$P/db/recsplit" # RecSplit remix
"$P/execution/protocol/rules/ethash" # FNV
)

flags=()
for pkg in "${EXEMPT[@]}"; do
flags+=("-gcflags=$pkg=-overflowdetect=false")
done

export GOTOOLCHAIN=local CGO_ENABLED=1

case "${1:-build}" in
build)
# -truncationdetect=true is deliberately NOT set: it fires within the first
# few executions of nearly every package, so it needs its own triage pass.
target=${2:-./cmd/erigon}
out=${OUT:-./build/bin/$(basename "$target")-gosentry}
exec "$GOSENTRY/bin/go" build "${flags[@]}" -o "$out" "$target"
;;
test)
shift
exec "$GOSENTRY/bin/go" test "${flags[@]}" -short -count=1 "${@:-./...}"
;;
fuzz)
# the LibAFL harness is linked by cargo, which does not pull in the C++
# runtime that evmone's modexp needs
export RUSTFLAGS="-C link-arg=-lstdc++"
pkg=$2 target=$3 dur=${4:-60s}
exec "$GOSENTRY/bin/go" test "$pkg" -run='^$' -fuzz="^$target\$" -fuzztime="$dur" \
--focus-on-new-code=false --catch-races=false --catch-leaks=false "${flags[@]}"
;;
*)
echo "usage: $0 {build [pkg]|test [pkgs...]|fuzz <pkg> <FuzzTarget> [dur]}" >&2
exit 2
;;
esac
1 change: 1 addition & 0 deletions p2p/enode/nodedb.go
Original file line number Diff line number Diff line change
Expand Up @@ -595,6 +595,7 @@ func (db *DB) QuerySeeds(n int, maxAge time.Duration) []*Node {
// of hitting all existing nodes in very small databases.
ctr := id[0]
rand.Read(id[:])
// overflow_false_positive
id[0] = ctr + id[0]%16
var n *Node
for k, v, err := c.Seek(nodeKey(id)); k != nil && n == nil; k, v, err = c.Next() {
Expand Down
Loading