[tor-bugs] #33367 [Circumvention/Snowflake]: Snowflake server using 1.5 GB memory, preventing other allocations

Tor Bug Tracker & Wiki blackhole at torproject.org
Tue Feb 18 21:19:06 UTC 2020


#33367: Snowflake server using 1.5 GB memory, preventing other allocations
-------------------------------------+------------------------------
 Reporter:  dcf                      |          Owner:  (none)
     Type:  defect                   |         Status:  needs_review
 Priority:  Medium                   |      Milestone:
Component:  Circumvention/Snowflake  |        Version:
 Severity:  Normal                   |     Resolution:
 Keywords:                           |  Actual Points:
Parent ID:                           |         Points:
 Reviewer:                           |        Sponsor:
-------------------------------------+------------------------------
Changes (by dcf):

 * status:  new => needs_review


Comment:

 Replying to [comment:2 dcf]:
 > Initially I suspected the recent websocketconn changes from #33144, but
 those can only have had effect since 2020-02-10 18:57 when the server was
 restarted, and the earliest reports of "Could not connect to the bridge"
 predate that

 Well, there is a leak in the new websocketconn code anyway. You can see it
 with this patch. The `"Close pw1"` line runs but the `"Close pr2"` line
 does not, leaking a goroutine.
 {{{#!diff
 diff --git a/common/websocketconn/websocketconn.go
 b/common/websocketconn/websocketconn.go
 index 46bb977..0fb7fa9 100644
 --- a/common/websocketconn/websocketconn.go
 +++ b/common/websocketconn/websocketconn.go
 @@ -3,4 +3,5 @@ package websocketconn
  import (
         "io"
 +       "log"
         "time"

 @@ -106,8 +107,10 @@ func New(ws *websocket.Conn) *Conn {
         go func() {
                 pw1.CloseWithError(closeErrorToEOF(readLoop(pw1, ws)))
 +               log.Printf("%p Close pw1", ws)
         }()
         pr2, pw2 := io.Pipe()
         go func() {
                 pr2.CloseWithError(closeErrorToEOF(writeLoop(ws, pr2)))
 +               log.Printf("%p Close pr2", ws)
         }()
         return &Conn{
 }}}

 https://gitweb.torproject.org/pluggable-
 transports/snowflake.git/tree/common/websocketconn/websocketconn.go?id=ca9ae12c383405bc9a755e1bc902e9755495c1f1#n106
 {{{#!go
         pr1, pw1 := io.Pipe()
         go func() {
                 pw1.CloseWithError(closeErrorToEOF(readLoop(pw1, ws)))
         }()
         pr2, pw2 := io.Pipe()
         go func() {
                 pr2.CloseWithError(closeErrorToEOF(writeLoop(ws, pr2)))
         }()
         return &Conn{
                 Conn:   ws,
                 Reader: pr1,
                 Writer: pw2,
         }
 }}}

 The first goroutine reads messages from the WebSocket `ws` and writes them
 to `pw1`, which causes them to be returned from the `Conn`'s `Read` method
 (`pr1` is the `Reader` and is the other end of the `pw1` pipe). This part
 is fine.

 The second goroutine reads from `pr2` and writes to the WebSocket `ws`.
 `pr2` gets its input from things written using the `Conn`'s `Write`
 method, which feeds directly into `pw2`.

 Problem is, there's nothing that ever closes `pw2`. The `Close` method
 closes the WebSocket, so if the second goroutine ever ''were'' to try to
 write to it, it would detect an error and exit. But as long as nothing
 further ever calls `Write`, nothing is written to `pw2` and so the
 goroutine waits forever.

 Here's a patch that closes the internal `Pipe`s when the `Close` method is
 called, and a test case that fails before the patch and does not fail
 after the patch.
 https://gitweb.torproject.org/user/dcf/snowflake.git/commit/?h=bug33367-websocketconn&id=380b133155ad725126bc418d0e66b3c550b4c555

--
Ticket URL: <https://trac.torproject.org/projects/tor/ticket/33367#comment:5>
Tor Bug Tracker & Wiki <https://trac.torproject.org/>
The Tor Project: anonymity online


More information about the tor-bugs mailing list