Commit 6e5e403e authored by Kirill Smelkov's avatar Kirill Smelkov Committed by Kamil Kisiel

encoder: Fix GLOBAL emission wrt module/name with \n

Caught via fuzzing:

	"\x8c\x030\n02\x93."

        0: \x8c SHORT_BINUNICODE '0\n0'
        5: 2    DUP
        6: \x93 STACK_GLOBAL
        7: .    STOP

	panic: protocol 0: decode back error: err
	pickle: "c0\n0\n0\n0\n."

	goroutine 1 [running]:
	github.com/kisielk/og-rek.Fuzz(0x7f2f1009a000, 0x8, 0x200000, 0x3)
	        /tmp/go-fuzz-build645492341/gopath/src/github.com/kisielk/og-rek/fuzz.go:47 +0x8b8
	go-fuzz-dep.Main(0x525e10)
	        /tmp/go-fuzz-build645492341/goroot/src/go-fuzz-dep/main.go:49 +0xad
	main.main()
	        /tmp/go-fuzz-build645492341/gopath/src/github.com/kisielk/og-rek/go.fuzz.main/main.go:10 +0x2d
	exit status 2

i.e. '0\n0' module name was emitted as-is as part ot text-based GLOBAL which
completely broke pickle stream.

For the reference Python decodes such globals with \n in name just ok:

	In [10]: s = b"S'decimal\\nq'\nS'Decimal'\n\x93."

	In [11]: pickle.loads(s)
	---------------------------------------------------------------------------
	ModuleNotFoundError                       Traceback (most recent call last)
	<ipython-input-11-764e4625bc41> in <module>()
	----> 1 pickle.loads(s)

	ModuleNotFoundError: No module named 'decimal\nq'

	In [12]: import sys

	In [15]: d = sys.modules['decimal']

	In [16]: sys.modules['decimal\nq'] = d

	In [17]: pickle.loads(s)
	Out[17]: decimal.Decimal
parent f2f59c50
...@@ -420,6 +420,8 @@ func (e *Encoder) encodeCall(v *Call) error { ...@@ -420,6 +420,8 @@ func (e *Encoder) encodeCall(v *Call) error {
return e.emit(opReduce) return e.emit(opReduce)
} }
var errP0123GlobalStringLineOnly = errors.New(`protocol 0-3: global: module & name must be string without \n`)
func (e *Encoder) encodeClass(v *Class) error { func (e *Encoder) encodeClass(v *Class) error {
// PEP 3154: Protocol 4 forbids use of the GLOBAL opcode and replaces // PEP 3154: Protocol 4 forbids use of the GLOBAL opcode and replaces
// it with STACK_GLOBAL. // it with STACK_GLOBAL.
...@@ -436,6 +438,9 @@ func (e *Encoder) encodeClass(v *Class) error { ...@@ -436,6 +438,9 @@ func (e *Encoder) encodeClass(v *Class) error {
} }
// else use GLOBAL opcode from protocol 0 // else use GLOBAL opcode from protocol 0
if strings.Contains(v.Module, "\n") || strings.Contains(v.Name, "\n") {
return errP0123GlobalStringLineOnly
}
return e.emitf("%c%s\n%s\n", opGlobal, v.Module, v.Name) return e.emitf("%c%s\n%s\n", opGlobal, v.Module, v.Name)
} }
......
...@@ -32,8 +32,14 @@ func Fuzz(data []byte) int { ...@@ -32,8 +32,14 @@ func Fuzz(data []byte) int {
err = enc.Encode(obj) err = enc.Encode(obj)
if err != nil { if err != nil {
// must succeed, as obj was obtained via successful decode // must succeed, as obj was obtained via successful decode
// the only exception is that we cannot encode non-string Ref at proto=0 // some exceptions are accounted for first:
if proto == 0 && err == errP0PersIDStringLineOnly { switch {
case proto == 0 && err == errP0PersIDStringLineOnly:
// we cannot encode non-string Ref at proto=0
continue
case proto <= 3 && err == errP0123GlobalStringLineOnly:
// we cannot encode Class (GLOBAL opcode) with \n at proto <= 4
continue continue
} }
panic(fmt.Sprintf("protocol %d: encode error: %s", proto, err)) panic(fmt.Sprintf("protocol %d: encode error: %s", proto, err))
......
...@@ -303,6 +303,10 @@ var tests = []TestEntry{ ...@@ -303,6 +303,10 @@ var tests = []TestEntry{
P4_("\x8c\x03foo\x8c\x03bar\x93."), // SHORT_BINUNICODE + STACK_GLOBAL P4_("\x8c\x03foo\x8c\x03bar\x93."), // SHORT_BINUNICODE + STACK_GLOBAL
I("S'foo'\nS'bar'\n\x93.")), // STRING + STACK_GLOBAL I("S'foo'\nS'bar'\n\x93.")), // STRING + STACK_GLOBAL
X("foo\n2.bar # global with \\n", Class{Module: "foo\n2", Name: "bar"},
P0123(errP0123GlobalStringLineOnly),
P4_("\x8c\x05foo\n2\x8c\x03bar\x93.")), // SHORT_BINUNICODE + STACK_GLOBAL
X(`foo.bar("bing") # global + reduce`, Call{Callable: Class{Module: "foo", Name: "bar"}, Args: []interface{}{"bing"}}, X(`foo.bar("bing") # global + reduce`, Call{Callable: Class{Module: "foo", Name: "bar"}, Args: []interface{}{"bing"}},
P0("cfoo\nbar\n(S\"bing\"\ntR."), // GLOBAL + MARK + STRING + TUPLE + REDUCE P0("cfoo\nbar\n(S\"bing\"\ntR."), // GLOBAL + MARK + STRING + TUPLE + REDUCE
P1("cfoo\nbar\n(U\x04bingtR."), // GLOBAL + MARK + SHORT_BINSTRING + TUPLE + REDUCE P1("cfoo\nbar\n(U\x04bingtR."), // GLOBAL + MARK + SHORT_BINSTRING + TUPLE + REDUCE
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment