Skip to content

Commit

Permalink
fix: set frame level in destination nodes to avoid memory corruption (t…
Browse files Browse the repository at this point in the history
…raefik#733)

When operations write their result to a non-local frame, the node
level field must be set accordingly, otherwise they attempt to write
in the wrong frame.

Fixes traefik#730.
  • Loading branch information
mvertes authored and james-lawrence committed Jul 2, 2020
1 parent 9977ef6 commit 45731a6
Show file tree
Hide file tree
Showing 8 changed files with 120 additions and 32 deletions.
16 changes: 16 additions & 0 deletions _test/assign14.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package main

var optionsG map[string]string = nil

var roundG = 30

func main() {
dummy := roundG
roundG = dummy + 1
println(roundG)
println(optionsG == nil)
}

// Output:
// 31
// true
1 change: 1 addition & 0 deletions example/pkg/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pkg/
3 changes: 3 additions & 0 deletions example/pkg/_pkg12/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module github.com/foo/pkg

go 1.14
11 changes: 11 additions & 0 deletions example/pkg/_pkg12/pkg.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package pkg

import (
"fmt"
)

func NewSample() func() string {
return func() string {
return fmt.Sprintf("gomod!")
}
}
59 changes: 59 additions & 0 deletions example/pkg/pkg_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,65 @@ func TestPackages(t *testing.T) {
}
}

func TestModules(t *testing.T) {
testCases := []struct {
desc string
cwd string
options interp.Options
expected string
}{
{
desc: "gomods",
cwd: "./_pkg12/",
expected: "gomod!",
},
}

// nothing to test if its turned off
if os.Getenv("GO111MODULE") == "off" {
return
}

for _, test := range testCases {
test := test
t.Run(test.desc, func(t *testing.T) {
if test.cwd != "" {
prev, err := os.Getwd()
if err != nil {
t.Fatal(err)
}
if err := os.Chdir(test.cwd); err != nil {
t.Fatal(err)
}
// rollback.
defer func() { _ = os.Chdir(prev) }()
}

// Init go interpreter
i := interp.New(test.options)
i.Use(stdlib.Symbols) // Use binary standard library

// Load pkg from sources
if _, err := i.Eval(`import "github.com/foo/pkg"`); err != nil {
t.Fatal(err)
}

value, err := i.Eval(`pkg.NewSample()`)
if err != nil {
t.Fatal(err)
}

fn := value.Interface().(func() string)

msg := fn()

if msg != test.expected {
t.Errorf("Got %q, want %q", msg, test.expected)
}
})
}
}

func TestPackagesError(t *testing.T) {
testCases := []struct {
desc string
Expand Down
7 changes: 7 additions & 0 deletions interp/cfg.go
Original file line number Diff line number Diff line change
Expand Up @@ -520,6 +520,7 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
}
}
n.findex = dest.findex
n.level = dest.level

