Skip to content

Commit b3debf2

Browse files
committed
fix: harden vcl lifecycle for varnish9 v4.3.3
1 parent 44c680c commit b3debf2

15 files changed

Lines changed: 483 additions & 145 deletions

.dockerignore

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,11 @@
33
**/vendor
44
.libs
55
.deps
6+
**/.libs
7+
**/.deps
8+
*.lo
9+
*.la
10+
*.o
611
autom4te.cache
712
aclocal.m4
813
config.h

CHANGELOG.md

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,27 @@
22

33
All notable changes to vmod-wasm will be documented in this file.
44

5+
## [4.3.3] - 2026-05-24
6+
7+
### Fixed
8+
- Isolated Wasm engines per loaded VCL so VCL reloads and discards no longer
9+
tear down modules still owned by another active VCL.
10+
- Reordered engine shutdown so tick timers, store pools, warm instances, and
11+
HTTP pools are destroyed before Wasmtime modules and the Wasmtime engine.
12+
- Hardened HTTP connection pooling by applying socket I/O timeouts and refusing
13+
to reuse pooled sockets with unread stale data.
14+
- Initialized Proxy-Wasm warm lifecycle calls with a real host context, memory,
15+
allocator, shared data, queue store, and metric store.
16+
- Copied Proxy-Wasm header and request-property mutations into Varnish
17+
workspace before passing them to Varnish HTTP APIs.
18+
- Fixed Proxy-Wasm header-map size reporting for request and response pseudo
19+
headers.
20+
- Rejected duplicate module names in a VCL-local engine and cleaned up failed
21+
module loads more defensively.
22+
23+
### Tests
24+
- Added VTC coverage for VCL reload lifecycle isolation.
25+
526
## [4.3.2] - 2026-05-24
627

728
### Fixed

Makefile.am

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ EXTRA_DIST = \
7979
tests/proxy_wasm_sdk.vtc \
8080
tests/proxy_wasm_security.vtc \
8181
tests/resource_config.vtc \
82+
tests/vcl_reload_lifecycle.vtc \
8283
tests/wasm/test_module.wasm
8384

8485
# Unit tests cannot link standalone against the VMOD because Varnish internal

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -119,9 +119,9 @@ make install
119119

120120
### Release Bundles
121121

122-
GitHub releases use Varnish-specific tags such as `varnish9-v4.3.2` so the
122+
GitHub releases use Varnish-specific tags such as `varnish9-v4.3.3` so the
123123
supported Varnish ABI line is visible before download. The package version
124-
remains semantic (`4.3.2`), while the release channel identifies Varnish 9.
124+
remains semantic (`4.3.3`), while the release channel identifies Varnish 9.
125125

126126
Release assets include source and convenience binary bundles for Linux `amd64`
127127
and `arm64` on Varnish 9. Binary bundles include `libvmod_wasm.so`,

