Defer in Loop
ID |
go.defer_in_loop |
Severity |
high |
Remediation Complexity |
trivial |
Remediation Risk |
low |
Remediation Effort |
low |
Resource |
Reliability |
Language |
Go |
Tags |
reliability, suspicious |
Description
Reports a defer statement whose nearest enclosing loop is inside the same function. A
deferred call is not executed at the end of the loop body — it is queued and only runs when
the enclosing function returns.
Rationale
A resource opened on every iteration and released with defer stays open until the whole
function finishes, because the deferred call never fires between iterations. Over a large or
unbounded loop this accumulates open file descriptors, held locks or connections and can
exhaust the process. A defer inside a closure that is itself defined inside the loop is not
reported — the closure owns its own defer scope, so its deferred calls run per closure
invocation, which is the correct per-iteration cleanup idiom.
for _, name := range names {
f, _ := os.Open(name)
defer f.Close() // FLAW — every file stays open until the function returns
}
for _, name := range names {
func() {
f, _ := os.Open(name)
defer f.Close() // OK — the closure releases the file each iteration
}()
}
Remediation
Release the resource at the end of the loop body with an explicit call, or wrap the
per-iteration work in a closure (or a helper function) so the defer fires when that closure
returns.