go-yaml / yaml

YAML support for the Go language.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Empty `!!null` node fails encoding and causes panic

TristanSpeakEasy opened this issue · comments

Consider this reproducible:

package main

import (
	"fmt"

	"gopkg.in/yaml.v3"
)

func createIntNode(str string) *yaml.Node {
	n := &yaml.Node{
		Kind:  yaml.ScalarNode,
		Tag:   "!!int",
		Value: str,
	}
	return n
}

func createEmptyNullNode() *yaml.Node {
	n := &yaml.Node{
		Kind:  yaml.ScalarNode,
		Tag:   "!!null",
		Value: "",
	}
	return n
}

func encodeNode(node *yaml.Node) error {
	var rawNode yaml.Node
	if err := rawNode.Encode(node); err != nil {
		return err
	}

	var v interface{}
	if err := rawNode.Decode(&v); err != nil {
		return err
	}

	fmt.Println(v)

	return nil
}

func main() {
	err := encodeNode(createIntNode("1"))
	if err != nil {
		panic(err)
	}

	yamlDoc := `key:`

	var node yaml.Node
	if err := yaml.Unmarshal([]byte(yamlDoc), &node); err != nil {
		panic(err)
	}

	err = encodeNode(node.Content[0].Content[1])
	if err != nil {
		panic(err)
	}

	err = encodeNode(createEmptyNullNode())
	if err != nil {
		panic(err)
	}

	fmt.Println("done")
}

this causes this panic:

go run main.go
1
panic: runtime error: invalid memory address or nil pointer dereference [recovered]
        panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x48 pc=0x4ea2f3]

goroutine 1 [running]:
gopkg.in/yaml%2ev3.handleErr(0xc00015dbc8)
        /home/trist/go/pkg/mod/gopkg.in/yaml.v3@v3.0.1/yaml.go:294 +0x6d
panic({0x4fda80?, 0x5ea890?})
        /home/linuxbrew/.linuxbrew/opt/go/libexec/src/runtime/panic.go:914 +0x21f
gopkg.in/yaml%2ev3.(*Node).Encode(0xc000144820, {0x5071a0, 0xc000144780})
        /home/trist/go/pkg/mod/gopkg.in/yaml.v3@v3.0.1/yaml.go:269 +0x593
main.encodeNode(0xc000136598?)
        /home/trist/workspace/scratch/main.go:29 +0x3b
main.main()
        /home/trist/workspace/scratch/main.go:56 +0xdb

as far as I can see a key in a yaml document can have no value and it is meant to be interpreted as a null value. https://stackoverflow.com/a/64462925

Which is fine when unmarshalling the document but I have code that is then also trying to marshal yaml again (and I'm working with yaml.Node instead of go models of the document to control ordering etc of maps)

And it blows up when Encoding this particular type of node.

The reason is pretty clear here: https://github.com/go-yaml/yaml/blob/v3/yaml.go#L269

func (n *Node) Encode(v interface{}) (err error) {
	defer handleErr(&err)
	e := newEncoder()
	defer e.destroy()
	e.marshalDoc("", reflect.ValueOf(v))
	e.finish()
	p := newParser(e.out)
	p.textless = true
	defer p.destroy()
	doc := p.parse()
	*n = *doc.Content[0]
	return nil
}

The value returned from p.parse() is nil and then this is dereferenced without any nil check