fix(decryption): eliminate data race in JPK decompression

Package-level globals mShiftIndex and mFlag were shared across
concurrent goroutines calling UnpackSimple from quest handlers.
Replace with a local jpkState struct scoped to each decompression
call. Add concurrent safety test validated with -race.
This commit is contained in:
Houmgaor
2026-02-23 20:01:38 +01:00
parent f712e3c04d
commit 7af41a7796
2 changed files with 56 additions and 30 deletions

View File

@@ -81,21 +81,40 @@ func TestUnpackSimple_JPKHeader(t *testing.T) {
}
func TestJPKBitShift_Initialization(t *testing.T) {
// Test that the function doesn't crash with bad initial global state
mShiftIndex = 10
mFlag = 0xFF
// Create data without JPK header (will return as-is)
// Need at least 4 bytes since UnpackSimple reads a uint32 header
// Test that bitShift correctly initializes from zero state
bf := byteframe.NewByteFrame()
bf.WriteUint32(0xAABBCCDD) // Not a JPK header
bf.WriteUint8(0xFF) // All bits set
bf.WriteUint8(0x00) // No bits set
data := bf.Data()
result := UnpackSimple(data)
_, _ = bf.Seek(0, io.SeekStart)
s := &jpkState{}
// Without JPK header, should return data as-is
if !bytes.Equal(result, data) {
t.Error("UnpackSimple with non-JPK data should return input as-is")
// First call should read 0xFF as flag and return bit 7 = 1
bit := s.bitShift(bf)
if bit != 1 {
t.Errorf("bitShift() first bit of 0xFF = %d, want 1", bit)
}
}
func TestUnpackSimple_ConcurrentSafety(t *testing.T) {
// Verify that concurrent UnpackSimple calls don't race.
// Non-JPK data is returned as-is; the important thing is no data race.
input := []byte{0x00, 0x01, 0x02, 0x03}
done := make(chan struct{})
for i := 0; i < 8; i++ {
go func() {
defer func() { done <- struct{}{} }()
for j := 0; j < 100; j++ {
result := UnpackSimple(input)
if !bytes.Equal(result, input) {
t.Errorf("concurrent UnpackSimple returned wrong data")
}
}
}()
}
for i := 0; i < 8; i++ {
<-done
}
}