// Propagate type
// TODO: Check that existing destination type matches source type
Expand Down Expand Up @@ -749,6 +750,7 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
dest := n.anc.child[childPos(n)-n.anc.nright]
n.typ = dest.typ
n.findex = dest.findex
n.level = dest.level
case n.anc.kind == returnStmt:
// To avoid a copy in frame, if the result is to be returned, store it directly
// at the frame location reserved for output arguments.
Expand Down Expand Up @@ -804,6 +806,7 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
if len(n.child) > 0 {
l := n.lastChild()
n.findex = l.findex
n.level = l.level
n.val = l.val
n.sym = l.sym
n.typ = l.typ
Expand All @@ -818,6 +821,7 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
wireChild(n)
l := n.lastChild()
n.findex = l.findex
n.level = l.level
n.val = l.val
n.sym = l.sym
n.typ = l.typ
Expand Down Expand Up @@ -884,6 +888,7 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
n.gen = nop
n.typ = n.child[1].typ
n.findex = n.child[1].findex
n.level = n.child[1].level
n.val = n.child[1].val
n.rval = n.child[1].rval
} else {
Expand Down Expand Up @@ -1278,6 +1283,7 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
wireChild(n)
c := n.lastChild()
n.findex = c.findex
n.level = c.level
n.typ = c.typ
n.rval = c.rval

Expand Down Expand Up @@ -1744,6 +1750,7 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
dest := n.anc.child[childPos(n)-n.anc.nright]
n.typ = dest.typ
n.findex = dest.findex
n.level = dest.level
case n.anc.kind == returnStmt:
pos := childPos(n)
n.typ = sc.def.typ.ret[pos]
Expand Down
48 changes: 17 additions & 31 deletions interp/src.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package interp

import (
"fmt"
"go/build"
"io/ioutil"
"os"
"path/filepath"
Expand All @@ -26,17 +27,14 @@ func (interp *Interpreter) importSrc(rPath, path string) (string, error) {
rPath = "."
}
dir = filepath.Join(filepath.Dir(interp.Name), rPath, path)
} else {
root, err := interp.rootFromSourceLocation(rPath)
if err != nil {
return "", err
}
if dir, rPath, err = pkgDir(interp.context.GOPATH, root, path); err != nil {
return "", err
}
} else if dir, rPath, err = pkgDir(&interp.context, rPath, path); err != nil {
return "", err
}

if interp.rdir[path] {
// BUG: importing source can trigger panics, which leaves
// interp.rdir in a dirty state causing this error to be incorrect
// if the package failed to be imported.
return "", fmt.Errorf("import cycle not allowed\n\timports %s", path)
}
interp.rdir[path] = true
Expand Down Expand Up @@ -144,44 +142,32 @@ func (interp *Interpreter) importSrc(rPath, path string) (string, error) {
return pkgName, nil
}

func (interp *Interpreter) rootFromSourceLocation(rPath string) (string, error) {
sourceFile := interp.Name
if rPath != "main" || !strings.HasSuffix(sourceFile, ".go") {
return rPath, nil
}
wd, err := os.Getwd()
if err != nil {
return "", err
}
pkgDir := filepath.Join(wd, filepath.Dir(sourceFile))
root := strings.TrimPrefix(pkgDir, filepath.Join(interp.context.GOPATH, "src")+"/")
if root == wd {
return "", fmt.Errorf("package location %s not in GOPATH", pkgDir)
}
return root, nil
}

// pkgDir returns the absolute path in filesystem for a package given its name and
// the root of the subtree dependencies.
func pkgDir(goPath string, root, path string) (string, string, error) {
func pkgDir(ctx *build.Context, root, path string) (pdir string, proot string, err error) {
rPath := filepath.Join(root, "vendor")
dir := filepath.Join(goPath, "src", rPath, path)

dir := filepath.Join(ctx.GOPATH, "src", rPath, path)
if _, err := os.Stat(dir); err == nil {
return dir, rPath, nil // found!
}

dir = filepath.Join(goPath, "src", effectivePkg(root, path))

dir = filepath.Join(ctx.GOPATH, "src", effectivePkg(root, path))
if _, err := os.Stat(dir); err == nil {
return dir, root, nil // found!
}

if len(root) == 0 {
// for backwards compatibility behavior only use the 'normal' go
// package location when current implementation fails to discover
// the source.
if pkg, err := ctx.Import(path, ".", build.FindOnly); err == nil {
return pkg.Dir, pkg.Root, nil
}

return "", "", fmt.Errorf("unable to find source related to: %q", path)
}

return pkgDir(goPath, previousRoot(root), path)
return pkgDir(ctx, previousRoot(root), path)
}

// Find the previous source root (vendor > vendor > ... > GOPATH).
Expand Down
7 changes: 6 additions & 1 deletion interp/src_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import (
"os"
"path/filepath"
"testing"

"go/build"
)

func Test_effectivePkg(t *testing.T) {
Expand Down Expand Up @@ -178,7 +180,10 @@ func Test_pkgDir(t *testing.T) {
}
}

dir, rPath, err := pkgDir(goPath, test.root, test.path)
bd := build.Default
bd.GOPATH = goPath

dir, rPath, err := pkgDir(&bd, test.root, test.path)
if err != nil {
t.Fatal(err)
}
Expand Down

0 comments on commit 45731a6

Please sign in to comment.