Skip to content

Commit 8818cc8

Browse files
dspeziarsc
authored andcommitted
go/build: fix cgo ${SRCDIR} substitution when that variable contains spaces
When the source directory path contains spaces, cgo directives cannot be properly validated: $ pwd /root/src/issue 11868 $ cat main.go package main //#cgo CFLAGS: -I${SRCDIR}/../../include import "C" func main() { } $ go build can't load package: package issue 11868: /root/src/issue 11868/main.go: malformed #cgo argument: -I/root/src/issue 11868/../../include Make sure spaces are tolerated in ${SRCDIR} when this variable is expanded. This applies to ${SRCDIR} only. Shell safety checks are still done in the same exact way for anything else. Fixes #11868 Change-Id: I93d1d2b5ab167caa7ae353fe46fb8f69f1f06969 Reviewed-on: https://go-review.googlesource.com/16302 Reviewed-by: Russ Cox <[email protected]>
1 parent 7b767f4 commit 8818cc8

File tree

2 files changed

+52
-9
lines changed

2 files changed

+52
-9
lines changed

src/go/build/build.go

Lines changed: 29 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1127,9 +1127,9 @@ func (ctxt *Context) saveCgo(filename string, di *Package, cg *ast.CommentGroup)
11271127
if err != nil {
11281128
return fmt.Errorf("%s: invalid #cgo line: %s", filename, orig)
11291129
}
1130+
var ok bool
11301131
for i, arg := range args {
1131-
arg = expandSrcDir(arg, di.Dir)
1132-
if !safeCgoName(arg) {
1132+
if arg, ok = expandSrcDir(arg, di.Dir); !ok {
11331133
return fmt.Errorf("%s: malformed #cgo argument: %s", filename, arg)
11341134
}
11351135
args[i] = arg
@@ -1153,25 +1153,46 @@ func (ctxt *Context) saveCgo(filename string, di *Package, cg *ast.CommentGroup)
11531153
return nil
11541154
}
11551155

1156-
func expandSrcDir(str string, srcdir string) string {
1156+
// expandSrcDir expands any occurrence of ${SRCDIR}, making sure
1157+
// the result is safe for the shell.
1158+
func expandSrcDir(str string, srcdir string) (string, bool) {
11571159
// "\" delimited paths cause safeCgoName to fail
11581160
// so convert native paths with a different delimeter
1159-
// to "/" before starting (eg: on windows)
1161+
// to "/" before starting (eg: on windows).
11601162
srcdir = filepath.ToSlash(srcdir)
1161-
return strings.Replace(str, "${SRCDIR}", srcdir, -1)
1163+
1164+
// Spaces are tolerated in ${SRCDIR}, but not anywhere else.
1165+
chunks := strings.Split(str, "${SRCDIR}")
1166+
if len(chunks) < 2 {
1167+
return str, safeCgoName(str, false)
1168+
}
1169+
ok := true
1170+
for _, chunk := range chunks {
1171+
ok = ok && (chunk == "" || safeCgoName(chunk, false))
1172+
}
1173+
ok = ok && (srcdir == "" || safeCgoName(srcdir, true))
1174+
res := strings.Join(chunks, srcdir)
1175+
return res, ok && res != ""
11621176
}
11631177

11641178
// NOTE: $ is not safe for the shell, but it is allowed here because of linker options like -Wl,$ORIGIN.
11651179
// We never pass these arguments to a shell (just to programs we construct argv for), so this should be okay.
11661180
// See golang.org/issue/6038.
1167-
var safeBytes = []byte("+-.,/0123456789=ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz:$")
1181+
const safeString = "+-.,/0123456789=ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz:$"
1182+
const safeSpaces = " "
11681183

1169-
func safeCgoName(s string) bool {
1184+
var safeBytes = []byte(safeSpaces + safeString)
1185+
1186+
func safeCgoName(s string, spaces bool) bool {
11701187
if s == "" {
11711188
return false
11721189
}
1190+
safe := safeBytes
1191+
if !spaces {
1192+
safe = safe[len(safeSpaces):]
1193+
}
11731194
for i := 0; i < len(s); i++ {
1174-
if c := s[i]; c < 0x80 && bytes.IndexByte(safeBytes, c) < 0 {
1195+
if c := s[i]; c < 0x80 && bytes.IndexByte(safe, c) < 0 {
11751196
return false
11761197
}
11771198
}

src/go/build/build_test.go

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -267,11 +267,33 @@ var expandSrcDirTests = []struct {
267267

268268
func TestExpandSrcDir(t *testing.T) {
269269
for _, test := range expandSrcDirTests {
270-
output := expandSrcDir(test.input, expandSrcDirPath)
270+
output, _ := expandSrcDir(test.input, expandSrcDirPath)
271271
if output != test.expected {
272272
t.Errorf("%q expands to %q with SRCDIR=%q when %q is expected", test.input, output, expandSrcDirPath, test.expected)
273273
} else {
274274
t.Logf("%q expands to %q with SRCDIR=%q", test.input, output, expandSrcDirPath)
275275
}
276276
}
277277
}
278+
279+
func TestShellSafety(t *testing.T) {
280+
tests := []struct {
281+
input, srcdir, expected string
282+
result bool
283+
}{
284+
{"-I${SRCDIR}/../include", "/projects/src/issue 11868", "-I/projects/src/issue 11868/../include", true},
285+
{"-X${SRCDIR}/1,${SRCDIR}/2", "/projects/src/issue 11868", "-X/projects/src/issue 11868/1,/projects/src/issue 11868/2", true},
286+
{"-I/tmp -I/tmp", "/tmp2", "-I/tmp -I/tmp", false},
287+
{"-I/tmp", "/tmp/[0]", "-I/tmp", true},
288+
{"-I${SRCDIR}/dir", "/tmp/[0]", "-I/tmp/[0]/dir", false},
289+
}
290+
for _, test := range tests {
291+
output, ok := expandSrcDir(test.input, test.srcdir)
292+
if ok != test.result {
293+
t.Errorf("Expected %t while %q expands to %q with SRCDIR=%q; got %t", test.result, test.input, output, test.srcdir, ok)
294+
}
295+
if output != test.expected {
296+
t.Errorf("Expected %q while %q expands with SRCDIR=%q; got %q", test.expected, test.input, test.srcdir, output)
297+
}
298+
}
299+
}

0 commit comments

Comments
 (0)