From acce8e2556251fbe9f2bda31cc6f2ceef1e18a3f Mon Sep 17 00:00:00 2001 From: Joshua Wright Date: Wed, 26 Aug 2026 10:31:21 -0500 Subject: [PATCH 1/2] Fix out-of-bounds dltab in the WASI dlopen shim MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit dltab was a tentative one-element array ("dict_t* dltab[]") indexed past its end, and slot 0 was never assigned: the first library was stored at dltab[1] while the lookup loop read dltab[0..dltab_index). Every dlopen lookup therefore dereferenced a NULL dict, i.e. guest address 0. That happens to be harmless while address 0 holds zeros (the dict's len field reads as 0), which is why the socket-file transport never noticed. With the CMA wire channel, live request bytes sit at the bottom of linear memory, so the NULL-dict read picks up garbage lengths and entry pointers and traps — reproducible as a crash on the first CREATE EXTENSION issued over CMA. Make dltab a real fixed-size array, keep every slot initialized, check the existing-library branch against index >= 0 (slot 0 is valid), and return stable 1-based handles. --- .../sdk_port-wasi/sdk_port-wasi-dlfcn.c | 21 ++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/wasm-build/sdk_port-wasi/sdk_port-wasi-dlfcn.c b/wasm-build/sdk_port-wasi/sdk_port-wasi-dlfcn.c index 44dac9e5e3cd2..034651dacf2b1 100644 --- a/wasm-build/sdk_port-wasi/sdk_port-wasi-dlfcn.c +++ b/wasm-build/sdk_port-wasi/sdk_port-wasi-dlfcn.c @@ -81,7 +81,16 @@ ends_with(const char *str, const char *suffix) // dlfcn.h -volatile dict_t* dltab[]; +/* dltab was a tentative (1-element) array indexed past its end, and slot + * 0 was never assigned: the first library landed at dltab[1] while the + * lookup loop read dltab[0..dltab_index), so every lookup dereferenced a + * NULL dict — i.e. guest address 0. That is harmless while address 0 + * holds zeros, but the CMA wire channel places live request bytes at the + * bottom of memory, turning the NULL-dict read into wild loads and traps + * (observed as a crash on the first CREATE EXTENSION over CMA). Use a + * real array and keep every slot initialized. */ +#define DLTAB_MAX 64 +static dict_t dltab[DLTAB_MAX]; volatile int dltab_index = 0; void * @@ -132,8 +141,8 @@ dlopen(const char *filename, int flags) { dict_t tab = NULL; fprintf(stderr,"void *dlopen(const char *filename = %s, int flags=%d)\n", filename, flags); for (int i=0; i< dltab_index; i++) { - if ( dict_find_index(dltab[i], filename) > 0 ) - return (void *)i; + if ( dltab[i] && dict_find_index(dltab[i], filename) >= 0 ) + return (void *)(i + 1); } printf("dlopen: new lib '%s'\n", filename ); if ( ends_with(filename,"/plpgsql.so") ){ @@ -141,9 +150,11 @@ dlopen(const char *filename, int flags) { _PG_init(); } + if (dltab_index >= DLTAB_MAX) + return NULL; tab = dict_new(); - dict_add(tab, filename, dltab_index++ ); - dltab[dltab_index] = tab; + dict_add(tab, filename, dltab_index); + dltab[dltab_index++] = tab; return (void *)dltab_index; } From 11ade7ce1a25b067414f4fa428063a6c5e1a84ef Mon Sep 17 00:00:00 2001 From: Joshua Wright Date: Wed, 26 Aug 2026 10:31:50 -0500 Subject: [PATCH 2/2] Add hooks for statically linked extensions in the WASI build The WASI flavor has no dynamic loading; its dlopen shim resolves symbols from a hardcoded table covering only dict_snowball and plpgsql, so extensions like pgvector cannot be added without editing the shim. Add two overridable (weak) hooks: - pglite_extra_dlsym(symbol): consulted when the built-in table misses, letting a linked object supply extension symbols (e.g. a table generated from llvm-nm over the extension's static library); - pglite_extra_dlopen(filename): called for every dlopen, letting the object run the extension's renamed _PG_init. Also pass ${EXTRA_PG_LIBS:-} at the end of the pglite.wasi link line so a build can append static extension archives without patching build.sh. The weak defaults keep the stock build byte-identical in behavior. Used to link pgvector 0.8.0 (with a 327-entry generated dlsym table) into pglite.wasi; CREATE EXTENSION vector, the vector type, distance operators, and HNSW/IVFFlat index builds all work, verified natively on iOS/macOS and under wazero/node WASI hosts. --- pglite-wasm/build.sh | 2 +- wasm-build/sdk_port-wasi/sdk_port-wasi-dlfcn.c | 15 +++++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/pglite-wasm/build.sh b/pglite-wasm/build.sh index bb97a96ae3704..a4ec773bcf3e1 100755 --- a/pglite-wasm/build.sh +++ b/pglite-wasm/build.sh @@ -105,7 +105,7 @@ ________________________________________________________ $LINK_ICU \ ${PG_BUILD}/${BUILD}/src/backend/snowball/libdict_snowball.a \ ${PG_BUILD}/${BUILD}/src/pl/plpgsql/src/libplpgsql.a \ - -lxml2 -lz + -lxml2 -lz ${EXTRA_PG_LIBS:-} else echo "compilation of libpglite ${BUILD} support failed" fi diff --git a/wasm-build/sdk_port-wasi/sdk_port-wasi-dlfcn.c b/wasm-build/sdk_port-wasi/sdk_port-wasi-dlfcn.c index 034651dacf2b1..ad12f0d41da1b 100644 --- a/wasm-build/sdk_port-wasi/sdk_port-wasi-dlfcn.c +++ b/wasm-build/sdk_port-wasi/sdk_port-wasi-dlfcn.c @@ -135,6 +135,18 @@ extern void plpgsql_validator(void); extern void _PG_init(void); +/* Statically-linked extension hook: a build may link an extra object that + * overrides these to resolve extension symbols (e.g. a generated table for + * pgvector) and run its _PG_init when the extension's .so is "opened". + * The weak defaults keep the stock build behavior. */ +__attribute__((weak)) void * +pglite_extra_dlsym(const char *symbol) { + return NULL; +} +__attribute__((weak)) void +pglite_extra_dlopen(const char *filename) { +} + void * dlopen(const char *filename, int flags) { @@ -149,6 +161,7 @@ dlopen(const char *filename, int flags) { puts(" ========= CALLING _PG_init ========="); _PG_init(); } + pglite_extra_dlopen(filename); if (dltab_index >= DLTAB_MAX) return NULL; @@ -202,6 +215,8 @@ dlsym(void *__restrict handle, const char *__restrict symbol) { } report:; + if (sym == NULL) + sym = pglite_extra_dlsym(symbol); fprintf(stderr, "void *dlsym(void *handle = %p, const char *symbol = %s) => %p\n", handle, symbol, sym); return sym; }