@@ -670,103 +670,106 @@ 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 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+ ) ?;
712711
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 ) ;
712+ let mut store = Store :: new ( & engine, ( ) ) ;
713+ let instance = Instance :: new ( & mut store, & module, & [ ] ) ?;
714+
715+ let grow = instance. get_typed_func :: < u32 , i32 > ( & mut store, "grow" ) ?;
716+ let size = instance. get_typed_func :: < ( ) , u32 > ( & mut store, "size" ) ?;
717+ let i32_load = instance. get_typed_func :: < u32 , i32 > ( & mut store, "i32.load" ) ?;
718+ let i32_store = instance. get_typed_func :: < ( u32 , i32 ) , ( ) > ( & mut store, "i32.store" ) ?;
719+ let memory = instance. get_memory ( & mut store, "memory" ) . unwrap ( ) ;
720+
721+ // basic length 1 tests
722+ // assert_eq!(memory.grow(&mut store, 1)?, 0);
723+ assert_eq ! ( memory. size( & store) , 1 ) ;
724+ assert_eq ! ( size. call( & mut store, ( ) ) ?, 1 ) ;
725+ assert_eq ! ( i32_load. call( & mut store, 0 ) ?, 0 ) ;
726+ assert_eq ! ( i32_load. call( & mut store, 100 ) ?, i32 :: from( b'x' ) ) ;
727+ i32_store. call ( & mut store, ( 0 , 0 ) ) ?;
728+ i32_store. call ( & mut store, ( 100 , i32:: from ( b'y' ) ) ) ?;
729+ assert_eq ! ( i32_load. call( & mut store, 100 ) ?, i32 :: from( b'y' ) ) ;
730+
731+ // basic length 2 tests
732+ let page = 64 * 1024 ;
733+ assert_eq ! ( grow. call( & mut store, 1 ) ?, 1 ) ;
734+ assert_eq ! ( memory. size( & store) , 2 ) ;
735+ assert_eq ! ( size. call( & mut store, ( ) ) ?, 2 ) ;
736+ i32_store. call ( & mut store, ( page, 200 ) ) ?;
737+ assert_eq ! ( i32_load. call( & mut store, page) ?, 200 ) ;
738+
739+ // test writes are visible
740+ i32_store. call ( & mut store, ( 2 , 100 ) ) ?;
741+ assert_eq ! ( i32_load. call( & mut store, 2 ) ?, 100 ) ;
742+
743+ // test growth can't exceed maximum
744+ let too_many = max_size / ( 64 * 1024 ) ;
745+ assert_eq ! ( grow. call( & mut store, too_many as u32 ) ?, -1 ) ;
746+ assert ! ( memory. grow( & mut store, too_many) . is_err( ) ) ;
747+
748+ assert_eq ! ( memory. data( & store) [ page as usize ] , 200 ) ;
749+
750+ // Re-instantiate in another store.
751+ store = Store :: new ( & engine, ( ) ) ;
752+ let instance = Instance :: new ( & mut store, & module, & [ ] ) ?;
753+ let i32_load = instance. get_typed_func :: < u32 , i32 > ( & mut store, "i32.load" ) ?;
754+ let memory = instance. get_memory ( & mut store, "memory" ) . unwrap ( ) ;
755+
756+ // This is out of bounds...
757+ assert ! ( i32_load. call( & mut store, page) . is_err( ) ) ;
758+ assert_eq ! ( memory. data_size( & store) , page as usize ) ;
759+
760+ // ... but implementation-wise it should still be mapped memory from
761+ // before if we don't have any guard pages.
762+ //
763+ // Note though that prior writes should all appear as zeros and we can't see
764+ // data from the prior instance.
765+ //
766+ // Note that this part is only implemented on Linux which has
767+ // `MADV_DONTNEED`.
768+ if cfg ! ( target_os = "linux" ) && guard_size == 0 && !signals_based_traps {
769+ unsafe {
770+ let ptr = memory. data_ptr ( & store) ;
771+ assert_eq ! ( * ptr. offset( page as isize ) , 0 ) ;
772+ }
770773 }
771774 }
772775 }
@@ -1354,6 +1357,49 @@ fn pagemap_scan_enabled_or_disabled() -> Result<()> {
13541357 Ok ( ( ) )
13551358}
13561359
1360+ #[ test]
1361+ #[ cfg_attr( miri, ignore) ]
1362+ fn pooling_reuse_resets ( ) -> Result < ( ) > {
1363+ let mut config = Config :: new ( ) ;
1364+ let mut cfg = crate :: small_pool_config ( ) ;
1365+ cfg. total_memories ( 1 ) ;
1366+ cfg. max_memory_size ( 0x2000000 ) ;
1367+ config. allocation_strategy ( InstanceAllocationStrategy :: Pooling ( cfg) ) ;
1368+ config. memory_guard_size ( 0 ) ;
1369+ config. memory_reservation ( 0x2000000 ) ;
1370+ let engine = Engine :: new ( & config) ?;
1371+
1372+ let a = Module :: new ( & engine, r#"(module (memory (export "m") 10 100))"# ) ?;
1373+ let b = Module :: new (
1374+ & engine,
1375+ r#"
1376+ (module $B
1377+ (memory 5 100)
1378+ (func (export "read_oob") (result i32)
1379+ (i32.load (i32.const 983040))
1380+ )
1381+ )
1382+ "# ,
1383+ ) ?;
1384+
1385+ {
1386+ let mut store = Store :: new ( & engine, ( ) ) ;
1387+ let instance = Instance :: new ( & mut store, & a, & [ ] ) ?;
1388+ let memory = instance. get_memory ( & mut store, "m" ) . unwrap ( ) ;
1389+ memory. grow ( & mut store, 10 ) ?;
1390+ }
1391+
1392+ {
1393+ let mut store = Store :: new ( & engine, ( ) ) ;
1394+ let instance = Instance :: new ( & mut store, & b, & [ ] ) ?;
1395+ let read_oob = instance. get_typed_func :: < ( ) , i32 > ( & mut store, "read_oob" ) ?;
1396+ let trap: Trap = read_oob. call ( & mut store, ( ) ) . unwrap_err ( ) . downcast ( ) ?;
1397+ assert_eq ! ( trap, Trap :: MemoryOutOfBounds ) ;
1398+ }
1399+
1400+ Ok ( ( ) )
1401+ }
1402+
13571403// This test instantiates a memory with an image into a slot in the pooling
13581404// allocator in a way that maps the image into the allocator but fails
13591405// instantiation. Failure here is injected with `ResourceLimiter`. Afterwards
0 commit comments