@@ -670,103 +670,111 @@ fn instance_too_large() -> Result<()> {
670670#[ cfg_attr( miri, ignore) ]
671671fn dynamic_memory_pooling_allocator ( ) -> Result < ( ) > {
672672 for guard_size in [ 0 , 1 << 16 ] {
673- let max_size = 128 << 20 ;
674- let mut pool = crate :: small_pool_config ( ) ;
675- pool. max_memory_size ( max_size as usize ) ;
676- let mut config = Config :: new ( ) ;
677- config. memory_reservation ( max_size) ;
678- config. memory_guard_size ( guard_size) ;
679- config. allocation_strategy ( pool) ;
680-
681- let engine = Engine :: new ( & config) ?;
682-
683- let module = Module :: new (
684- & engine,
685- r#"
686- (module
687- (memory (export "memory") 1)
688-
689- (func (export "grow") (param i32) (result i32)
690- local.get 0
691- memory.grow)
692-
693- (func (export "size") (result i32)
694- memory.size)
695-
696- (func (export "i32.load") (param i32) (result i32)
697- local.get 0
698- i32.load)
699-
700- (func (export "i32.store") (param i32 i32)
701- local.get 0
702- local.get 1
703- i32.store)
704-
705- (data (i32.const 100) "x")
706- )
707- "# ,
708- ) ?;
709-
710- let mut store = Store :: new ( & engine, ( ) ) ;
711- let instance = Instance :: new ( & mut store, & module, & [ ] ) ?;
673+ for signals_based_traps in [ false , true ] {
674+ let max_size = 128 << 20 ;
675+ let mut pool = crate :: small_pool_config ( ) ;
676+ pool. max_memory_size ( max_size as usize ) ;
677+ let mut config = Config :: new ( ) ;
678+ config. memory_reservation ( max_size) ;
679+ config. memory_guard_size ( guard_size) ;
680+ config. allocation_strategy ( pool) ;
681+ config. signals_based_traps ( signals_based_traps) ;
682+
683+ let engine = Engine :: new ( & config) ?;
684+
685+ let Ok ( module) = Module :: new (
686+ & engine,
687+ r#"
688+ (module
689+ (memory (export "memory") 1)
690+
691+ (func (export "grow") (param i32) (result i32)
692+ local.get 0
693+ memory.grow)
694+
695+ (func (export "size") (result i32)
696+ memory.size)
697+
698+ (func (export "i32.load") (param i32) (result i32)
699+ local.get 0
700+ i32.load)
701+
702+ (func (export "i32.store") (param i32 i32)
703+ local.get 0
704+ local.get 1
705+ i32.store)
706+
707+ (data (i32.const 100) "x")
708+ )
709+ "# ,
710+ ) else {
711+ // Ignore invalid configurations on 32-bit which can't run with
712+ // signals-based-traps.
713+ assert ! ( cfg!( target_pointer_width = "32" ) && signals_based_traps) ;
714+ continue ;
715+ } ;
712716
713- let grow = instance. get_typed_func :: < u32 , i32 > ( & mut store, "grow" ) ?;
714- let size = instance. get_typed_func :: < ( ) , u32 > ( & mut store, "size" ) ?;
715- let i32_load = instance. get_typed_func :: < u32 , i32 > ( & mut store, "i32.load" ) ?;
716- let i32_store = instance. get_typed_func :: < ( u32 , i32 ) , ( ) > ( & mut store, "i32.store" ) ?;
717- let memory = instance. get_memory ( & mut store, "memory" ) . unwrap ( ) ;
718-
719- // basic length 1 tests
720- // assert_eq!(memory.grow(&mut store, 1)?, 0);
721- assert_eq ! ( memory. size( & store) , 1 ) ;
722- assert_eq ! ( size. call( & mut store, ( ) ) ?, 1 ) ;
723- assert_eq ! ( i32_load. call( & mut store, 0 ) ?, 0 ) ;
724- assert_eq ! ( i32_load. call( & mut store, 100 ) ?, i32 :: from( b'x' ) ) ;
725- i32_store. call ( & mut store, ( 0 , 0 ) ) ?;
726- i32_store. call ( & mut store, ( 100 , i32:: from ( b'y' ) ) ) ?;
727- assert_eq ! ( i32_load. call( & mut store, 100 ) ?, i32 :: from( b'y' ) ) ;
728-
729- // basic length 2 tests
730- let page = 64 * 1024 ;
731- assert_eq ! ( grow. call( & mut store, 1 ) ?, 1 ) ;
732- assert_eq ! ( memory. size( & store) , 2 ) ;
733- assert_eq ! ( size. call( & mut store, ( ) ) ?, 2 ) ;
734- i32_store. call ( & mut store, ( page, 200 ) ) ?;
735- assert_eq ! ( i32_load. call( & mut store, page) ?, 200 ) ;
736-
737- // test writes are visible
738- i32_store. call ( & mut store, ( 2 , 100 ) ) ?;
739- assert_eq ! ( i32_load. call( & mut store, 2 ) ?, 100 ) ;
740-
741- // test growth can't exceed maximum
742- let too_many = max_size / ( 64 * 1024 ) ;
743- assert_eq ! ( grow. call( & mut store, too_many as u32 ) ?, -1 ) ;
744- assert ! ( memory. grow( & mut store, too_many) . is_err( ) ) ;
745-
746- assert_eq ! ( memory. data( & store) [ page as usize ] , 200 ) ;
747-
748- // Re-instantiate in another store.
749- store = Store :: new ( & engine, ( ) ) ;
750- let instance = Instance :: new ( & mut store, & module, & [ ] ) ?;
751- let i32_load = instance. get_typed_func :: < u32 , i32 > ( & mut store, "i32.load" ) ?;
752- let memory = instance. get_memory ( & mut store, "memory" ) . unwrap ( ) ;
753-
754- // This is out of bounds...
755- assert ! ( i32_load. call( & mut store, page) . is_err( ) ) ;
756- assert_eq ! ( memory. data_size( & store) , page as usize ) ;
757-
758- // ... but implementation-wise it should still be mapped memory from
759- // before if we don't have any guard pages.
760- //
761- // Note though that prior writes should all appear as zeros and we can't see
762- // data from the prior instance.
763- //
764- // Note that this part is only implemented on Linux which has
765- // `MADV_DONTNEED`.
766- if cfg ! ( target_os = "linux" ) && guard_size == 0 {
767- unsafe {
768- let ptr = memory. data_ptr ( & store) ;
769- assert_eq ! ( * ptr. offset( page as isize ) , 0 ) ;
717+ let mut store = Store :: new ( & engine, ( ) ) ;
718+ let instance = Instance :: new ( & mut store, & module, & [ ] ) ?;
719+
720+ let grow = instance. get_typed_func :: < u32 , i32 > ( & mut store, "grow" ) ?;
721+ let size = instance. get_typed_func :: < ( ) , u32 > ( & mut store, "size" ) ?;
722+ let i32_load = instance. get_typed_func :: < u32 , i32 > ( & mut store, "i32.load" ) ?;
723+ let i32_store = instance. get_typed_func :: < ( u32 , i32 ) , ( ) > ( & mut store, "i32.store" ) ?;
724+ let memory = instance. get_memory ( & mut store, "memory" ) . unwrap ( ) ;
725+
726+ // basic length 1 tests
727+ // assert_eq!(memory.grow(&mut store, 1)?, 0);
728+ assert_eq ! ( memory. size( & store) , 1 ) ;
729+ assert_eq ! ( size. call( & mut store, ( ) ) ?, 1 ) ;
730+ assert_eq ! ( i32_load. call( & mut store, 0 ) ?, 0 ) ;
731+ assert_eq ! ( i32_load. call( & mut store, 100 ) ?, i32 :: from( b'x' ) ) ;
732+ i32_store. call ( & mut store, ( 0 , 0 ) ) ?;
733+ i32_store. call ( & mut store, ( 100 , i32:: from ( b'y' ) ) ) ?;
734+ assert_eq ! ( i32_load. call( & mut store, 100 ) ?, i32 :: from( b'y' ) ) ;
735+
736+ // basic length 2 tests
737+ let page = 64 * 1024 ;
738+ assert_eq ! ( grow. call( & mut store, 1 ) ?, 1 ) ;
739+ assert_eq ! ( memory. size( & store) , 2 ) ;
740+ assert_eq ! ( size. call( & mut store, ( ) ) ?, 2 ) ;
741+ i32_store. call ( & mut store, ( page, 200 ) ) ?;
742+ assert_eq ! ( i32_load. call( & mut store, page) ?, 200 ) ;
743+
744+ // test writes are visible
745+ i32_store. call ( & mut store, ( 2 , 100 ) ) ?;
746+ assert_eq ! ( i32_load. call( & mut store, 2 ) ?, 100 ) ;
747+
748+ // test growth can't exceed maximum
749+ let too_many = max_size / ( 64 * 1024 ) ;
750+ assert_eq ! ( grow. call( & mut store, too_many as u32 ) ?, -1 ) ;
751+ assert ! ( memory. grow( & mut store, too_many) . is_err( ) ) ;
752+
753+ assert_eq ! ( memory. data( & store) [ page as usize ] , 200 ) ;
754+
755+ // Re-instantiate in another store.
756+ store = Store :: new ( & engine, ( ) ) ;
757+ let instance = Instance :: new ( & mut store, & module, & [ ] ) ?;
758+ let i32_load = instance. get_typed_func :: < u32 , i32 > ( & mut store, "i32.load" ) ?;
759+ let memory = instance. get_memory ( & mut store, "memory" ) . unwrap ( ) ;
760+
761+ // This is out of bounds...
762+ assert ! ( i32_load. call( & mut store, page) . is_err( ) ) ;
763+ assert_eq ! ( memory. data_size( & store) , page as usize ) ;
764+
765+ // ... but implementation-wise it should still be mapped memory from
766+ // before if we don't have any guard pages.
767+ //
768+ // Note though that prior writes should all appear as zeros and we can't see
769+ // data from the prior instance.
770+ //
771+ // Note that this part is only implemented on Linux which has
772+ // `MADV_DONTNEED`.
773+ if cfg ! ( target_os = "linux" ) && guard_size == 0 && !signals_based_traps {
774+ unsafe {
775+ let ptr = memory. data_ptr ( & store) ;
776+ assert_eq ! ( * ptr. offset( page as isize ) , 0 ) ;
777+ }
770778 }
771779 }
772780 }
@@ -1400,6 +1408,49 @@ fn pagemap_scan_enabled_or_disabled() -> Result<()> {
14001408 Ok ( ( ) )
14011409}
14021410
1411+ #[ test]
1412+ #[ cfg_attr( miri, ignore) ]
1413+ fn pooling_reuse_resets ( ) -> Result < ( ) > {
1414+ let mut config = Config :: new ( ) ;
1415+ let mut cfg = crate :: small_pool_config ( ) ;
1416+ cfg. total_memories ( 1 ) ;
1417+ cfg. max_memory_size ( 0x2000000 ) ;
1418+ config. allocation_strategy ( InstanceAllocationStrategy :: Pooling ( cfg) ) ;
1419+ config. memory_guard_size ( 0 ) ;
1420+ config. memory_reservation ( 0x2000000 ) ;
1421+ let engine = Engine :: new ( & config) ?;
1422+
1423+ let a = Module :: new ( & engine, r#"(module (memory (export "m") 10 100))"# ) ?;
1424+ let b = Module :: new (
1425+ & engine,
1426+ r#"
1427+ (module $B
1428+ (memory 5 100)
1429+ (func (export "read_oob") (result i32)
1430+ (i32.load (i32.const 983040))
1431+ )
1432+ )
1433+ "# ,
1434+ ) ?;
1435+
1436+ {
1437+ let mut store = Store :: new ( & engine, ( ) ) ;
1438+ let instance = Instance :: new ( & mut store, & a, & [ ] ) ?;
1439+ let memory = instance. get_memory ( & mut store, "m" ) . unwrap ( ) ;
1440+ memory. grow ( & mut store, 10 ) ?;
1441+ }
1442+
1443+ {
1444+ let mut store = Store :: new ( & engine, ( ) ) ;
1445+ let instance = Instance :: new ( & mut store, & b, & [ ] ) ?;
1446+ let read_oob = instance. get_typed_func :: < ( ) , i32 > ( & mut store, "read_oob" ) ?;
1447+ let trap: Trap = read_oob. call ( & mut store, ( ) ) . unwrap_err ( ) . downcast ( ) ?;
1448+ assert_eq ! ( trap, Trap :: MemoryOutOfBounds ) ;
1449+ }
1450+
1451+ Ok ( ( ) )
1452+ }
1453+
14031454// This test instantiates a memory with an image into a slot in the pooling
14041455// allocator in a way that maps the image into the allocator but fails
14051456// instantiation. Failure here is injected with `ResourceLimiter`. Afterwards
0 commit comments