Skip to content

Commit

Permalink
fix(CodeBuilder): prevent infinite recursion in struct field lookup
Browse files Browse the repository at this point in the history
Added `visited` map to track already processed types in `findMember`,
`embeddedField` and `field` methods to prevent stack overflow when
dealing with circular struct embedding, fixing the issue reported in
goplus/builder#1199 (comment).

Signed-off-by: Aofei Sheng <aofei@aofeisheng.com>
  • Loading branch information
aofei committed Jan 14, 2025
1 parent dc8cddf commit a7e2c67
Show file tree
Hide file tree
Showing 2 changed files with 18 additions and 10 deletions.
2 changes: 1 addition & 1 deletion builtin.go
Original file line number Diff line number Diff line change
Expand Up @@ -1142,7 +1142,7 @@ func (p addableT) Match(pkg *Package, typ types.Type) bool {
// TODO: refactor
cb := pkg.cb
cb.stk.Push(elemNone)
kind := cb.findMember(typ, "Gop_Add", "", MemberFlagVal, &Element{}, nil)
kind := cb.findMember(typ, "Gop_Add", "", MemberFlagVal, &Element{}, nil, nil)
if kind != 0 {
cb.stk.PopN(1)
if kind == MemberMethod {
Expand Down
26 changes: 17 additions & 9 deletions codebuild.go
Original file line number Diff line number Diff line change
Expand Up @@ -1569,7 +1569,7 @@ func (p *CodeBuilder) Member(name string, flag MemberFlag, src ...ast.Node) (kin
flag = memberFlagMethodToFunc
}
aliasName, flag = aliasNameOf(name, flag)
kind = p.findMember(at, name, aliasName, flag, arg, srcExpr)
kind = p.findMember(at, name, aliasName, flag, arg, srcExpr, nil)
if isType && kind != MemberMethod {
code, pos := p.loadExpr(srcExpr)
return MemberInvalid, p.newCodeError(
Expand Down Expand Up @@ -1617,7 +1617,15 @@ func getUnderlying(pkg *Package, typ types.Type) types.Type {
}

func (p *CodeBuilder) findMember(
typ types.Type, name, aliasName string, flag MemberFlag, arg *Element, srcExpr ast.Node) MemberKind {
typ types.Type, name, aliasName string, flag MemberFlag, arg *Element, srcExpr ast.Node, visited map[types.Type]struct{}) MemberKind {
if visited == nil {
visited = make(map[types.Type]struct{})
}
if _, ok := visited[typ]; ok {
return MemberInvalid
}

Check warning on line 1626 in codebuild.go

View check run for this annotation

Codecov / codecov/patch

codebuild.go#L1625-L1626

Added lines #L1625 - L1626 were not covered by tests
visited[typ] = struct{}{}

var named *types.Named
retry:
switch o := typ.(type) {
Expand All @@ -1635,10 +1643,10 @@ retry:
return kind
}
if fstruc {
return p.embeddedField(struc, name, aliasName, flag, arg, srcExpr)
return p.embeddedField(struc, name, aliasName, flag, arg, srcExpr, visited)
}
case *types.Struct:
if kind := p.field(t, name, aliasName, flag, arg, srcExpr); kind != MemberInvalid {
if kind := p.field(t, name, aliasName, flag, arg, srcExpr, visited); kind != MemberInvalid {
return kind
}
}
Expand All @@ -1649,7 +1657,7 @@ retry:
}
goto retry
case *types.Struct:
if kind := p.field(o, name, aliasName, flag, arg, srcExpr); kind != MemberInvalid {
if kind := p.field(o, name, aliasName, flag, arg, srcExpr, visited); kind != MemberInvalid {
return kind
}
if named != nil {
Expand Down Expand Up @@ -1829,10 +1837,10 @@ func (p *CodeBuilder) normalField(
}

func (p *CodeBuilder) embeddedField(
o *types.Struct, name, aliasName string, flag MemberFlag, arg *Element, src ast.Node) MemberKind {
o *types.Struct, name, aliasName string, flag MemberFlag, arg *Element, src ast.Node, visited map[types.Type]struct{}) MemberKind {
for i, n := 0, o.NumFields(); i < n; i++ {
if fld := o.Field(i); fld.Embedded() {
if kind := p.findMember(fld.Type(), name, aliasName, flag, arg, src); kind != MemberInvalid {
if kind := p.findMember(fld.Type(), name, aliasName, flag, arg, src, visited); kind != MemberInvalid {
return kind
}
}
Expand All @@ -1841,11 +1849,11 @@ func (p *CodeBuilder) embeddedField(
}

func (p *CodeBuilder) field(
o *types.Struct, name, aliasName string, flag MemberFlag, arg *Element, src ast.Node) MemberKind {
o *types.Struct, name, aliasName string, flag MemberFlag, arg *Element, src ast.Node, visited map[types.Type]struct{}) MemberKind {
if kind := p.normalField(o, name, arg, src); kind != MemberInvalid {
return kind
}
return p.embeddedField(o, name, aliasName, flag, arg, src)
return p.embeddedField(o, name, aliasName, flag, arg, src, visited)
}

func toFuncSig(sig *types.Signature, recv *types.Var) *types.Signature {
Expand Down

0 comments on commit a7e2c67

Please sign in to comment.