Skip to content

Commit dcde6e5

Browse files
committed
dotenv: fix an unintended buffer copy
before this change: the test allocates ~10MB after this change: the test allocates ~300KB fixes docker/compose#10951 Signed-off-by: Nick Santos <nick.santos@docker.com>
1 parent 923ed64 commit dcde6e5

File tree

2 files changed

+29
-1
lines changed

2 files changed

+29
-1
lines changed

dotenv/parser.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,11 @@ func (p *parser) locateKeyName(src string) (string, string, bool, error) {
9898
var key string
9999
var inherited bool
100100
// trim "export" and space at beginning
101-
src = strings.TrimLeftFunc(exportRegex.ReplaceAllString(src, ""), isSpace)
101+
if exportRegex.MatchString(src) {
102+
// we use a `strings.trim` to preserve the pointer to the same underlying memory.
103+
// a regexp replace would copy the string.
104+
src = strings.TrimLeftFunc(strings.TrimPrefix(src, "export"), isSpace)
105+
}
102106

103107
// locate key name end and validate it in single loop
104108
offset := 0

dotenv/parser_test.go

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
package dotenv
22

33
import (
4+
"fmt"
5+
"runtime"
6+
"strings"
47
"testing"
58

69
"gotest.tools/v3/assert"
@@ -38,3 +41,24 @@ func TestParseVariable(t *testing.T) {
3841
assert.Error(t, err, "line 1: unexpected character \"%\" in variable name \"%!(EXTRA string)=foo\"")
3942

4043
}
44+
45+
func TestMemoryExplosion(t *testing.T) {
46+
p := newParser()
47+
var startMemStats runtime.MemStats
48+
var endMemStats runtime.MemStats
49+
runtime.ReadMemStats(&startMemStats)
50+
51+
size := 1000
52+
input := []string{}
53+
for i := 0; i < size; i++ {
54+
input = append(input, fmt.Sprintf("KEY%d=VALUE%d", i, i))
55+
}
56+
out := map[string]string{}
57+
err := p.parse(strings.Join(input, "\n"), out, nil)
58+
assert.NilError(t, err)
59+
assert.Equal(t, size, len(out))
60+
runtime.ReadMemStats(&endMemStats)
61+
assert.Assert(t, endMemStats.Alloc-startMemStats.Alloc < uint64(size)*1000, /* assume 1K per line */
62+
"memory usage should be linear with input size. Memory grew by: %d",
63+
endMemStats.Alloc-startMemStats.Alloc)
64+
}

0 commit comments

Comments
 (0)