Fix DoS: nil-pointer dereference in Account.send after upload#9
Open
thistehneisen wants to merge 1 commit into
Open
Fix DoS: nil-pointer dereference in Account.send after upload#9thistehneisen wants to merge 1 commit into
thistehneisen wants to merge 1 commit into
Conversation
processUploads (upload.go:413) calls a.send("upload-processed", ...) from a
goroutine spawned in uploadHandler (upload.go:253). If the user disconnects
before processing completes, listen()'s deferred teardown has already nilled
a.Session.Websocket, and the existing `if a.Session != nil` guard does not
catch that — WriteMessage segfaults and crashes the whole service.
Repro (100% reliable, no race tuning):
1. signup, open WS to populate online[]
2. POST /upload
3. close WS (close tab, network drop)
4. processUploads finishes -> a.send() -> SIGSEGV in
github.com/gorilla/websocket.(*Conn).WriteMessage
Fix:
* Re-check a.Session.Websocket != nil inside send() while holding
WebsocketMutex; bail out cleanly if the connection is gone.
* Take WebsocketMutex around the Close/nil-out in listen()'s deferred
teardown so we never read a half-initialised state.
* Also early-return on a nil Session up front; the previous code touched
a.Session.IP in the json.Marshal error log before the nil check.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fix DoS via nil-pointer dereference in
Account.sendafter uploadReported by: OffSeq — Nils Putniņš
<npu@offseq.com>Affected version: v2.6.1 (
a2a794ef6916d16da3fcc2f9f8d86225482e7263)What this PR fixes
Any user who uploads a file via
/uploadand then disconnects beforeprocessUploadsfinishes triggers anildereference insidegorilla/websocket.(*Conn).WriteMessage. The goroutine spawned atupload.go:253has norecover(), so the panic terminates the entireGraphoscope process. All other signed-in users lose their sessions.
Reproduction is 100% reliable — not race-window dependent.
Root cause
upload.go:413callsa.send("upload-processed", filename, "")from abackground goroutine.
Account.send(websocket.go) guards only againsta.Session == nil, but on disconnectlisten()runs its deferred teardown:…which leaves
a.Session != nilbuta.Session.Websocket == nil. Thesubsequent
a.Session.Websocket.WriteMessage(...)insend()segfaults.Additionally, the assignment at line 105 was not protected by
WebsocketMutex, so even a basic nil-check insend()would still racewith the teardown.
The fix
Account.send:a.Session == nilbefore touchinga.Session.IP(the previous error path dereferenced it in a log line).
a.Session.Websocket != nilinside theWebsocketMutexcritical section — that's where
listen()writes to it.Account.listen's deferred teardown:WebsocketMutexaroundClose()+Websocket = nilso the"write side" never observes a half-initialised state.
Diff is 38 lines, single file (
websocket.go).Repro (PoC)
Panic stack trace (pre-patch)
Post-patch verification
Ran the same PoC against a binary built from this branch. Service log shows
the normal "Websocket connection closed by client" → "Processed file added
to the downloads list" sequence and
curl /signinreturns 200 throughout.Out of scope (follow-ups)
While reproducing this we noticed related issues that aren't covered by
this PR — happy to file them separately:
fatal error: concurrent map writesononline[](websocket.go:23,mutated at
:91/:106, read inbroadcast()andupload.go:98).Reproducible from ~20 parallel WS upgrades. Suggested fix:
sync.RWMutexorsync.Map.uploadHandler.func1goroutine has norecover()— any panic froma downstream plugin (e.g. MISP
panic(err)patterns) still crashes theservice. Suggested fix: top-level
defer recover()in every goroutinespawned from request handlers.
context.Contextfrom the HTTP handler intoprocessUploadsso that a client disconnect can cancel the in-flight work cleanly.
Nils Putniņš / npu[at]offseq[dot]com / OffSeq
https://offseq.com / https://radar.offseq.com