[PATCH] ws: get a new mask for each new outgoing frame
authorDaniel Stenberg <daniel@haxx.se>
Mon, 8 Sep 2025 12:14:15 +0000 (14:14 +0200)
committerSamuel Henrique <samueloph@debian.org>
Fri, 8 May 2026 14:16:21 +0000 (07:16 -0700)
Reported-by: Calvin Ruocco
Closes #18496

Backported-by: Samuel Henrique <samueloph@debian.org>
Backported-by: Alex <alex@puer-robustus.eu>
Changes:
* Refresh patch context for lib/ws.c
* Adapt return value to current function return type

Backported by: Samuel Henrique <samueloph@debian.org>

Changes:
* Apply the per-frame mask generation in ws_packethead() (bookworm
  7.88.1) rather than upstream's ws_enc_add_frame() (and trixie's
  ws_enc_write_head()), since the bookworm code path that builds the
  outgoing frame header is structurally different. On Curl_rand()
  failure return 0 -- ws_packethead() already uses 0 as the
  soft-error sentinel for buffer-too-small.
* Preserve the DEBUGBUILD CURL_WS_FORCE_ZERO_MASK escape hatch
  alongside the new per-frame mask generation, matching upstream.
* Remove the corresponding one-time mask generation in
  Curl_ws_accept(), and initialise `result` to CURLE_OK at
  declaration since it is no longer unconditionally assigned by the
  removed Curl_rand() call.

Gbp-Pq: Name CVE-2025-10148.patch

lib/ws.c

index 0fc5e56aee90cc5b23d37d155f50ea262eb7c8e9..992357ca64c47060504717d781c4c4ae33fba4e0 100644 (file)
--- a/lib/ws.c
+++ b/lib/ws.c
@@ -127,7 +127,7 @@ CURLcode Curl_ws_accept(struct Curl_easy *data,
   struct connectdata *conn = data->conn;
   struct websocket *wsp = &data->req.p.http->ws;
   struct ws_conn *wsc = &conn->proto.ws;
-  CURLcode result;
+  CURLcode result = CURLE_OK;
 
   /* Verify the Sec-WebSocket-Accept response.
 
@@ -148,13 +148,7 @@ CURLcode Curl_ws_accept(struct Curl_easy *data,
      subprotocol not requested by the client), the client MUST Fail
      the WebSocket Connection. */
 
-  /* 4 bytes random */
-  result = Curl_rand(data, (unsigned char *)&ws->ws.mask, sizeof(ws->ws.mask));
-  if(result)
-    return result;
-
-  infof(data, "Received 101, switch to WebSocket; mask %02x%02x%02x%02x",
-        ws->ws.mask[0], ws->ws.mask[1], ws->ws.mask[2], ws->ws.mask[3]);
+  infof(data, "Received 101, switch to WebSocket");
   Curl_dyn_init(&wsc->early, data->set.buffer_size);
   if(nread) {
     result = Curl_dyn_addn(&wsc->early, mem, nread);
@@ -628,6 +622,20 @@ static size_t ws_packethead(struct Curl_easy *data,
         firstbyte);
   infof(data, "WS: send payload len %u", (int)len);
 
+  /* 4 bytes random */
+  {
+    CURLcode result = Curl_rand(data, (unsigned char *)&ws->ws.mask,
+                                sizeof(ws->ws.mask));
+    if(result)
+      return 0;
+  }
+
+#ifdef DEBUGBUILD
+  if(getenv("CURL_WS_FORCE_ZERO_MASK"))
+    /* force the bit mask to 0x00000000, effectively disabling masking */
+    memset(&ws->ws.mask, 0, sizeof(ws->ws.mask));
+#endif
+
   /* 4 bytes mask */
   memcpy(&out[outi], &ws->ws.mask, 4);