Skip to content

Commit 5add50e

Browse files
authored
feat: improve input loading and string handling (#16)
* chore: improve code quality * take `void *` instead of `uint8_t*` for buffer params * add `const` to imports to signify when a ExtismPointer's data is modified * pass void to imports that shouldn't take parameters * minor cleanup in store and load functions * use size_t for pdk offsets and sizes instead of hardcoding it to uint64_t. * in wasm32 its 4 bytes * in wasm64 its 8 bytes * deprecate extism_alloc_string / add extism_alloc_buf * chore: namespace import macros * feat: implement convenience methods to load extism memory into zero terminated and malloc'd buffers * test: add tests for new functions, check input length before loading input, rename parameters to be more like libc
1 parent 04c7f05 commit 5add50e

8 files changed

+239
-96
lines changed

extism-pdk.h

+175-79
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#pragma once
22

3+
#include <stdbool.h>
34
#include <stdint.h>
45

56
typedef uint64_t ExtismPointer;
@@ -12,125 +13,220 @@ typedef uint64_t ExtismPointer;
1213
EXTISM_EXPORT_AS(#name) \
1314
name(void)
1415

15-
#define IMPORT(a, b) __attribute__((import_module(a), import_name(b)))
16-
#define IMPORT_ENV(b) \
16+
#define EXTISM_IMPORT(a, b) __attribute__((import_module(a), import_name(b)))
17+
#define EXTISM_IMPORT_ENV(b) \
1718
__attribute__((import_module(EXTISM_ENV_MODULE), import_name(b)))
18-
#define IMPORT_USER(b) \
19+
#define EXTISM_IMPORT_USER(b) \
1920
__attribute__((import_module(EXTISM_USER_MODULE), import_name(b)))
2021

21-
IMPORT_ENV("input_length")
22-
extern uint64_t extism_input_length();
23-
IMPORT_ENV("length")
24-
extern uint64_t extism_length(ExtismPointer);
25-
IMPORT_ENV("alloc")
26-
extern ExtismPointer extism_alloc(uint64_t);
27-
IMPORT_ENV("free")
22+
EXTISM_IMPORT_ENV("input_length")
23+
extern uint64_t extism_input_length(void);
24+
EXTISM_IMPORT_ENV("length")
25+
extern uint64_t extism_length(const ExtismPointer);
26+
EXTISM_IMPORT_ENV("alloc")
27+
extern ExtismPointer extism_alloc(const uint64_t);
28+
EXTISM_IMPORT_ENV("free")
2829
extern void extism_free(ExtismPointer);
30+
EXTISM_IMPORT_ENV("input_load_u8")
31+
extern uint8_t extism_input_load_u8(const ExtismPointer);
2932

30-
IMPORT_ENV("input_load_u8")
31-
extern uint8_t extism_input_load_u8(ExtismPointer);
33+
EXTISM_IMPORT_ENV("input_load_u64")
34+
extern uint64_t extism_input_load_u64(const ExtismPointer);
3235

33-
IMPORT_ENV("input_load_u64")
34-
extern uint64_t extism_input_load_u64(ExtismPointer);
36+
EXTISM_IMPORT_ENV("output_set")
37+
extern void extism_output_set(const ExtismPointer, const uint64_t);
3538

36-
IMPORT_ENV("output_set")
37-
extern void extism_output_set(ExtismPointer, uint64_t);
39+
EXTISM_IMPORT_ENV("error_set")
40+
extern void extism_error_set(const ExtismPointer);
3841

39-
IMPORT_ENV("error_set")
40-
extern void extism_error_set(ExtismPointer);
42+
EXTISM_IMPORT_ENV("config_get")
43+
extern ExtismPointer extism_config_get(const ExtismPointer);
4144

42-
IMPORT_ENV("config_get")
43-
extern ExtismPointer extism_config_get(ExtismPointer);
45+
EXTISM_IMPORT_ENV("var_get")
46+
extern ExtismPointer extism_var_get(const ExtismPointer);
4447

45-
IMPORT_ENV("var_get")
46-
extern ExtismPointer extism_var_get(ExtismPointer);
48+
EXTISM_IMPORT_ENV("var_set")
49+
extern void extism_var_set(ExtismPointer, const ExtismPointer);
4750

48-
IMPORT_ENV("var_set")
49-
extern void extism_var_set(ExtismPointer, ExtismPointer);
51+
EXTISM_IMPORT_ENV("store_u8")
52+
extern void extism_store_u8(ExtismPointer, const uint8_t);
5053

51-
IMPORT_ENV("store_u8")
52-
extern void extism_store_u8(ExtismPointer, uint8_t);
54+
EXTISM_IMPORT_ENV("load_u8")
55+
extern uint8_t extism_load_u8(const ExtismPointer);
5356

54-
IMPORT_ENV("load_u8")
55-
extern uint8_t extism_load_u8(ExtismPointer);
57+
EXTISM_IMPORT_ENV("store_u64")
58+
extern void extism_store_u64(ExtismPointer, const uint64_t);
5659

57-
IMPORT_ENV("store_u64")
58-
extern void extism_store_u64(ExtismPointer, uint64_t);
60+
EXTISM_IMPORT_ENV("load_u64")
61+
extern uint64_t extism_load_u64(const ExtismPointer);
5962

60-
IMPORT_ENV("load_u64")
61-
extern uint64_t extism_load_u64(ExtismPointer);
63+
EXTISM_IMPORT_ENV("http_request")
64+
extern ExtismPointer extism_http_request(const ExtismPointer,
65+
const ExtismPointer);
6266

63-
IMPORT_ENV("http_request")
64-
extern ExtismPointer extism_http_request(ExtismPointer, ExtismPointer);
67+
EXTISM_IMPORT_ENV("http_status_code")
68+
extern int32_t extism_http_status_code(void);
6569

66-
IMPORT_ENV("http_status_code")
67-
extern int32_t extism_http_status_code();
68-
69-
IMPORT_ENV("log_info")
70-
extern void extism_log_info(ExtismPointer);
71-
IMPORT_ENV("log_debug")
72-
extern void extism_log_debug(ExtismPointer);
73-
IMPORT_ENV("log_warn")
74-
extern void extism_log_warn(ExtismPointer);
75-
IMPORT_ENV("log_error")
76-
extern void extism_log_error(ExtismPointer);
70+
EXTISM_IMPORT_ENV("log_info")
71+
extern void extism_log_info(const ExtismPointer);
72+
EXTISM_IMPORT_ENV("log_debug")
73+
extern void extism_log_debug(const ExtismPointer);
74+
EXTISM_IMPORT_ENV("log_warn")
75+
extern void extism_log_warn(const ExtismPointer);
76+
EXTISM_IMPORT_ENV("log_error")
77+
extern void extism_log_error(const ExtismPointer);
7778

7879
// Load data from Extism memory
79-
static void extism_load(ExtismPointer offs, uint8_t *buffer, uint64_t length) {
80-
uint64_t chunk_count = length >> 3;
81-
uint64_t *i64_buffer = (uint64_t *)buffer;
82-
for (uint64_t chunk_idx = 0; chunk_idx < chunk_count; chunk_idx++) {
80+
// Does not verify load is in bounds
81+
static void extism_load(const ExtismPointer offs, void *dest, const size_t n) {
82+
const size_t chunk_count = n >> 3;
83+
uint64_t *i64_buffer = dest;
84+
for (size_t chunk_idx = 0; chunk_idx < chunk_count; chunk_idx++) {
8385
i64_buffer[chunk_idx] = extism_load_u64(offs + (chunk_idx << 3));
8486
}
8587

86-
uint64_t remainder = length & 7;
87-
uint64_t remainder_offset = chunk_count << 3;
88-
for (uint64_t index = remainder_offset;
89-
index < (remainder + remainder_offset); index++) {
90-
buffer[index] = extism_load_u8(offs + index);
88+
size_t remainder_offset = chunk_count << 3;
89+
const size_t remainder_end = remainder_offset + (n & 7);
90+
for (uint8_t *u8_buffer = dest; remainder_offset < remainder_end;
91+
remainder_offset++) {
92+
u8_buffer[remainder_offset] = extism_load_u8(offs + remainder_offset);
9193
}
9294
}
9395

9496
// Load data from input buffer
95-
static void extism_load_input(uint8_t *buffer, uint64_t length) {
96-
uint64_t chunk_count = length >> 3;
97-
uint64_t *i64_buffer = (uint64_t *)buffer;
98-
for (uint64_t chunk_idx = 0; chunk_idx < chunk_count; chunk_idx++) {
97+
// Does not verify load is inbounds
98+
static void extism_load_input_unsafe(void *dest, const size_t n) {
99+
const size_t chunk_count = n >> 3;
100+
uint64_t *i64_buffer = dest;
101+
for (size_t chunk_idx = 0; chunk_idx < chunk_count; chunk_idx++) {
99102
i64_buffer[chunk_idx] = extism_input_load_u64(chunk_idx << 3);
100103
}
101104

102-
uint64_t remainder = length & 7;
103-
uint64_t remainder_offset = chunk_count << 3;
104-
for (uint64_t index = remainder_offset;
105-
index < (remainder + remainder_offset); index++) {
106-
buffer[index] = extism_input_load_u8(index);
105+
size_t remainder_offset = chunk_count << 3;
106+
const size_t remainder_end = remainder_offset + (n & 7);
107+
for (uint8_t *u8_buffer = dest; remainder_offset < remainder_end;
108+
remainder_offset++) {
109+
u8_buffer[remainder_offset] = extism_input_load_u8(remainder_offset);
110+
}
111+
}
112+
113+
// Load data from input buffer
114+
// Verifies load is inbounds
115+
static bool extism_load_input(void *dest, const size_t n) {
116+
const uint64_t input_len = extism_input_length();
117+
if (n > input_len) {
118+
return false;
107119
}
120+
extism_load_input_unsafe(dest, n);
121+
return true;
122+
}
123+
124+
// Load n-1 bytes from input buffer and zero terminate
125+
// Does not verify load is inbounds
126+
static void extism_load_input_sz_unsafe(char *dest, const size_t n) {
127+
extism_load_input_unsafe(dest, n - 1);
128+
dest[n - 1] = '\0';
129+
}
130+
131+
// Load n-1 bytes from input buffer and zero terminate
132+
// Verifies load is inbounds
133+
static bool extism_load_input_sz(char *dest, const size_t n) {
134+
const uint64_t input_len = extism_input_length();
135+
if ((n - 1) > input_len) {
136+
return false;
137+
}
138+
extism_load_input_sz_unsafe(dest, n);
139+
return true;
108140
}
109141

110142
// Copy data into Extism memory
111-
static void extism_store(ExtismPointer offs, const uint8_t *buffer,
112-
uint64_t length) {
113-
uint64_t chunk_count = length >> 3;
114-
uint64_t *i64_buffer = (uint64_t *)buffer;
115-
for (uint64_t chunk_idx = 0; chunk_idx < chunk_count; chunk_idx++) {
143+
static void extism_store(ExtismPointer offs, const void *buffer,
144+
const size_t length) {
145+
const size_t chunk_count = length >> 3;
146+
const uint64_t *i64_buffer = buffer;
147+
for (size_t chunk_idx = 0; chunk_idx < chunk_count; chunk_idx++) {
116148
extism_store_u64(offs + (chunk_idx << 3), i64_buffer[chunk_idx]);
117149
}
118150

119-
uint64_t remainder = length & 7;
120-
uint64_t remainder_offset = chunk_count << 3;
121-
for (uint64_t index = remainder_offset;
122-
index < (remainder + remainder_offset); index++) {
123-
extism_store_u8(offs + index, buffer[index]);
151+
size_t remainder_offset = chunk_count << 3;
152+
const size_t remainder_end = remainder_offset + (length & 7);
153+
for (const uint8_t *u8_buffer = buffer; remainder_offset < remainder_end;
154+
remainder_offset++) {
155+
extism_store_u8(offs + remainder_offset, u8_buffer[remainder_offset]);
124156
}
125157
}
126158

127-
// Allocate a string and copy the provided value into Extism memory
128-
static ExtismPointer extism_alloc_string(const char *s, uint64_t length) {
129-
ExtismPointer ptr = extism_alloc(length);
130-
extism_store(ptr, (const uint8_t *)s, length);
159+
// Allocate a buffer in Extism memory and copy into it
160+
static ExtismPointer extism_alloc_buf(const void *src, const size_t n) {
161+
ExtismPointer ptr = extism_alloc(n);
162+
extism_store(ptr, src, n);
131163
return ptr;
132164
}
133165

166+
__attribute__((
167+
deprecated("Use extism_alloc_buf instead."))) static inline ExtismPointer
168+
extism_alloc_string(const char *s, const size_t n) {
169+
return extism_alloc_buf(s, n);
170+
}
171+
172+
#ifdef EXTISM_USE_LIBC
173+
#include <stdlib.h>
174+
#include <string.h>
175+
176+
#define extism_strlen strlen
177+
178+
// get the input length (n) and malloc(n), load n bytes from Extism memory into
179+
// it. If outSize is provided, set it to n
180+
static void *extism_load_input_dup(size_t *outSize) {
181+
const uint64_t n = extism_input_length();
182+
if (n > SIZE_MAX) {
183+
return NULL;
184+
}
185+
void *buf = malloc(n);
186+
if (!buf) {
187+
return NULL;
188+
}
189+
extism_load_input_unsafe(buf, n);
190+
if (outSize) {
191+
*outSize = n;
192+
}
193+
return buf;
194+
}
195+
196+
// get the input length, add 1 to it to get n. malloc(n), load n - 1 bytes from
197+
// Extism memory into it. Zero terminate. If outSize is provided, set it to n
198+
static void *extism_load_input_sz_dup(size_t *outSize) {
199+
uint64_t n = extism_input_length();
200+
if (n > (SIZE_MAX - 1)) {
201+
return NULL;
202+
}
203+
n++;
204+
char *buf = malloc(n);
205+
if (!buf) {
206+
return NULL;
207+
}
208+
extism_load_input_sz_unsafe(buf, n);
209+
if (outSize) {
210+
*outSize = n;
211+
}
212+
return buf;
213+
}
214+
215+
#else
216+
static size_t extism_strlen(const char *sz) {
217+
size_t len;
218+
for (len = 0; sz[len] != '\0'; len++) {
219+
}
220+
return len;
221+
}
222+
#endif
223+
224+
// Allocate a buffer in Extism memory and copy string data into it
225+
// copied string is NOT null terminated
226+
static ExtismPointer extism_alloc_buf_from_sz(const char *sz) {
227+
return extism_alloc_buf(sz, extism_strlen(sz));
228+
}
229+
134230
typedef enum {
135231
ExtismLogInfo,
136232
ExtismLogDebug,
@@ -139,9 +235,9 @@ typedef enum {
139235
} ExtismLog;
140236

141237
// Write to Extism log
142-
static void extism_log(const char *s, uint64_t len, ExtismLog level) {
238+
static void extism_log(const char *s, const size_t len, const ExtismLog level) {
143239
ExtismPointer ptr = extism_alloc(len);
144-
extism_store(ptr, (const uint8_t *)s, len);
240+
extism_store(ptr, s, len);
145241
switch (level) {
146242
case ExtismLogInfo:
147243
extism_log_info(ptr);

tests/Makefile

+5-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
WASI_SDK_PATH?=../wasi-sdk
22

3-
test: test-alloc test-hello
3+
test: test-alloc test-hello test-load_input_bounds_checks test-strlen test-use_libc test-alloc_buf_from_sz
44
run-fail: test-fail
5-
build: alloc hello fail
5+
build: alloc hello fail load_input_bounds_checks strlen use_libc alloc_buf_from_sz
6+
7+
strlen use_libc:
8+
$(WASI_SDK_PATH)/bin/clang --target=wasm32-wasi -o $@.wasm $@.c -mexec-model=reactor
69

710
%: %.c
811
$(WASI_SDK_PATH)/bin/clang --target=wasm32-unknown-unknown -o $*.wasm $*.c -Wl,--no-entry -nostdlib

tests/alloc_buf_from_sz.c

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
#include "util.h"
2+
3+
EXTISM_EXPORT_AS("run_test") int32_t run_test(void) {
4+
const char *msg = "Hello, world!";
5+
ExtismPointer ptr = extism_alloc_buf_from_sz(msg);
6+
assert(ptr > 0);
7+
assert(extism_length(ptr) == extism_strlen(msg));
8+
return 0;
9+
}

tests/hello.c

+4-4
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,10 @@
22

33
EXTISM_EXPORT_AS("run_test") int32_t run_test(void) {
44
const char *msg = "Hello, world!";
5-
ExtismPointer ptr = extism_alloc(strlen(msg));
5+
ExtismPointer ptr = extism_alloc(extism_strlen(msg));
66
assert(ptr > 0);
7-
extism_store(ptr, (const uint8_t *)msg, strlen(msg));
8-
assert(extism_length(ptr) == strlen(msg));
9-
extism_output_set(ptr, strlen(msg));
7+
extism_store(ptr, (const uint8_t *)msg, extism_strlen(msg));
8+
assert(extism_length(ptr) == extism_strlen(msg));
9+
extism_output_set(ptr, extism_strlen(msg));
1010
return 0;
1111
}

tests/load_input_bounds_checks.c

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
#include "util.h"
2+
3+
EXTISM_EXPORT_AS("run_test") int32_t run_test(void) {
4+
char c[2];
5+
extism_load_input_unsafe(c, 0);
6+
assert(extism_load_input(c, 0));
7+
assert(!extism_load_input(c, 1));
8+
extism_load_input_sz_unsafe(c, 1);
9+
assert(!extism_load_input_sz(c, 0));
10+
assert(extism_load_input_sz(c, 1));
11+
assert(!extism_load_input_sz(c, 2));
12+
return 0;
13+
}

tests/strlen.c

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
#include "util.h"
2+
#include <string.h>
3+
4+
EXTISM_EXPORT_AS("run_test") int32_t run_test(void) {
5+
const char *hello = "hello";
6+
assert(strlen(hello) == extism_strlen(hello));
7+
assert(strlen("") == extism_strlen(""));
8+
return 0;
9+
}

0 commit comments

Comments
 (0)