Project homepage Mailing List  Warmcat.com  API Docs  Github Mirror 
    npro  
 Modern all-safe Rust Network Protocol library supporting h1, h2, h3, ws, wt sans-IO and with socket IO + tls
git clone https://npro.rs/repo/npro
 
root / src / virt / v-private.h
Author[]Andy Green <andy@warmcat.com> 2026-09-18 18:41 UTC
Committer[]Andy Green <andy@warmcat.com> 2026-09-18 18:41 UTC
Tree92b8109ea945f68e03e52c7ecad0294e4ab3673f   Raw Patch
 
server: bound builder loadreport reassembly and free it on close, fixes F-019
server: bound builder loadreport reassembly and free it on close, fixes F-019

Same defect class as F-017 on the builder link: fragmented loadreports
are stashed in pss->onward_reassembly until the message completes, with
no cap on the total buffered bytes and no destroy of a half-filled
buflist when the connection closes.  A builder that sends a message
header and endless continuation fragments grows sai-server's heap
without bound; a builder conn closed mid-loadreport leaks its buflist.

Use sais_buflist_append_bounded() (shared with the power-link fix) with
a 64KiB cap at both append sites and drop the connection over it or on
OOM; destroy onward_reassembly on CLOSED next to power_rx_cache.
diff --git a/src/server/s-comms.c b/src/server/s-comms.c index ac638a1..afdb1cc 100644 --- a/src/server/s-comms.c +++ b/src/server/s-comms.c @@ -517,6 +517,7 @@ s_callback_ws(struct lws *wsi, enum lws_callback_reasons reason, void *user, /* a conn closed mid-message must not leak its reassembly */ lws_buflist_destroy_all_segments(&pss->power_rx_cache); + lws_buflist_destroy_all_segments(&pss->onward_reassembly); { const unsigned char *cp = lws_get_close_payload(wsi); diff --git a/src/server/s-ws-builder.c b/src/server/s-ws-builder.c index a37ad5f..8637b6d 100644 --- a/src/server/s-ws-builder.c +++ b/src/server/s-ws-builder.c @@ -34,6 +34,13 @@ #include "s-private.h" +/* + * Sanity cap on the per-builder loadreport reassembly. Loadreports are + * small; the reassembly exists so fragmented ones can be forwarded to the + * web side as an atomic message. + */ +#define SAIS_LOADREPORT_REASSEMBLY_MAX (64 * 1024) + const lws_struct_map_t lsm_schema_map_ta[] = { LSM_SCHEMA (sai_task_t, NULL, lsm_task, "com-warmcat-sai-ta"), }; @@ -804,10 +811,15 @@ sais_ws_json_rx_builder(struct vhd *vhd, struct pss *pss, uint8_t *buf, size_t b */ *((unsigned int *)(buf - sizeof(int))) = ss_flags; - if (lws_buflist_append_segment(&pss->onward_reassembly, - buf - sizeof(int), - bl + sizeof(int)) < 0) + if (sais_buflist_append_bounded( + &pss->onward_reassembly, + buf - sizeof(int), + bl + sizeof(int), + SAIS_LOADREPORT_REASSEMBLY_MAX)) { + lwsl_err("%s: loadreport reassembly over cap / OOM\n", + __func__); return -1; + } } pss->frag = 1; @@ -1144,10 +1156,15 @@ sais_ws_json_rx_builder(struct vhd *vhd, struct pss *pss, uint8_t *buf, size_t b */ *((unsigned int *)(buf - sizeof(int))) = ss_flags; - if (lws_buflist_append_segment(&pss->onward_reassembly, - buf - sizeof(int), - bl + sizeof(int)) < 0) + if (sais_buflist_append_bounded( + &pss->onward_reassembly, + buf - sizeof(int), + bl + sizeof(int), + SAIS_LOADREPORT_REASSEMBLY_MAX)) { + lwsl_err("%s: loadreport reassembly over cap / OOM\n", + __func__); return -1; + } /* * Then let's forward the whole reassembly buflist on
Page fetched 0s ago, creation time: 3ms (vhost etag hits: 0%, cache hits: 0%)