cespare / xxhash

A Go implementation of the 64-bit xxHash algorithm (XXH64)
MIT License
1.79k stars 123 forks source link

Apple M1 sum64 throw panic: runtime error: slice bounds out of range error #76

Closed maxid closed 6 months ago

maxid commented 6 months ago

panic: runtime error: slice bounds out of range [::41] with capacity 9

goroutine 1 [running]: github.com/cespare/xxhash/v2.Sum64({0x14014c9ecc0, 0x29, 0x9}) .../vendor/github.com/cespare/xxhash/v2/xxhash_other.go:22 +0x6c4

cespare commented 6 months ago

Can you please show runnable code that demonstrates the problem? Thanks.

maxid commented 6 months ago

Can you please show runnable code that demonstrates the problem? Thanks.

package main

import (
    "context"
    "fmt"
    "time"
    "unsafe"

    "github.com/buraksezer/olric"
    "github.com/buraksezer/olric/config"
    "github.com/cespare/xxhash/v2"
)

type xxhasher struct {
}

func (xh xxhasher) Sum64(key []byte) uint64 {
    fmt.Println(fmt.Sprintf("key %s, length: %d", string(key), len(key)))
    return xxhash.Sum64(key) // replace to xxhash.Sum64([]byte(string(key))) can fixed this problem
}

var _dm olric.DMap

func main() {
    // in this case key length >= 32 always test pass
    tmp := "test-cluster-dmap" + "lock.xxx-xxxxx-xxxx-xxx"
    fmt.Println(fmt.Sprintf("key length: %d", len(tmp)))
    fmt.Println(xxhash.Sum64(*(*[]byte)(unsafe.Pointer(&tmp))))

    // but this case some time test failure
    initOlric()
    testDLock("xxx-xxxxx")          // key length < 32, test pass
    testDLock("xxx-xxxxx-xxxx-xxx") // key length >= 32, test failure
}

func initOlric() {
    ctx, cancel := context.WithCancel(context.Background())
    var db *olric.Olric
    c := config.New("local")
    c.Hasher = xxhasher{}
    c.Started = func() {
        defer cancel()
        fmt.Println("olric cluster is ready to accept connections")
    }

    db, err := olric.New(c)
    if err != nil {
        fmt.Println(fmt.Sprintf("Failed to create olric cluster instance: %v", err))
    }
    go func() {
        err = db.Start()
        if err != nil {
            fmt.Println(fmt.Sprintf("olric cluster Start returned an error: %v", err))
        }
    }()

    <-ctx.Done()

    if db != nil {
        embeddedClient := db.NewEmbeddedClient()
        if dm, err := embeddedClient.NewDMap(fmt.Sprintf("%s-cluster-dmap", "test")); err == nil {
            _dm = dm
        }
    }
}

func testDLock(key string) {
    if _dm != nil {
        ctx := context.Background()
        if lock, err := _dm.Lock(ctx, fmt.Sprintf("lock.%s", key), time.Second*5); err == nil {
            _ = lock.Unlock(ctx) // <--- failure
        }
    }
}
2024/03/19 10:26:10 [INFO] Routing table has been pushed by 127.0.0.1:3320 => operations.go:86
2024/03/19 10:26:10 [INFO] The cluster coordinator has been bootstrapped => discovery.go:42
2024/03/19 10:26:10 [INFO] Memberlist bindAddr: 127.0.0.1, bindPort: 3322 => routingtable.go:413
2024/03/19 10:26:10 [INFO] Cluster coordinator: 127.0.0.1:3320 => routingtable.go:414
2024/03/19 10:26:10 [INFO] Node name in the cluster: 127.0.0.1:3320 => olric.go:380
2024/03/19 10:26:10 [INFO] Olric bindAddr: 127.0.0.1, bindPort: 3320 => olric.go:386
2024/03/19 10:26:10 [INFO] Replication count is 1 => olric.go:388
olric cluster is ready to accept connections
key test-cluster-dmaplock.xxx-xxxxx, length: 31
key test-cluster-dmaplock.xxx-xxxxx, length: 31
key test-cluster-dmaplock.xxx-xxxxx, length: 31
key test-cluster-dmaplock.xxx-xxxxx, length: 31
key test-cluster-dmaplock.xxx-xxxxx, length: 31
key test-cluster-dmaplock.xxx-xxxxx-xxxx-xxx, length: 40
panic: runtime error: slice bounds out of range [::40] with capacity 0

goroutine 1 [running]:
github.com/cespare/xxhash/v2.Sum64({0x140000ce2d0, 0x28, 0x0})
        .../vendor/github.com/cespare/xxhash/v2/xxhash_other.go:22 +0x6c4
main.xxhasher.Sum64({}, {0x140000ce2d0, 0x28, 0x0})
        .../main.go:19 +0x1b8
github.com/buraksezer/olric/internal/cluster/partitions.HKey({0x140004ea000, 0x11}, {0x140004ea030, 0x17})
        .../vendor/github.com/buraksezer/olric/internal/cluster/partitions/hkey.go:37 +0x8c
github.com/buraksezer/olric/internal/dmap.(*DMap).put(0x140004ec000, 0x140004f0100)
        .../vendor/github.com/buraksezer/olric/internal/dmap/put.go:363 +0x68
github.com/buraksezer/olric/internal/dmap.(*DMap).tryLock(0x140004ec000, 0x140004f0100, 0x12a05f200)
        .../vendor/github.com/buraksezer/olric/internal/dmap/lock.go:94 +0x3c
github.com/buraksezer/olric/internal/dmap.(*DMap).Lock(0x140004ec000, {0x104ff5b10, 0x10544ce20}, {0x140004ea030, 0x17}, 0x0, 0x12a05f200)
        .../vendor/github.com/buraksezer/olric/internal/dmap/lock.go:157 +0x264
github.com/buraksezer/olric.(*EmbeddedDMap).Lock(0x14000126000, {0x104ff5b10, 0x10544ce20}, {0x140004ea030, 0x17}, 0x12a05f200)
        .../vendor/github.com/buraksezer/olric/embedded_client.go:167 +0x78
main.testDLock({0x104e27713, 0x12})
        .../main.go:70 +0x11c
main.main()
        .../main.go:33 +0x210
Exiting.

Debugger finished with the exit code 0
cespare commented 6 months ago

Can you make a simpler reproducer without the other packages? I suspect there is a bug in your code. At a glance, my best guess is that you have a data race on the slice you're passing to Sum64 (you can run with -race to check). But I'd want a simpler repro case before I do more debugging.

maxid commented 6 months ago

Can you make a simpler reproducer without the other packages? I suspect there is a bug in your code. At a glance, my best guess is that you have a data race on the slice you're passing to Sum64 (you can run with -race to check). But I'd want a simpler repro case before I do more debugging.

You are right!Thanks!

// xxhash.go:211 maybe binary.LittleEndian.Uint64(b) have a data race ?
func u64(b []byte) uint64 { return binary.LittleEndian.Uint64(b) }