From 7d4f67ba7682eb8fb8ed80d1838370d840c5b774 Mon Sep 17 00:00:00 2001 From: denis-troller Date: Mon, 3 Nov 2025 10:31:09 +0000 Subject: [PATCH 1/3] Create rule S8253 --- rules/S8253/go/metadata.json | 25 ++++++++++++++++++++ rules/S8253/go/rule.adoc | 44 ++++++++++++++++++++++++++++++++++++ rules/S8253/metadata.json | 2 ++ 3 files changed, 71 insertions(+) create mode 100644 rules/S8253/go/metadata.json create mode 100644 rules/S8253/go/rule.adoc create mode 100644 rules/S8253/metadata.json diff --git a/rules/S8253/go/metadata.json b/rules/S8253/go/metadata.json new file mode 100644 index 00000000000..9a8601ed53b --- /dev/null +++ b/rules/S8253/go/metadata.json @@ -0,0 +1,25 @@ +{ + "title": "FIXME", + "type": "CODE_SMELL", + "status": "ready", + "remediation": { + "func": "Constant\/Issue", + "constantCost": "5min" + }, + "tags": [ + ], + "defaultSeverity": "Major", + "ruleSpecification": "RSPEC-8253", + "sqKey": "S8253", + "scope": "All", + "defaultQualityProfiles": ["Sonar way"], + "quickfix": "unknown", + "code": { + "impacts": { + "MAINTAINABILITY": "HIGH", + "RELIABILITY": "MEDIUM", + "SECURITY": "LOW" + }, + "attribute": "CONVENTIONAL" + } +} diff --git a/rules/S8253/go/rule.adoc b/rules/S8253/go/rule.adoc new file mode 100644 index 00000000000..7193b5561c7 --- /dev/null +++ b/rules/S8253/go/rule.adoc @@ -0,0 +1,44 @@ +FIXME: add a description + +// If you want to factorize the description uncomment the following line and create the file. +//include::../description.adoc[] + +== Why is this an issue? + +FIXME: remove the unused optional headers (that are commented out) + +//=== What is the potential impact? + +== How to fix it +//== How to fix it in FRAMEWORK NAME + +=== Code examples + +==== Noncompliant code example + +[source,go,diff-id=1,diff-type=noncompliant] +---- +FIXME +---- + +==== Compliant solution + +[source,go,diff-id=1,diff-type=compliant] +---- +FIXME +---- + +//=== How does this work? + +//=== Pitfalls + +//=== Going the extra mile + + +//== Resources +//=== Documentation +//=== Articles & blog posts +//=== Conference presentations +//=== Standards +//=== External coding guidelines +//=== Benchmarks diff --git a/rules/S8253/metadata.json b/rules/S8253/metadata.json new file mode 100644 index 00000000000..2c63c085104 --- /dev/null +++ b/rules/S8253/metadata.json @@ -0,0 +1,2 @@ +{ +} From 6d1817f060314a827131aecadcf851f8a2124216 Mon Sep 17 00:00:00 2001 From: denis-troller Date: Mon, 3 Nov 2025 11:45:39 +0100 Subject: [PATCH 2/3] Update rules/S8253/go/rule.adoc in PR #5829 --- rules/S8253/go/rule.adoc | 68 +++++++++++++++++++++++++++++----------- 1 file changed, 49 insertions(+), 19 deletions(-) diff --git a/rules/S8253/go/rule.adoc b/rules/S8253/go/rule.adoc index 7193b5561c7..ba460d55e1b 100644 --- a/rules/S8253/go/rule.adoc +++ b/rules/S8253/go/rule.adoc @@ -1,16 +1,31 @@ -FIXME: add a description - -// If you want to factorize the description uncomment the following line and create the file. -//include::../description.adoc[] +This is an issue when creating unbuffered channels that multiple goroutines will send to, especially when not all sends are guaranteed to have immediate receivers. == Why is this an issue? -FIXME: remove the unused optional headers (that are commented out) +Unbuffered channels require both a sender and receiver to be ready at the same time for communication to occur. When multiple goroutines send to an unbuffered channel without guaranteed receivers, some goroutines may block indefinitely on channel writes. + +This blocking prevents goroutines from completing their execution and being garbage collected. Since blocked goroutines hold references to the channel, the channel itself cannot be garbage collected either. Over time, this leads to goroutine leaks and memory consumption that grows without bounds. + +The problem is particularly common in patterns where: + +* Multiple goroutines are launched to perform work concurrently +* Results are sent back through a shared channel +* The main goroutine may return early (e.g., on first error) without reading all results + +In such scenarios, remaining goroutines will hang forever trying to send their results to a channel that no longer has receivers. -//=== What is the potential impact? +=== What is the potential impact? + +Goroutine leaks can cause serious memory issues in long-running applications. Each leaked goroutine consumes memory for its stack (typically 2KB initially, but can grow). In applications that repeatedly create such patterns, hundreds or thousands of goroutines may accumulate, leading to: + +* Increased memory usage that never decreases +* Potential out-of-memory errors +* Degraded application performance +* Resource exhaustion in containerized environments == How to fix it -//== How to fix it in FRAMEWORK NAME + +Buffer the channel with capacity equal to the number of goroutines that will send to it. This ensures all sends can complete without blocking, allowing goroutines to finish and be garbage collected. === Code examples @@ -18,27 +33,42 @@ FIXME: remove the unused optional headers (that are commented out) [source,go,diff-id=1,diff-type=noncompliant] ---- -FIXME +func Query(conns []Conn, query string) Result { + ch := make(chan Result) // Noncompliant + for _, conn := range conns { + go func(c Conn) { + ch <- c.DoQuery(query) // may block if no receiver ready + }(conn) + } + return <-ch +} ---- ==== Compliant solution [source,go,diff-id=1,diff-type=compliant] ---- -FIXME +func Query(conns []Conn, query string) Result { + ch := make(chan Result, len(conns)) // buffered channel + for _, conn := range conns { + go func(c Conn) { + ch <- c.DoQuery(query) // won't block + }(conn) + } + return <-ch +} ---- -//=== How does this work? +== Resources + +=== Documentation + + * Go Blog: Concurrency Patterns - https://go.dev/blog/go-concurrency-patterns-timing-out-and[Official Go blog post explaining channel buffering patterns and goroutine management] -//=== Pitfalls + * Effective Go: Channels - https://go.dev/doc/effective_go#channels[Official documentation on proper channel usage in Go] -//=== Going the extra mile + * Go Memory Model - https://go.dev/ref/mem[Specification of Go's memory model and channel synchronization guarantees] +=== Standards -//== Resources -//=== Documentation -//=== Articles & blog posts -//=== Conference presentations -//=== Standards -//=== External coding guidelines -//=== Benchmarks + * CWE-401: Missing Release of Memory after Effective Lifetime - https://cwe.mitre.org/data/definitions/401.html[Covers memory leaks caused by unreleased resources, including goroutine leaks] From 276550befd8685a80705f87c7c11a99f81824121 Mon Sep 17 00:00:00 2001 From: denis-troller Date: Mon, 3 Nov 2025 11:45:42 +0100 Subject: [PATCH 3/3] Update rules/S8253/go/metadata.json in PR #5829 --- rules/S8253/go/metadata.json | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/rules/S8253/go/metadata.json b/rules/S8253/go/metadata.json index 9a8601ed53b..0520aec9f94 100644 --- a/rules/S8253/go/metadata.json +++ b/rules/S8253/go/metadata.json @@ -1,25 +1,29 @@ { - "title": "FIXME", - "type": "CODE_SMELL", + "title": "Channels should be buffered to prevent goroutine blocking", + "type": "BUG", "status": "ready", "remediation": { - "func": "Constant\/Issue", - "constantCost": "5min" + "func": "Constant/Issue", + "constantCost": "5 min" }, "tags": [ + "concurrency", + "memory-leak", + "goroutine" ], - "defaultSeverity": "Major", + "defaultSeverity": "Blocker", "ruleSpecification": "RSPEC-8253", "sqKey": "S8253", - "scope": "All", - "defaultQualityProfiles": ["Sonar way"], + "scope": "Main", + "defaultQualityProfiles": [ + "Sonar way" + ], "quickfix": "unknown", "code": { "impacts": { - "MAINTAINABILITY": "HIGH", - "RELIABILITY": "MEDIUM", - "SECURITY": "LOW" + "RELIABILITY": "BLOCKER", + "MAINTAINABILITY": "BLOCKER" }, - "attribute": "CONVENTIONAL" + "attribute": "COMPLETE" } -} +} \ No newline at end of file