chunk.NetworkDecode and (*chunk.Chunk).SetBlock panic on certain data
SetBlock Panic
func (w *World) SetBlockByRuntimeID(pos cube.Pos, blockID uint32) {
if cube.Pos(pos).OutOfBounds(cube.Range(w.Dimension().Range())) {
return
}
chunkPos := protocol.ChunkPos{int32(pos[0]) >> 4, int32(pos[2]) >> 4}
c := w.Chunk(chunkPos) // This returns a dragonfly *chunk.Chunk
if c == nil {
return
}
c.SetBlock(uint8(pos[0]), int16(pos[1]), uint8(pos[2]), 0, blockID) // This line panics
}
case *packet.UpdateBlock:
rid := pk.NewBlockRuntimeID
_, ok := world.BlockByRuntimeID(rid)
if !ok {
rid = airRuntimeID
}
pos := cube.Pos{int(pk.Position.X()), int(pk.Position.Y()), int(pk.Position.Z())}
p.player.World().SetBlockByRuntimeID(pos, rid)
github.com/df-mc/dragonfly/server/world/chunk in (*Chunk).SetBlock at line 88 runtime error: index out of range [-1] https://github.com/df-mc/dragonfly/blob/1171b7e8d91e40388267e04014de2ad9f941c730/server/world/chunk/chunk.go#L88
chunk.NetworkDecode panic
chunk.NetworkDecode panic: runtime error: index out of range [109] with length 24
case *packet.LevelChunk:
dim := dimensionFromNetworkID(pk.Dimension)
c, err := chunk.NetworkDecode(airRuntimeID, pk.RawPayload, int(pk.SubChunkCount), dim.Range())
if err != nil {
c = chunk.New(airRuntimeID, dim.Range())
}
github.com/df-mc/dragonfly/server/world/chunk in NetworkDecode at line 24 https://github.com/df-mc/dragonfly/blob/1171b7e8d91e40388267e04014de2ad9f941c730/server/world/chunk/decode.go#L24
I have many logs of chunk data from these crashes, here's a log of *packet.LevelChunk https://gist.github.com/HashimTheArab/1ebab2a39a22d8af009987d96b598a24
bump
I can't really help with the first issue, since there isn't enough information (missing a buffer & chunk dump) For the second issue, it seems like this is to combat proxies by sending a subchunk version 9 with an index out of range... this should handle this error:
var (
c = New(air, r)
buf = bytes.NewBuffer(data)
- err error
+ n = uint8((r.Height() >> 4) + 1)
)
for i := 0; i < count; i++ {
index := uint8(i)
- c.sub[index], err = decodeSubChunk(buf, c, &index, NetworkEncoding)
+ sub, err := decodeSubChunk(buf, c, &index, NetworkEncoding)
if err != nil {
return nil, err
}
+ if index > n {
+ return nil, fmt.Errorf("index out of range")
+ }
+ c.sub[index] = sub
}
Code used: (with dump: https://gist.github.com/HashimTheArab/1ebab2a39a22d8af009987d96b598a24)
import (
"encoding/base64"
"encoding/json"
"github.com/df-mc/dragonfly/server/block"
"github.com/df-mc/dragonfly/server/world"
"github.com/df-mc/dragonfly/server/world/chunk"
"os"
_ "unsafe"
)
type LevelChunkDump struct {
Position []int `json:"position"`
Dimension int `json:"dimension"`
BlobHashes string `json:"blob_hashes"`
CacheEnabled bool `json:"cache_enabled"`
SubChunkCount int `json:"subchunk_count"`
RawPayloadB64 string `json:"raw_payload_b64"`
HighestSubchunk int `json:"highest_subchunk"`
}
func main() {
world_finaliseBlockRegistry()
file, err := os.ReadFile("level_chunk_dump.json")
if err != nil {
panic(err)
}
var pk LevelChunkDump
if err := json.Unmarshal(file, &pk); err != nil {
panic(err)
}
rawPayload, err := base64.StdEncoding.DecodeString(pk.RawPayloadB64)
if err != nil {
panic(err)
}
dim, _ := world.DimensionByID(pk.Dimension)
airRuntimeID := world.BlockRuntimeID(block.Air{})
c, err := chunk.NetworkDecode(airRuntimeID, rawPayload, pk.SubChunkCount, dim.Range())
if err != nil {
c = chunk.New(airRuntimeID, dim.Range())
}
_ = c
}
// noinspection ALL
//
//go:linkname world_finaliseBlockRegistry github.com/df-mc/dragonfly/server/world.finaliseBlockRegistry
func world_finaliseBlockRegistry()
I THOUGHT THE PROBLEM WAS MINE. YEYYYYYYY
I can't really help with the first issue, since there isn't enough information (missing a buffer & chunk dump) For the second issue, it seems like this is to combat proxies by sending a subchunk version 9 with an index out of range... this should handle this error:
var ( c = New(air, r) buf = bytes.NewBuffer(data)
err error
) for i := 0; i < count; i++ { index := uint8(i)n = uint8((r.Height() >> 4) + 1)
c.sub[index], err = decodeSubChunk(buf, c, &index, NetworkEncoding)
sub, err := decodeSubChunk(buf, c, &index, NetworkEncoding) if err != nil { return nil, err }if index < 0 || index > n {return nil, fmt.Errorf("index out of range")} } Code used: (with dump: https://gist.github.com/HashimTheArab/1ebab2a39a22d8af009987d96b598a24)c.sub[index] = subimport ( "encoding/base64" "encoding/json" "github.com/df-mc/dragonfly/server/block" "github.com/df-mc/dragonfly/server/world" "github.com/df-mc/dragonfly/server/world/chunk" "os" _ "unsafe" )
type LevelChunkDump struct { Position []int
json:"position"Dimension intjson:"dimension"BlobHashes stringjson:"blob_hashes"CacheEnabled booljson:"cache_enabled"SubChunkCount intjson:"subchunk_count"RawPayloadB64 stringjson:"raw_payload_b64"HighestSubchunk intjson:"highest_subchunk"}func main() { world_finaliseBlockRegistry()
file, err := os.ReadFile("level_chunk_dump.json") if err != nil { panic(err) } var pk LevelChunkDump
if err := json.Unmarshal(file, &pk); err != nil { panic(err) } rawPayload, err := base64.StdEncoding.DecodeString(pk.RawPayloadB64) if err != nil { panic(err) }
dim, _ := world.DimensionByID(pk.Dimension)
airRuntimeID := world.BlockRuntimeID(block.Air{}) c, err := chunk.NetworkDecode(airRuntimeID, rawPayload, pk.SubChunkCount, dim.Range()) if err != nil { c = chunk.New(airRuntimeID, dim.Range()) } _ = c }
// noinspection ALL // //go:linkname world_finaliseBlockRegistry github.com/df-mc/dragonfly/server/world.finaliseBlockRegistry func world_finaliseBlockRegistry()
im not sure if this works but index < 0 can't happen anyway because it's unsigned 8
Good catch, just edited my patch