Skip to content

Commit 7b72ddb

Browse files
committed
systemd: simplify parser and fix infinite loop
This commit simplifies the systemd parser logic, and it solves an infinite loop when using a continuation line. Closes: #24810 Signed-off-by: Giuseppe Scrivano <gscrivan@redhat.com>
1 parent bf1661c commit 7b72ddb

File tree

3 files changed

+46
-39
lines changed

3 files changed

+46
-39
lines changed

pkg/systemd/parser/split.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -55,8 +55,8 @@ func isValidUnicode(c uint32) bool {
5555
/* This is based on code from systemd (src/basic/escape.c), marked LGPL-2.1-or-later and is copyrighted by the systemd developers */
5656

5757
func cUnescapeOne(p string, acceptNul bool) (int, rune, bool) {
58-
var count = 1
59-
var eightBit = false
58+
count := 1
59+
eightBit := false
6060
var ret rune
6161

6262
// Unescapes C style. Returns the unescaped character in ret.
@@ -297,7 +297,7 @@ loop1:
297297
}
298298

299299
if flags&(SplitCUnescape|SplitUnescapeSeparators) != 0 {
300-
var r = -1
300+
r := -1
301301
var u rune
302302

303303
if flags&SplitCUnescape != 0 {

pkg/systemd/parser/unitfile.go

Lines changed: 15 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -376,53 +376,36 @@ func (p *UnitFileParser) flushPendingComments(toComment bool) {
376376
}
377377
}
378378

379-
func nextLine(data string, afterPos int) (string, string) {
380-
rest := data[afterPos:]
381-
if i := strings.Index(rest, "\n"); i >= 0 {
382-
return strings.TrimSpace(data[:i+afterPos]), data[i+afterPos+1:]
383-
}
384-
return data, ""
385-
}
386-
387-
func trimSpacesFromLines(data string) string {
388-
lines := strings.Split(data, "\n")
389-
for i, line := range lines {
390-
lines[i] = strings.TrimSpace(line)
391-
}
392-
return strings.Join(lines, "\n")
393-
}
394-
395379
// Parse an already loaded unit file (in the form of a string)
396380
func (f *UnitFile) Parse(data string) error {
397381
p := &UnitFileParser{
398-
file: f,
399-
lineNr: 1,
382+
file: f,
400383
}
401384

402-
data = trimSpacesFromLines(data)
385+
lines := strings.Split(strings.TrimSuffix(data, "\n"), "\n")
386+
remaining := ""
403387

404-
for len(data) > 0 {
405-
origdata := data
406-
nLines := 1
407-
var line string
408-
line, data = nextLine(data, 0)
388+
for lineNr, line := range lines {
389+
p.lineNr = lineNr + 1
409390

410391
if !lineIsComment(line) {
411-
// Handle multi-line continuations
412-
// Note: This doesn't support comments in the middle of the continuation, which systemd does
413-
if lineIsKeyValuePair(line) {
414-
for len(data) > 0 && line[len(line)-1] == '\\' {
415-
line, data = nextLine(origdata, len(line)+1)
416-
nLines++
392+
// check whether the line is a continuation of the previous line
393+
if strings.HasSuffix(line, "\\") {
394+
line = line[:len(line)-1]
395+
if lineNr != len(lines)-1 {
396+
remaining += line
397+
continue
417398
}
418399
}
400+
if remaining != "" {
401+
line = remaining + line
402+
remaining = ""
403+
}
419404
}
420405

421406
if err := p.parseLine(line); err != nil {
422407
return err
423408
}
424-
425-
p.lineNr += nLines
426409
}
427410

428411
if p.currentGroup == nil {

pkg/systemd/parser/unitfile_test.go

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package parser
22

33
import (
44
"reflect"
5+
"strings"
56
"testing"
67

78
"github.com/stretchr/testify/assert"
@@ -119,7 +120,9 @@ After=dbus.socket
119120
120121
[Service]
121122
BusName=org.freedesktop.login1
122-
CapabilityBoundingSet=CAP_SYS_ADMIN CAP_MAC_ADMIN CAP_AUDIT_CONTROL CAP_CHOWN CAP_DAC_READ_SEARCH CAP_DAC_OVERRIDE CAP_FOWNER CAP_SYS_TTY_CONFIG CAP_LINUX_IMMUTABLE
123+
CapabilityBoundingSet=CAP_SYS_ADMIN CAP_MAC_ADMIN CAP_AUDIT_CONTROL CAP_CHOWN CAP_DAC_READ_SEARCH CAP_DAC_OVERRIDE # comment inside a continuation line \
124+
CAP_FOWNER \
125+
CAP_SYS_TTY_CONFIG CAP_LINUX_IMMUTABLE
123126
DeviceAllow=block-* r
124127
DeviceAllow=char-/dev/console rw
125128
DeviceAllow=char-drm rw
@@ -158,8 +161,8 @@ SystemCallFilter=@system-service
158161
159162
# Increase the default a bit in order to allow many simultaneous logins since
160163
# we keep one fd open per session.
161-
LimitNOFILE=524288
162-
`
164+
LimitNOFILE=524288 \`
165+
163166
const systemdnetworkdService = `# SPDX-License-Identifier: LGPL-2.1-or-later
164167
#
165168
# This file is part of systemd.
@@ -264,6 +267,14 @@ var sampleDropinPaths = map[string][]string{
264267
sampleDropinTemplateInstance: sampleDropinTemplateInstancePaths,
265268
}
266269

270+
func filterComments(input string) string {
271+
// merge continuation lines
272+
joined := strings.ReplaceAll(input, "\\\n", "")
273+
274+
// remove trailing newlines and backslashes
275+
return strings.TrimSuffix(strings.TrimSuffix(joined, "\n"), "\\")
276+
}
277+
267278
func TestRanges_Roundtrip(t *testing.T) {
268279
for i := range samples {
269280
sample := samples[i]
@@ -278,7 +289,7 @@ func TestRanges_Roundtrip(t *testing.T) {
278289
panic(e)
279290
}
280291

281-
assert.Equal(t, sample, asStr)
292+
assert.Equal(t, filterComments(sample), filterComments(asStr))
282293
}
283294
}
284295

@@ -292,3 +303,16 @@ func TestUnitDropinPaths_Search(t *testing.T) {
292303
assert.True(t, reflect.DeepEqual(expectedPaths, generatedPaths))
293304
}
294305
}
306+
307+
func FuzzParser(f *testing.F) {
308+
for _, sample := range samples {
309+
f.Add([]byte(sample))
310+
}
311+
312+
f.Fuzz(func(t *testing.T, orig []byte) {
313+
unitFile := NewUnitFile()
314+
unitFile.Path = "foo/bar"
315+
unitFile.Filename = "bar"
316+
_ = unitFile.Parse(string(orig))
317+
})
318+
}

0 commit comments

Comments
 (0)