@@ -39,7 +39,7 @@ return after a call to http.Error
3939 // Code that assumes the error was properly handled.
4040 slurp, _ := ioutil.ReadAll(res.Body)
4141
42- This checker helps uncover latent nil dereference bugs by reporting a
42+ This checker helps uncover latent nil dereference bugs, or security problems by reporting a
4343diagnostic for such mistakes.`
4444
4545var Analyzer = & analysis.Analyzer {
@@ -88,22 +88,6 @@ func run(pass *analysis.Pass) (interface{}, error) {
8888 // Let's inspect its control flow graph.
8989 acfg := cfgs .FuncDecl (fnDecl )
9090
91- explorable := make (map [int32 ]bool )
92- for _ , block := range acfg .Blocks {
93- explorable [block .Index ] = true
94- }
95- // Partition the graph, by deleting the roots accessible by the
96- // entry block, so that the two roots can never be connected.
97- for _ , block := range acfg .Blocks [0 ].Succs {
98- delete (explorable , block .Index )
99- }
100-
101- // Now that the roots are deleted, we can build the incidence graph with no more problems.
102- partitionedButConnectedBlocks := make (incidence )
103- for _ , block := range acfg .Blocks [0 ].Succs {
104- buildIncidence (block , explorable , partitionedButConnectedBlocks )
105- }
106-
10791 for _ , block := range acfg .Blocks {
10892 if retStmt := block .Return (); retStmt != nil {
10993 continue
@@ -129,7 +113,7 @@ func run(pass *analysis.Pass) (interface{}, error) {
129113 ident = t .Fun .(* ast.Ident )
130114 }
131115
132- if ident != nil && identMatches (pass , ident , "http.Error" ) {
116+ if ident != nil && identMatches (pass , ident , responseWriterRepliers ... ) {
133117 firstHTTPErrorIndex = i
134118 break
135119 }
@@ -140,6 +124,7 @@ func run(pass *analysis.Pass) (interface{}, error) {
140124 continue
141125 }
142126
127+ var succs []* cfg.Block
143128 tillEndOfBlock := block .Nodes [firstHTTPErrorIndex + 1 :]
144129 // First attempt is to try to find any terminating statements in the same block as the
145130 // http.Error statement, for example:
@@ -155,38 +140,29 @@ func run(pass *analysis.Pass) (interface{}, error) {
155140 }
156141 }
157142
158- // The last attempt is to find partitioned but connected blocks that
159- // might have return statements or terminating statements in them.
160- // In this case, let's retrieve all the block indices accessible after we fall through
161- // this block or in the next/larger scope e.g.
162- // if errors.Is(err, os.ErrNotExist) {
163- // http.NotFound(rw, req)
164- // } else {
165- // http.Error(rw, "cannot load archive", 500)
166- // }
167- // return
143+ // Check if the successors don't have terminating statements.
144+ succs = append (succs , block .Succs ... )
145+ for len (succs ) > 0 {
146+ succ := succs [0 ]
147+ succs = succs [1 :]
168148
169- for _ , index := range partitionedButConnectedBlocks [block .Index ] {
170- // Does the block have a return statement.
171- blocksToExplore := []* cfg.Block {acfg .Blocks [index ]}
172- if false {
173- for _ , subIndex := range partitionedButConnectedBlocks [index ] {
174- blocksToExplore = append (blocksToExplore , acfg .Blocks [subIndex ])
175- }
176- }
177- for _ , cBlock := range blocksToExplore {
178- if cBlock .Return () != nil {
149+ for _ , node := range succ .Nodes {
150+ switch node .(type ) {
151+ case * ast.ReturnStmt :
179152 goto done
180- }
181- // Now check if any of the nodes in there have terminating statements.
182- for _ , node := range cBlock . Nodes {
153+ case * ast. DeferStmt :
154+ continue
155+ default :
183156 if isTerminatingStmt (pass , node ) {
184157 goto done
185158 }
159+ goto failed
186160 }
187161 }
162+ succs = append (succs , succ .Succs ... )
188163 }
189164
165+ failed:
190166 // We did not find a terminating statement in this block.
191167 pass .ReportRangef (block .Nodes [firstHTTPErrorIndex ], "call to http.Error without a terminating statement below it" )
192168 done:
@@ -195,6 +171,8 @@ func run(pass *analysis.Pass) (interface{}, error) {
195171 return nil , nil
196172}
197173
174+ var responseWriterRepliers = []string {"http.Error" , "http.NotFound" }
175+
198176// Check that the function arguments contain:
199177// http.ResponseWriter
200178func responseWriterInParams (pass * analysis.Pass , fnDecl * ast.FuncDecl ) bool {
@@ -208,19 +186,6 @@ func responseWriterInParams(pass *analysis.Pass, fnDecl *ast.FuncDecl) bool {
208186 return false
209187}
210188
211- type incidence map [int32 ][]int32
212-
213- // buildIncidence traverses a block's successive neighbors, using explorable as a guide
214- // for a well partitioned directed graph.
215- func buildIncidence (discover * cfg.Block , explorable map [int32 ]bool , ind incidence ) {
216- for _ , succ := range discover .Succs {
217- if explorable [succ .Index ] {
218- ind [discover .Index ] = append (ind [discover .Index ], succ .Index )
219- buildIncidence (succ , explorable , ind )
220- }
221- }
222- }
223-
224189func isTerminatingStmt (pass * analysis.Pass , n ast.Node ) bool {
225190 if n == nil {
226191 return false
0 commit comments