honnef.co/go/tools@v0.5.0-0.dev.0.20240520180541-dcae280a5e87/staticcheck/sa1023/sa1023.go (about) 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.Documentation{ 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 }