Skip to content

x/tools/go/ssa: wrong base pointer of loadconstraint when handling *ssa.Next in genInstr()@gen.go #45735

@april1989

Description

@april1989

What version of Go are you using (go version)?

$ go version 1.16.3

Does this issue reproduce with the latest release?

Yes.

What operating system and processor architecture are you using (go env)?

go env Output
$ go env
GOHOSTARCH="amd64"
GOHOSTOS="darwin"

What did you do?

Run go pointer analysis under the directory /grpc-go/examples/features/xds/client (master with commit e38032e927812bb354297adcab933bedeff6c177) to analyze the main.go file. I do not think the commit is a necessary, probably the code from recent master is also able to reproduce the issue. I used the program below to run pointer analysis:

func main() {
	cfg := &packages.Config{
		Mode:  packages.LoadAllSyntax, // the level of information returned for each package
		Dir:   "",                     // directory in which to run the build system's query tool
		Tests: false,                  // setting Tests will include related test packages
	}
	initial, err := packages.Load(cfg)
	if err != nil {
		panic(fmt.Sprintln(err))
	}
	prog, pkgs := ssautil.AllPackages(initial, 0)
	prog.Build()
	mains, err := findMainPackages(pkgs)
	if err != nil {
		return
	}

	logfile, _ := os.Create("/Users/bozhen/Documents/GO2/default-x-tools/_logs/my_log_0") //replace with your log
	config := &pointer.Config{
		Mains:          mains,
		BuildCallGraph: true,
		Reflection:     false,
		Log:            logfile,
	}

	result, err := pointer.Analyze(config)
	if err != nil {
		panic(err) // internal error in pointer analysis
	}
	cg := result.CallGraph
	fmt.Println(cg)
}

func findMainPackages(pkgs []*ssa.Package) ([]*ssa.Package, error) {
	var mains []*ssa.Package
	for _, p := range pkgs {
		if p.Pkg.Name() == "main" && p.Func("main") != nil {
			mains = append(mains, p)
		}
	}
	if len(mains) == 0 { 
		return nil, fmt.Errorf("no main packages")
	}
	return mains, nil
}

What did you expect to see?

In the log, the constraints generated for IR statementt40 = next t34 in function (*google.golang.org/grpc/xds/internal/balancer/edsbalancer.balancerGroup).close should be:

; t40 = next t34
	localobj[t33] = n0
	load n319631 <- n319625[4]

where n319631 (the base pointer of the load) is from "Creating nodes for local values":

create n319629 bool for t40#0
create n319630 invalid type for t40#1
create n319631 *google.golang.org/grpc/xds/internal/balancer/edsbalancer.subBalancerWithConfig for t40#2
val[t40] = n319629  (*ssa.Next)

What did you see instead?

Instead, the generated constraints are:

; t40 = next t34
	localobj[t33] = n0
	load n319634 <- n319625[4]

where n319634 is from a irrelevant local variable at the begging of this function:

; *t4 = false:bool
	create n319634 bool for false:bool
	globalobj[false:bool] = n0
	val[false:bool] = n319634  (*ssa.Const)
	store n319598[0] <- n319634

I copied the grpc function here with which source code the above two IR statements mapped to:

func (bg *balancerGroup) close() {
	bg.incomingMu.Lock()
	if bg.incomingStarted { // !! this maps to IR: *t4 = false:bool
		bg.incomingStarted = false

		for _, pState := range bg.idToPickerState {
			// Reset everything to IDLE but keep the entry in map (to keep the
			// weight).
			pState.picker = nil
			pState.state = connectivity.Idle
		}

		// Also remove all SubConns.
		for sc := range bg.scToSubBalancer {
			bg.cc.RemoveSubConn(sc)
			delete(bg.scToSubBalancer, sc)
		}
	}
	bg.incomingMu.Unlock()

	bg.outgoingMu.Lock()
	if bg.outgoingStarted {
		bg.outgoingStarted = false
		for _, config := range bg.idToBalancerConfig { // !! this maps to IR: t40 = next t34
			config.stopBalancer()
		}
	}
	bg.outgoingMu.Unlock()
	// Clear(true) runs clear function to close sub-balancers in cache. It
	// must be called out of outgoing mutex.
	bg.balancerCache.Clear(true)
}

So, why is the node for t40 replaced by the node for t4 in the generated constraints, if my understanding is correct?

I checked the source code a little bit, found that the wrong mapping is because of the computation of odst += ksize when generating constraints for *ssa.Next and the key type is invalid (the code is at go/pointer/gen.go:1078 of the most recent commit when I submit this issue). Should not this be odst += 1? Because the algorithm only creates three pointers/nodes for this *ssa.Next instruction: ok, key and value. There is no flatten copy for key or value pointers. So, if the key type is invalid, shouldn't it skip this key node and use the value node (+= 1) as the load base, since the value will be extract later?

Thanks.

Metadata

Metadata

Assignees

No one assigned

    Labels

    AnalysisIssues related to static analysis (vet, x/tools/go/analysis)FrozenDueToAgeNeedsInvestigationSomeone must examine and confirm this is a valid issue and not a duplicate of an existing one.ToolsThis label describes issues relating to any tools in the x/tools repository.

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions