Skip to content

panic: index out of range in getBytesAndSize on empty event payload (StreamReplaced) crashes process #194

Description

@rbinar

Summary

getBytesAndSize panics with index out of range [0] with length 0 and crashes the whole process (SIGABRT) when an event's marshaled payload is empty. Reproducibly triggered by a StreamReplaced event.

Version

neonize 0.3.18.post0 (latest), Linux x86_64, Python 3.12.

Traceback (from production)

INFO  Successfully authenticated
INFO  Got replaced stream error, sending StreamReplaced event
panic: runtime error: index out of range [0] with length 0

goroutine 18 [running, locked to thread]:
main.getBytesAndSize(...)
	/home/runner/work/neonize/neonize/goneonize/main.go:341
main.CallbackFunction({...}, ...)
	/home/runner/work/neonize/neonize/goneonize/main.go:2444 +0x1eb
main.Neonize(...)
	/home/runner/work/neonize/neonize/goneonize/main.go:1092 +0xad5

Root cause

getBytesAndSize (goneonize/main.go:340-344) dereferences &data[0]:

func getBytesAndSize(data []byte) (*C.char, C.size_t) {
	messageSourceCDATA := (*C.char)(unsafe.Pointer(&data[0]))  // panics when len(data)==0
	messageSourceCSize := C.size_t(len(data))
	return messageSourceCDATA, messageSourceCSize
}

CallbackFunction (main.go:2444) calls it with proto.Marshal(message.message). For some events (observed: StreamReplaced) the marshaled message is empty (0 bytes), so &data[0] panics and aborts the runtime — taking down the entire host process.

Impact

Any session that receives a StreamReplaced event (e.g. the same account's stream is taken over by another client) crashes the process. Under supervisor/systemd this becomes a crash-restart loop (reconnect → StreamReplaced → panic → restart → …), making the service unusable and blocking new QR pairings.

Proposed fix

Guard getBytesAndSize against empty input:

func getBytesAndSize(data []byte) (*C.char, C.size_t) {
	if len(data) == 0 {
		return nil, 0
	}
	messageSourceCDATA := (*C.char)(unsafe.Pointer(&data[0]))
	messageSourceCSize := C.size_t(len(data))
	return messageSourceCDATA, messageSourceCSize
}

(The C side already receives an explicit size, so a nil/0 pair for empty payloads is safe.)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions