@@ -78,7 +78,7 @@ fn allocate_one_mib(_ctx: &ReceiveContext, _host: &Host<State>) -> ReceiveResult
7878#[ receive( contract = "bump_alloc_tests" , name = "dealloc_last_optimization" ) ]
7979fn dealloc_last_optimization ( ctx : & ReceiveContext , _host : & Host < State > ) -> ReceiveResult < ( ) > {
8080 let n: u64 = ctx. parameter_cursor ( ) . get ( ) ?;
81- let mut long_lasting = black_box ( Box :: new ( 0u64 ) ) ;
81+ let mut long_lived = black_box ( Box :: new ( 0u64 ) ) ;
8282 for i in 0 ..n {
8383 let addr_a = {
8484 let a = black_box ( vec ! [ i] ) ;
@@ -89,11 +89,76 @@ fn dealloc_last_optimization(ctx: &ReceiveContext, _host: &Host<State>) -> Recei
8989 b. as_ptr ( ) as usize
9090 } ;
9191 if addr_a != addr_b {
92- * long_lasting += 1 ;
92+ * long_lived += 1 ;
9393 }
9494 }
95- if * long_lasting != 0 {
95+ if * long_lived != 0 {
9696 return Err ( Reject :: default ( ) ) ;
9797 }
9898 Ok ( ( ) )
9999}
100+
101+ /// Tests that the allocator does not overwrite the static and stack regions of
102+ /// the memory.
103+ ///
104+ /// The general idea is to create static and local variables, perform some
105+ /// recursive calls and subsequently check the integrity of all the data.
106+ /// The details are in written in the internal comments and in the documentation
107+ /// for the recursive helper function `appender`.
108+ #[ receive( contract = "bump_alloc_tests" , name = "stack_and_static" ) ]
109+ fn stack_and_static ( ctx : & ReceiveContext , _host : & Host < State > ) -> ReceiveResult < ( ) > {
110+ // Create a static variable and some local variables.
111+ static ON_ODD : & str = "ODD" ;
112+ let ( mut text, n, on_even) : ( String , u32 , String ) = ctx. parameter_cursor ( ) . get ( ) ?;
113+ let original_n = n;
114+ let original_text_len = text. len ( ) ;
115+ // Allocate a long lived box on the heap.
116+ let long_lived: Box < Vec < u32 > > = black_box ( Box :: new ( ( 0 ..n) . collect ( ) ) ) ;
117+ // Run the appender function. Wrapped in a [`black_box`] to ensure that the
118+ // compiler won't try to optimize the internals away.
119+ black_box ( appender ( & mut text, n, & on_even, ON_ODD ) ) ;
120+ // Use some of the local variables defined before the recursive call.
121+ // Abort if `n` should have been altered.
122+ if original_n != n {
123+ return Err ( Reject :: default ( ) ) ;
124+ }
125+ // Abort if the text length hasn't increased when `n` is positive.
126+ if n != 0 && original_text_len == text. len ( ) {
127+ return Err ( Reject :: default ( ) ) ;
128+ }
129+ let n_usize = n as usize ;
130+ // Use the box to ensure that it is long lived.
131+ if long_lived[ n_usize - 100 ] != n - 100 {
132+ return Err ( Reject :: default ( ) ) ;
133+ }
134+ // Calculate the expected length of the `text` and check it.
135+ let expected_len = original_text_len
136+ + ( ON_ODD . len ( ) * ( n_usize / 2 ) ) // Append `ON_ODD` half the times.
137+ + ( on_even. len ( ) * ( n_usize / 2 ) ) // Append `on_even` half the times.
138+ + ( ON_ODD . len ( ) * ( n_usize % 2 ) ) ; // Append an extra `ON_ODD` if `n` is odd.
139+ if expected_len != text. len ( ) {
140+ return Err ( Reject :: default ( ) ) ;
141+ }
142+ Ok ( ( ) )
143+ }
144+
145+ /// Recursively alternates between appending `on_even` and `on_odd` to the
146+ /// `text` `n` times.
147+ ///
148+ /// For example with ("ORIGINAL", 3, "EVEN", "ODD"), the final `text` becomes:
149+ ///
150+ /// | | |
151+ /// ORIGINALODDEVENODD
152+ /// | | |
153+ fn appender ( text : & mut String , n : u32 , on_even : & str , on_odd : & str ) {
154+ if n == 0 {
155+ return ;
156+ }
157+ let is_even = n % 2 == 0 ;
158+ if is_even {
159+ text. push_str ( on_even) ;
160+ } else {
161+ text. push_str ( on_odd) ;
162+ }
163+ appender ( text, n - 1 , on_even, on_odd) ;
164+ }
0 commit comments