rafthttp: snapshotHandler panics on MsgSnap with missing Snapshot
Bug report criteria
- This bug report is not security related, security issues should be disclosed privately via the report form.
- This is not a support request or question, support requests or questions should be raised in the etcd discussion forums.
- You have read the etcd bug reporting guidelines.
- Existing open issues along with etcd frequently asked questions have been checked and this is not a duplicate.
What happened?
A validly encoded MsgSnap without the optional Snapshot field causes a nil dereference at rafthttp/http.go:272.
What did you expect to happen?
Return 400 Bad Request.
How can we reproduce it (as minimally and precisely as possible)?
Send a POST request to /raft/snapshot with normal valid peer headers and the following raw body:
00 00 00 00 00 00 00 02 08 07
The first 8 bytes are the message length (2). 08 07 encodes a protobuf Message with Type=MsgSnap, while field 9 (Snapshot) is absent.
The message decodes successfully, then snapshotHandler dereferences m.Snapshot.Metadata with m.Snapshot == nil and panics.
Anything else we need to know?
Message.Snapshot is *Snapshot, but the handler checks only m.GetType() before dereferencing m.Snapshot.Metadata.
In raft v3.5, Snapshot was a value field, so an omitted protobuf field decoded to the zero-value Snapshot. In v3.7, it is a pointer, so the same wire message leaves it nil, while snapshotHandler still dereferences it unconditionally.
Etcd version (please run commands below)
A standalone etcd binary was not used.
Reproduced against a locally modified source checkout based on etcd v3.7.1.
The affected snapshotHandler code path matches upstream etcd v3.7.1.
Go: go1.26.5 linux/amd64 etcdctl was not involved.
Etcd configuration (command line flags or environment variables)
No response
Etcd debug information (please run commands below, feel free to obfuscate the IP address or FQDN in the output)
No response
Relevant log output
panic: runtime error: invalid memory address or nil pointer dereference
go.etcd.io/etcd/server/v3/etcdserver/api/rafthttp.(*snapshotHandler).ServeHTTP
server/etcdserver/api/rafthttp/http.go:272
Source: etcd-io/etcd