Skip to content

Commit f2bde1e

Browse files
authored
Add missing realloc validation in string transcoding (bytecodealliance#13026)
This commit adds a missing validation that a return value of `realloc` is inbounds during string transcoding. This was accidentally missing on the transcoding path from `utf8` to `latin1+utf16` which meant that a nearly-raw pointer could get passed to the host to perform the transcode.
1 parent e8d6c9d commit f2bde1e

2 files changed

Lines changed: 73 additions & 0 deletions

File tree

crates/environ/src/fact/trampoline.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2352,6 +2352,7 @@ impl<'a, 'b> Compiler<'a, 'b> {
23522352
self.instruction(Call(dst_mem_opts.realloc.unwrap().as_u32()));
23532353
self.instruction(LocalSet(dst.ptr.idx));
23542354
self.verify_aligned(dst_opts.data_model.unwrap_memory(), dst.ptr.idx, 2);
2355+
self.validate_string_inbounds(&dst, dst_byte_len.idx);
23552356

23562357
// Call the host utf16 transcoding function. This will inflate the
23572358
// prior latin1 bytes and then encode the rest of the source string

tests/misc_testsuite/component-model/strings.wast

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -305,3 +305,75 @@
305305
(export "f" (func $c2 "f"))
306306
)
307307
(assert_trap (invoke "f") "unaligned pointer")
308+
309+
;; utf8 -> latin1+utf16
310+
;; - first realloc fails to hold latin1
311+
;; - second realloc is out of bounds
312+
(component
313+
(component $c
314+
(core module $m
315+
(global $cnt (mut i32) (i32.const 0))
316+
(func (export "") (param i32 i32)
317+
unreachable
318+
)
319+
(func (export "realloc") (param $old_ptr i32) (param $old_size i32)
320+
(param $align i32) (param $new_size i32) (result i32)
321+
(if (i32.ne (local.get $align) (i32.const 2)) (then unreachable))
322+
(global.set $cnt (i32.add (global.get $cnt) (i32.const 1)))
323+
324+
;; first allocation is aligned
325+
(if (i32.eq (global.get $cnt) (i32.const 1))
326+
(then
327+
(if (i32.ne (local.get $old_ptr) (i32.const 0)) (then unreachable))
328+
(if (i32.ne (local.get $old_size) (i32.const 0)) (then unreachable))
329+
(if (i32.ne (local.get $new_size) (i32.const 5)) (then unreachable))
330+
(return (i32.const 2)))
331+
)
332+
;; second allocation is out of bounds
333+
(if (i32.eq (global.get $cnt) (i32.const 2))
334+
(then
335+
(if (i32.ne (local.get $old_ptr) (i32.const 2)) (then unreachable))
336+
(if (i32.ne (local.get $old_size) (i32.const 5)) (then unreachable))
337+
(if (i32.ne (local.get $new_size) (i32.const 10)) (then unreachable))
338+
(return (i32.const -2)))
339+
)
340+
341+
unreachable
342+
)
343+
(memory (export "memory") 1)
344+
)
345+
(core instance $m (instantiate $m))
346+
(func (export "a") (param "a" string)
347+
(canon lift
348+
(core func $m "")
349+
(realloc (func $m "realloc"))
350+
(memory $m "memory")
351+
string-encoding=latin1+utf16)
352+
)
353+
)
354+
355+
(component $c2
356+
(import "a" (func $f (param "a" string)))
357+
(core module $libc
358+
(memory (export "memory") 1)
359+
;; "Ë┛" in UTF-8 is "\xc3\xab\xe2\x8c\x9b", 5 bytes.
360+
;; * First, a 5-byte allocation is made to see if it fits in latin 1.
361+
;; * This fails since "┛" does not fit in latin1. The second allocation
362+
;; is then out of bounds and should trap
363+
(data (memory 0) (i32.const 0) "Ë┛")
364+
)
365+
(core instance $libc (instantiate $libc))
366+
(core func $f (canon lower (func $f) (memory $libc "memory")))
367+
(core module $m
368+
(import "" "" (func $f (param i32 i32)))
369+
(func (export "f") (call $f (i32.const 0) (i32.const 5)))
370+
)
371+
(core instance $m (instantiate $m (with "" (instance (export "" (func $f))))))
372+
(func (export "f") (canon lift (core func $m "f")))
373+
)
374+
375+
(instance $c (instantiate $c))
376+
(instance $c2 (instantiate $c2 (with "a" (func $c "a"))))
377+
(export "f" (func $c2 "f"))
378+
)
379+
(assert_trap (invoke "f") "string content out-of-bounds")

0 commit comments

Comments
 (0)