Skip to content

Patch storage1.59#13822

Open
cpriti-os wants to merge 2 commits intomainfrom
patch-storage1.59
Open

Patch storage1.59#13822
cpriti-os wants to merge 2 commits intomainfrom
patch-storage1.59

Conversation

@cpriti-os
Copy link
Copy Markdown
Contributor

No description provided.

PR created by the Librarian CLI to initialize a release. Merging this PR
will auto trigger a release.

Librarian Version: v0.8.0
Language Image:
us-central1-docker.pkg.dev/cloud-sdk-librarian-prod/images-prod/librarian-go@sha256:718167d5c23ed389b41f617b3a00ac839bdd938a6bd2d48ae0c2f1fa51ab1c3d
<details><summary>storage: 1.59.2</summary>

##
[1.59.2](storage/v1.59.1...storage/v1.59.2)
(2026-01-28)

### Bug Fixes

* deadlock in event loop while coordinating channels (#13652)
([ff6c8e7](ff6c8e78))

</details>
@cpriti-os cpriti-os requested a review from a team February 10, 2026 06:57
@cpriti-os cpriti-os requested review from a team as code owners February 10, 2026 06:57
Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request updates the storage module to version 1.59.2 and introduces an internal request queue in the gRPC multi-range downloader to address a deadlock and prevent blocking the event loop. However, the implementation of the queue limit in the eventLoop introduces a potential deadlock scenario where the manager could become unresponsive if the queue fills up before a session is established. It is recommended to refine the command processing logic to ensure administrative commands can still be processed. Additionally, there are minor suggestions to correct a typo in the changelog date and simplify code in grpc_reader_multi_range.go by removing a redundant check.

Comment on lines +394 to 409
// Only read from cmds if we have space in the unsentRequests queue.
var cmdsChan chan mrdCommand
if m.unsentRequests.Len() < mrdAddInternalQueueMaxSize {
cmdsChan = m.cmds
}
select {
case <-m.ctx.Done():
return
case cmd := <-m.cmds:
// This path only triggers if space is available in the channel.
// It never blocks the eventLoop.
case targetChan <- nextReq:
m.unsentRequests.RemoveFront()
case cmd := <-cmdsChan:
cmd.apply(m.ctx, m)
if _, ok := cmd.(*mrdCloseCmd); ok {
return
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

security-high high

The eventLoop implementation contains a logic flaw that can lead to a permanent deadlock. When the unsentRequests queue reaches its maximum capacity (mrdAddInternalQueueMaxSize = 50000), the cmdsChan is set to nil (line 397). If at the same time m.currentSession is nil (e.g., at startup or after a session failure), the targetChan also remains nil (line 385). In Go, a select statement with only nil channels (except for the context's Done() channel) will block indefinitely. This prevents the eventLoop from processing any further commands, including those that would initialize a session to drain the queue or even a command to close the downloader. This results in a Denial of Service for the MultiRangeDownloader component.

Comment thread storage/CHANGES.md
# Changes


## [1.59.2](https://github.com/googleapis/google-cloud-go/releases/tag/storage%2Fv1.59.2) (2026-01-28)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The date (2026-01-28) appears to be a typo, as it is several years in the future. Please correct the year.

Comment on lines +388 to +393
if m.unsentRequests.Len() > 0 && m.currentSession != nil {
nextReq = m.unsentRequests.Front()
if nextReq != nil {
targetChan = m.currentSession.reqC
}
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The check if nextReq != nil is redundant. If m.unsentRequests.Len() > 0, then m.unsentRequests.Front() is guaranteed not to return nil. You can simplify this block for better clarity.

		if m.unsentRequests.Len() > 0 && m.currentSession != nil {
			nextReq = m.unsentRequests.Front()
			targetChan = m.currentSession.reqC
		}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant