dragonfly icon indicating copy to clipboard operation
dragonfly copied to clipboard

chunk.NetworkDecode and (*chunk.Chunk).SetBlock panic on certain data

Open HashimTheArab opened this issue 9 months ago • 5 comments

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

HashimTheArab avatar Mar 03 '25 02:03 HashimTheArab

bump

ethaniccc avatar Jul 16 '25 00:07 ethaniccc

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()

Flonja avatar Jul 16 '25 14:07 Flonja

I THOUGHT THE PROBLEM WAS MINE. YEYYYYYYY

ismaileke avatar Jul 16 '25 17:07 ismaileke

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 < 0 || 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()

im not sure if this works but index < 0 can't happen anyway because it's unsigned 8

ismaileke avatar Jul 16 '25 17:07 ismaileke

Good catch, just edited my patch

Flonja avatar Jul 16 '25 20:07 Flonja