bug: struct decode silently drops earlier sections when a section name repeats, unlike map decode #16

Closed
opened 2026-09-14 19:21:08 +00:00 by advisor-bot · 0 comments
Owner

Problem

In encoding/systemd/marshal_decode.go, decodeFileNode (~lines 63–71), the section lookup is built with:

sectionMap[cleanName] = sec

This overwrites any earlier section with the same name, so only the last occurrence's assignments are ever applied to the destination struct. decodeFileToMap (the map-decode path), by contrast, merges assignments across all sections sharing a name into the same destination map. The two decode paths silently disagree on the same input, and the struct path silently loses data with no error.

Reproduction

content := []byte("[Service]\nA=1\n\n[Service]\nB=2\n")

type File struct{ Service Service }
type Service struct{ A, B string }

var f File
systemd.Unmarshal(content, &f)
// f == {Service:{A: B:2}}  -- A is lost!

var m map[string]map[string]any
systemd.Unmarshal(content, &m)
// m == map[Service:map[A:1 B:2]]  -- correct, both keys present

Make decodeFileNode merge assignments from all sections sharing a name (e.g. accumulate a []*AssignNode per clean name, or call decodeSectionNode once per matching section against the same fieldVal instead of looking up a single winner) so struct decoding matches the map decoding semantics.

## Problem In `encoding/systemd/marshal_decode.go`, `decodeFileNode` (~lines 63–71), the section lookup is built with: ```go sectionMap[cleanName] = sec ``` This overwrites any earlier section with the same name, so only the *last* occurrence's assignments are ever applied to the destination struct. `decodeFileToMap` (the map-decode path), by contrast, merges assignments across all sections sharing a name into the same destination map. The two decode paths silently disagree on the same input, and the struct path silently loses data with no error. ## Reproduction ```go content := []byte("[Service]\nA=1\n\n[Service]\nB=2\n") type File struct{ Service Service } type Service struct{ A, B string } var f File systemd.Unmarshal(content, &f) // f == {Service:{A: B:2}} -- A is lost! var m map[string]map[string]any systemd.Unmarshal(content, &m) // m == map[Service:map[A:1 B:2]] -- correct, both keys present ``` ## Recommended fix Make `decodeFileNode` merge assignments from all sections sharing a name (e.g. accumulate a `[]*AssignNode` per clean name, or call `decodeSectionNode` once per matching section against the same `fieldVal` instead of looking up a single winner) so struct decoding matches the map decoding semantics.
Sign in to join this conversation.
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
pandora/systemd-file.gopack#16
No description provided.