configure.ac

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
AC_PREREQ([2.69])
2-
AC_INIT([vmod-wasm], [4.3.2], [https://github.com/RamazanKara/vmod-wasm/issues])
2+
AC_INIT([vmod-wasm], [4.3.3], [https://github.com/RamazanKara/vmod-wasm/issues])
33
AC_CONFIG_SRCDIR([src/vmod_wasm.vcc])
44
AC_CONFIG_MACRO_DIRS([m4])
55
AC_CONFIG_HEADERS([config.h])

src/host_functions.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -583,6 +583,7 @@ wasi_fd_write(void *env, wasmtime_caller_t *caller,
583583

584584
(void)env;
585585
(void)nargs;
586+
(void)nresults;
586587

587588
ctx = wasmtime_caller_context(caller);
588589
hctx = (struct vwasm_host_ctx *)wasmtime_context_get_data(ctx);
@@ -899,6 +900,7 @@ wasi_proc_exit(void *env, wasmtime_caller_t *caller,
899900
(void)caller;
900901
(void)args;
901902
(void)nargs;
903+
(void)results;
902904
(void)nresults;
903905
/* Trap instead of exiting the process */
904906
return (wasmtime_trap_new("wasi proc_exit called", 21));

src/http_pool.c

Lines changed: 24 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#include <poll.h>
2020
#include <sys/socket.h>
2121
#include <sys/types.h>
22+
#include <sys/time.h>
2223
#include <netinet/in.h>
2324
#include <netinet/tcp.h>
2425
#include <arpa/inet.h>
@@ -61,15 +62,6 @@ make_cache_key(const char *method, const char *host, uint16_t port,
6162
return (key);
6263
}
6364

64-
static char *
65-
make_upstream_key(const char *host, uint16_t port)
66-
{
67-
char key[280];
68-
69-
snprintf(key, sizeof(key), "%s:%u", host, port);
70-
return (strdup(key));
71-
}
72-
7365
static int
7466
set_nonblocking(int fd)
7567
{
@@ -111,12 +103,16 @@ conn_is_alive(int fd)
111103
return (0);
112104
if (ret == 0)
113105
return (1); /* No data pending = alive and idle */
106+
if (pfd.revents & (POLLERR | POLLHUP | POLLNVAL))
107+
return (0);
108+
if (!(pfd.revents & POLLIN))
109+
return (1);
114110

115-
/* Data pending: either response data (unlikely) or EOF */
111+
/* Data pending means the connection is not cleanly idle. */
116112
ret = (int)recv(fd, &buf, 1, MSG_PEEK | MSG_DONTWAIT);
117113
if (ret <= 0)
118114
return (0); /* EOF or error: peer closed */
119-
return (1); /* Data available: might be stale response */
115+
return (0);
120116
}
121117

122118
int
@@ -197,6 +193,8 @@ vwasm_http_addr_is_private(const struct sockaddr *sa)
197193
/*
198194
* Connect to host:port with timeout.
199195
*/
196+
static void set_io_timeouts(int fd, uint32_t timeout_ms);
197+
200198
static int
201199
connect_with_timeout(const struct sockaddr_storage *addr, socklen_t addrlen,
202200
uint32_t timeout_ms)
@@ -221,6 +219,7 @@ connect_with_timeout(const struct sockaddr_storage *addr, socklen_t addrlen,
221219
ret = connect(fd, (const struct sockaddr *)addr, addrlen);
222220
if (ret == 0) {
223221
set_blocking(fd);
222+
set_io_timeouts(fd, timeout_ms);
224223
return (fd);
225224
}
226225

@@ -249,9 +248,23 @@ connect_with_timeout(const struct sockaddr_storage *addr, socklen_t addrlen,
249248
}
250249

251250
set_blocking(fd);
251+
set_io_timeouts(fd, timeout_ms);
252252
return (fd);
253253
}
254254

255+
static void
256+
set_io_timeouts(int fd, uint32_t timeout_ms)
257+
{
258+
struct timeval tv;
259+
260+
if (timeout_ms == 0)
261+
timeout_ms = VWASM_HTTP_DEFAULT_TIMEOUT_MS;
262+
tv.tv_sec = timeout_ms / 1000;
263+
tv.tv_usec = (timeout_ms % 1000) * 1000;
264+
(void)setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
265+
(void)setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv));
266+
}
267+
255268
/* ----------------------------------------------------------------
256269
* Pool Creation / Destruction
257270
* ---------------------------------------------------------------- */

src/proxy_wasm.c

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1130,6 +1130,7 @@ pw_proxy_get_metric(void *env, wasmtime_caller_t *caller,
11301130
struct vwasm_metric_store *store;
11311131
uint32_t metric_id;
11321132
uint64_t value;
1133+
uint8_t *dst;
11331134

11341135
(void)env;
11351136
ctx = wasmtime_context_get_data(wasmtime_caller_context(caller));
@@ -1160,7 +1161,12 @@ pw_proxy_get_metric(void *env, wasmtime_caller_t *caller,
11601161
results[0].of.i32 = PROXY_BAD_ARGUMENT;
11611162
return (NULL);
11621163
}
1163-
memcpy(pw_mem_ptr(ctx, (uint32_t)args[1].of.i32), &value, 8);
1164+
dst = pw_mem_ptr(ctx, (uint32_t)args[1].of.i32);
1165+
if (dst == NULL) {
1166+
results[0].of.i32 = PROXY_BAD_ARGUMENT;
1167+
return (NULL);
1168+
}
1169+
memcpy(dst, &value, 8);
11641170

11651171
results[0].of.i32 = PROXY_OK;
11661172
return (NULL);

src/proxy_wasm_headers.c

Lines changed: 45 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -365,6 +365,7 @@ pw_proxy_add_header_map_value(void *env, wasmtime_caller_t *caller,
365365
struct vwasm_proxy_ctx *ctx;
366366
struct http *hp;
367367
char key_buf[256], val_buf[4096], hdr_line[4352];
368+
const char *ws_hdr;
368369
int32_t map_type;
369370

370371
(void)env;
@@ -407,8 +408,13 @@ pw_proxy_add_header_map_value(void *env, wasmtime_caller_t *caller,
407408
}
408409

409410
snprintf(hdr_line, sizeof(hdr_line), "%s: %s", key_buf, val_buf);
410-
http_SetHeader(hp,
411-
WS_Copy(ctx->vrt_ctx->ws, hdr_line, (int)(strlen(hdr_line) + 1)));
411+
ws_hdr = WS_Copy(ctx->vrt_ctx->ws, hdr_line,
412+
(int)(strlen(hdr_line) + 1));
413+
if (ws_hdr == NULL) {
414+
results[0].of.i32 = PROXY_INTERNAL;
415+
return (NULL);
416+
}
417+
http_SetHeader(hp, ws_hdr);
412418

413419
results[0].of.i32 = PROXY_OK;
414420
return (NULL);
@@ -426,6 +432,7 @@ pw_proxy_replace_header_map_value(void *env, wasmtime_caller_t *caller,
426432
struct vwasm_proxy_ctx *ctx;
427433
struct http *hp;
428434
char key_buf[256], val_buf[4096], hdr_search[260], hdr_line[4352];
435+
const char *ws_hdr;
429436
int i;
430437
int32_t map_type;
431438

@@ -473,8 +480,13 @@ pw_proxy_replace_header_map_value(void *env, wasmtime_caller_t *caller,
473480
}
474481

475482
snprintf(hdr_line, sizeof(hdr_line), "%s: %s", key_buf, val_buf);
476-
http_SetHeader(hp,
477-
WS_Copy(ctx->vrt_ctx->ws, hdr_line, (int)(strlen(hdr_line) + 1)));
483+
ws_hdr = WS_Copy(ctx->vrt_ctx->ws, hdr_line,
484+
(int)(strlen(hdr_line) + 1));
485+
if (ws_hdr == NULL) {
486+
results[0].of.i32 = PROXY_INTERNAL;
487+
return (NULL);
488+
}
489+
http_SetHeader(hp, ws_hdr);
478490

479491
results[0].of.i32 = PROXY_OK;
480492
return (NULL);
@@ -783,7 +795,7 @@ pw_proxy_get_header_map_pairs(void *env, wasmtime_caller_t *caller,
783795
break;
784796
}
785797
}
786-
} else if (map_type == 1) {
798+
} else if (map_type == PROXY_MAP_HTTP_RESPONSE_HEADERS) {
787799
if (hp->hd[HTTP_HDR_STATUS].b != NULL) {
788800
pseudo_keys[num_pseudo] = ":status";
789801
pseudo_vals[num_pseudo] = hp->hd[HTTP_HDR_STATUS].b;
@@ -951,6 +963,7 @@ pw_proxy_set_header_map_pairs(void *env, wasmtime_caller_t *caller,
951963
uint32_t map_type, data_ptr, data_size;
952964
uint32_t num_pairs, i, offset;
953965
char hdr_line[4352];
966+
const char *ws_hdr;
954967

955968
(void)env;
956969
(void)nargs;
@@ -1050,7 +1063,13 @@ pw_proxy_set_header_map_pairs(void *env, wasmtime_caller_t *caller,
10501063
if (key_size > 0 && key_size < 256 && val_size < 4096) {
10511064
snprintf(hdr_line, sizeof(hdr_line), "%.*s: %.*s",
10521065
(int)key_size, key, (int)val_size, val);
1053-
http_SetHeader(hp, hdr_line);
1066+
ws_hdr = WS_Copy(ctx->vrt_ctx->ws, hdr_line,
1067+
(int)(strlen(hdr_line) + 1));
1068+
if (ws_hdr == NULL) {
1069+
results[0].of.i32 = PROXY_INTERNAL;
1070+
return (NULL);
1071+
}
1072+
http_SetHeader(hp, ws_hdr);
10541073
}
10551074
}
10561075

@@ -1072,6 +1091,7 @@ pw_proxy_get_header_map_size(void *env, wasmtime_caller_t *caller,
10721091
struct vwasm_proxy_ctx *ctx;
10731092
int32_t map_type;
10741093
uint32_t count = 0;
1094+
uint32_t i;
10751095

10761096
(void)env;
10771097
(void)nargs;
@@ -1089,8 +1109,26 @@ pw_proxy_get_header_map_size(void *env, wasmtime_caller_t *caller,
10891109
count = tm->count;
10901110
} else {
10911111
const struct http *hp = pw_get_header_map(ctx, map_type);
1092-
if (hp != NULL)
1112+
if (hp != NULL) {
10931113
count = (uint32_t)(hp->nhd - HTTP_HDR_FIRST);
1114+
if (map_type == PROXY_MAP_HTTP_REQUEST_HEADERS) {
1115+
if (hp->hd[HTTP_HDR_METHOD].b != NULL)
1116+
count++;
1117+
if (hp->hd[HTTP_HDR_URL].b != NULL)
1118+
count++;
1119+
for (i = HTTP_HDR_FIRST; i < hp->nhd; i++) {
1120+
if (hp->hd[i].b != NULL &&
1121+
strncasecmp(hp->hd[i].b, "Host:",
1122+
5) == 0) {
1123+
count++;
1124+
break;
1125+
}
1126+
}
1127+
} else if (map_type == PROXY_MAP_HTTP_RESPONSE_HEADERS &&
1128+
hp->hd[HTTP_HDR_STATUS].b != NULL) {
1129+
count++;
1130+
}
1131+
}
10941132
}
10951133

10961134
if (pw_write_u32(ctx, (uint32_t)args[1].of.i32, count) != 0) {

src/proxy_wasm_properties.c

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,7 @@ pw_proxy_set_property(void *env, wasmtime_caller_t *caller,
176176
char path_buf[512], value_buf[4096];
177177
uint32_t path_size, value_size;
178178
struct http *hp;
179+
const char *ws_value;
179180

180181
(void)env;
181182
(void)nargs;
@@ -219,8 +220,13 @@ pw_proxy_set_property(void *env, wasmtime_caller_t *caller,
219220
results[0].of.i32 = PROXY_BAD_ARGUMENT;
220221
return (NULL);
221222
}
222-
http_SetH(hp, HTTP_HDR_URL, WS_Copy(vctx->ws,
223-
value_buf, (int)(value_size + 1)));
223+
ws_value = WS_Copy(vctx->ws, value_buf,
224+
(int)(value_size + 1));
225+
if (ws_value == NULL) {
226+
results[0].of.i32 = PROXY_INTERNAL;
227+
return (NULL);
228+
}
229+
http_SetH(hp, HTTP_HDR_URL, ws_value);
224230
results[0].of.i32 = PROXY_OK;
225231
return (NULL);
226232
}
@@ -232,8 +238,13 @@ pw_proxy_set_property(void *env, wasmtime_caller_t *caller,
232238
results[0].of.i32 = PROXY_BAD_ARGUMENT;
233239
return (NULL);
234240
}
235-
http_SetH(hp, HTTP_HDR_METHOD, WS_Copy(vctx->ws,
236-
value_buf, (int)(value_size + 1)));
241+
ws_value = WS_Copy(vctx->ws, value_buf,
242+
(int)(value_size + 1));
243+
if (ws_value == NULL) {
244+
results[0].of.i32 = PROXY_INTERNAL;
245+
return (NULL);
246+
}
247+
http_SetH(hp, HTTP_HDR_METHOD, ws_value);
237248
results[0].of.i32 = PROXY_OK;
238249
return (NULL);
239250
}
@@ -249,8 +260,13 @@ pw_proxy_set_property(void *env, wasmtime_caller_t *caller,
249260
snprintf(hdr_line, sizeof(hdr_line), "Host: %.*s",
250261
(int)value_size, value_buf);
251262
http_Unset(hp, (hdr_t)"\005Host:");
252-
http_SetHeader(hp, WS_Copy(vctx->ws,
253-
hdr_line, (int)(strlen(hdr_line) + 1)));
263+
ws_value = WS_Copy(vctx->ws, hdr_line,
264+
(int)(strlen(hdr_line) + 1));
265+
if (ws_value == NULL) {
266+
results[0].of.i32 = PROXY_INTERNAL;
267+
return (NULL);
268+
}
269+
http_SetHeader(hp, ws_value);
254270
results[0].of.i32 = PROXY_OK;
255271
return (NULL);
256272
}

0 commit comments

Comments
 (0)