sa1023.go raw

   1  package sa1023
   2  
   3  import (
   4  	"go/types"
   5  
   6  	"honnef.co/go/tools/analysis/lint"
   7  	"honnef.co/go/tools/analysis/report"
   8  	"honnef.co/go/tools/go/ir"
   9  	"honnef.co/go/tools/go/ir/irutil"
  10  	"honnef.co/go/tools/internal/passes/buildir"
  11  	"honnef.co/go/tools/knowledge"
  12  
  13  	"golang.org/x/tools/go/analysis"
  14  )
  15  
  16  var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{
  17  	Analyzer: &analysis.Analyzer{
  18  		Name:     "SA1023",
  19  		Run:      run,
  20  		Requires: []*analysis.Analyzer{buildir.Analyzer},
  21  	},
  22  	Doc: &lint.RawDocumentation{
  23  		Title:    `Modifying the buffer in an \'io.Writer\' implementation`,
  24  		Text:     `\'Write\' must not modify the slice data, even temporarily.`,
  25  		Since:    "2017.1",
  26  		Severity: lint.SeverityError,
  27  		MergeIf:  lint.MergeIfAny,
  28  	},
  29  })
  30  
  31  var Analyzer = SCAnalyzer.Analyzer
  32  
  33  func run(pass *analysis.Pass) (interface{}, error) {
  34  	// TODO(dh): this might be a good candidate for taint analysis.
  35  	// Taint the argument as MUST_NOT_MODIFY, then propagate that
  36  	// through functions like bytes.Split
  37  
  38  	for _, fn := range pass.ResultOf[buildir.Analyzer].(*buildir.IR).SrcFuncs {
  39  		sig := fn.Signature
  40  		if fn.Name() != "Write" || sig.Recv() == nil {
  41  			continue
  42  		}
  43  		if !types.Identical(sig, knowledge.Signatures["(io.Writer).Write"]) {
  44  			continue
  45  		}
  46  
  47  		for _, block := range fn.Blocks {
  48  			for _, ins := range block.Instrs {
  49  				switch ins := ins.(type) {
  50  				case *ir.Store:
  51  					addr, ok := ins.Addr.(*ir.IndexAddr)
  52  					if !ok {
  53  						continue
  54  					}
  55  					if addr.X != fn.Params[1] {
  56  						continue
  57  					}
  58  					report.Report(pass, ins, "io.Writer.Write must not modify the provided buffer, not even temporarily")
  59  				case *ir.Call:
  60  					if !irutil.IsCallTo(ins.Common(), "append") {
  61  						continue
  62  					}
  63  					if ins.Common().Args[0] != fn.Params[1] {
  64  						continue
  65  					}
  66  					report.Report(pass, ins, "io.Writer.Write must not modify the provided buffer, not even temporarily")
  67  				}
  68  			}
  69  		}
  70  	}
  71  	return nil, nil
  72  }
  73