syntax: Parser does not handle unescaped slash as original value in replacement parameter expansion
Ikke opened this issue · 3 comments
Ikke commented
When parsing this snippet:
: "${v////\\/}"
The following AST is generated:
Repl: *syntax.Replace {
. All: true
. Orig: nil
. With: *syntax.Word {
. . Parts: []syntax.WordPart (len = 1) {
. . . 0: *syntax.Lit {
. . . . ValuePos: 1:10
. . . . ValueEnd: 1:14
. . . . Value: "/\\\\/"
. . . }
. . }
. }
}
Note that Orig
is nil
Full AST
*syntax.File {
. Name: "examples/expand_replace.sh"
. Stmts: []*syntax.Stmt (len = 1) {
. . 0: *syntax.Stmt {
. . . Comments: []syntax.Comment (len = 0) {}
. . . Cmd: *syntax.CallExpr {
. . . . Assigns: []*syntax.Assign (len = 0) {}
. . . . Args: []*syntax.Word (len = 2) {
. . . . . 0: *syntax.Word {
. . . . . . Parts: []syntax.WordPart (len = 1) {
. . . . . . . 0: *syntax.Lit {
. . . . . . . . ValuePos: 1:1
. . . . . . . . ValueEnd: 1:2
. . . . . . . . Value: ":"
. . . . . . . }
. . . . . . }
. . . . . }
. . . . . 1: *syntax.Word {
. . . . . . Parts: []syntax.WordPart (len = 1) {
. . . . . . . 0: *syntax.DblQuoted {
. . . . . . . . Left: 1:3
. . . . . . . . Right: 1:15
. . . . . . . . Dollar: false
. . . . . . . . Parts: []syntax.WordPart (len = 1) {
. . . . . . . . . 0: *syntax.ParamExp {
. . . . . . . . . . Dollar: 1:4
. . . . . . . . . . Rbrace: 1:14
. . . . . . . . . . Short: false
. . . . . . . . . . Excl: false
. . . . . . . . . . Length: false
. . . . . . . . . . Width: false
. . . . . . . . . . Param: *syntax.Lit {
. . . . . . . . . . . ValuePos: 1:6
. . . . . . . . . . . ValueEnd: 1:7
. . . . . . . . . . . Value: "v"
. . . . . . . . . . }
. . . . . . . . . . Index: nil
. . . . . . . . . . Slice: nil
. . . . . . . . . . Repl: *syntax.Replace {
. . . . . . . . . . . All: true
. . . . . . . . . . . Orig: nil
. . . . . . . . . . . With: *syntax.Word {
. . . . . . . . . . . . Parts: []syntax.WordPart (len = 1) {
. . . . . . . . . . . . . 0: *syntax.Lit {
. . . . . . . . . . . . . . ValuePos: 1:10
. . . . . . . . . . . . . . ValueEnd: 1:14
. . . . . . . . . . . . . . Value: "/\\\\/"
. . . . . . . . . . . . . }
. . . . . . . . . . . . }
. . . . . . . . . . . }
. . . . . . . . . . }
. . . . . . . . . . Names: 0x0
. . . . . . . . . . Exp: nil
. . . . . . . . . }
. . . . . . . . }
. . . . . . . }
. . . . . . }
. . . . . }
. . . . }
. . . }
. . . Position: 1:1
. . . Semicolon: 0:0
. . . Negated: false
. . . Background: false
. . . Coprocess: false
. . . Redirs: []*syntax.Redirect (len = 0) {}
. . }
. }
. Last: []syntax.Comment (len = 0) {}
}
Providing the double quoted parameter expression to expand.Document()
then results in a segfault, because paramExp
passes a nil Orig to Pattern, which then tries to reference fields from it.
Reproducer
package main
import (
"fmt"
"mvdan.cc/sh/v3/expand"
"mvdan.cc/sh/v3/syntax"
)
func main() {
expandConfig := expand.Config{}
expanded, _ := expand.Document(&expandConfig, &syntax.Word{
Parts: []syntax.WordPart{
&syntax.DblQuoted{
Parts: []syntax.WordPart{
&syntax.ParamExp{
Param: &syntax.Lit{Value: "v"},
Repl: &syntax.Replace{
All: true,
With: &syntax.Word{
Parts: []syntax.WordPart{
&syntax.Lit{
Value: "/\\\\/",
},
},
},
},
},
},
},
},
})
fmt.Printf("expanded: %#v\n", expanded)
}
Error:
panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x0 pc=0x4bcae6]
goroutine 1 [running]:
mvdan.cc/sh/v3/expand.Pattern(0x0?, 0x0)
/home/kevin/go/pkg/mod/mvdan.cc/sh/v3@v3.8.0/expand/expand.go:214 +0x26
mvdan.cc/sh/v3/expand.(*Config).paramExp(0xc00012c3c0, 0xc00007e1e0)
/home/kevin/go/pkg/mod/mvdan.cc/sh/v3@v3.8.0/expand/param.go:204 +0x13fa
mvdan.cc/sh/v3/expand.(*Config).wordField(0xc00012c3c0, {0xc000016150?, 0x1, 0x0?}, 0x1)
/home/kevin/go/pkg/mod/mvdan.cc/sh/v3@v3.8.0/expand/expand.go:544 +0x4dd
mvdan.cc/sh/v3/expand.(*Config).wordField(0xc00012c3c0, {0xc000129f10?, 0x1, 0x10?}, 0x1)
/home/kevin/go/pkg/mod/mvdan.cc/sh/v3@v3.8.0/expand/expand.go:535 +0x5df
mvdan.cc/sh/v3/expand.Document(0x461506?, 0xc000129f20)
/home/kevin/go/pkg/mod/mvdan.cc/sh/v3@v3.8.0/expand/expand.go:197 +0x3f
main.main()
/home/kevin/tmp/test/main.go:13 +0x25c
exit status 2
Running this in other shells (bash/ash) works:
$ v=a/b/c
$ echo ${v////\\/}
a\/b\/c
mvdan commented
Thanks for the detailed report!