Fix brk not reset memory
This commit is contained in:
		
							parent
							
								
									0bf4a5a7f7
								
							
						
					
					
						commit
						fb12642254
					
				| @ -11,7 +11,6 @@ use super::vm_util::{ | ||||
|     FileBacked, VMInitializer, VMMapAddr, VMMapOptions, VMMapOptionsBuilder, VMRemapOptions, | ||||
| }; | ||||
| use std::collections::HashSet; | ||||
| use std::sync::atomic::{AtomicUsize, Ordering}; | ||||
| 
 | ||||
| // Used for heap and stack start address randomization.
 | ||||
| const RANGE_FOR_RANDOMIZATION: usize = 256 * 4096; // 1M
 | ||||
| @ -162,7 +161,7 @@ impl<'a, 'b> ProcessVMBuilder<'a, 'b> { | ||||
|         })?; | ||||
|         debug_assert!(heap_range.start() % heap_layout.align() == 0); | ||||
|         trace!("heap range = {:?}", heap_range); | ||||
|         let brk = AtomicUsize::new(heap_range.start()); | ||||
|         let brk = RwLock::new(heap_range.start()); | ||||
|         chunks.insert(chunk_ref); | ||||
| 
 | ||||
|         // Init the stack memory in the process
 | ||||
| @ -274,7 +273,7 @@ pub struct ProcessVM { | ||||
|     elf_ranges: Vec<VMRange>, | ||||
|     heap_range: VMRange, | ||||
|     stack_range: VMRange, | ||||
|     brk: AtomicUsize, | ||||
|     brk: RwLock<usize>, | ||||
|     // Memory safety notes: the mem_chunks field must be the last one.
 | ||||
|     //
 | ||||
|     // Rust drops fields in the same order as they are declared. So by making
 | ||||
| @ -441,25 +440,40 @@ impl ProcessVM { | ||||
|     } | ||||
| 
 | ||||
|     pub fn get_brk(&self) -> usize { | ||||
|         self.brk.load(Ordering::SeqCst) | ||||
|         *self.brk.read().unwrap() | ||||
|     } | ||||
| 
 | ||||
|     pub fn brk(&self, new_brk: usize) -> Result<usize> { | ||||
|     pub fn brk(&self, brk: usize) -> Result<usize> { | ||||
|         let heap_start = self.heap_range.start(); | ||||
|         let heap_end = self.heap_range.end(); | ||||
| 
 | ||||
|         if new_brk >= heap_start && new_brk <= heap_end { | ||||
|             self.brk | ||||
|                 .fetch_update(Ordering::SeqCst, Ordering::SeqCst, |old_brk| Some(new_brk)); | ||||
|             Ok(new_brk) | ||||
|         // Acquire lock first to avoid data-race.
 | ||||
|         let mut brk_guard = self.brk.write().unwrap(); | ||||
| 
 | ||||
|         if brk >= heap_start && brk <= heap_end { | ||||
|             // Get page-aligned brk address.
 | ||||
|             let new_brk = align_up(brk, PAGE_SIZE); | ||||
|             // Get page-aligned old brk address.
 | ||||
|             let old_brk = align_up(*brk_guard, PAGE_SIZE); | ||||
| 
 | ||||
|             // Reset the memory when brk shrinks.
 | ||||
|             if new_brk < old_brk { | ||||
|                 let shrink_brk_range = | ||||
|                     VMRange::new(new_brk, old_brk).expect("shrink brk range must be valid"); | ||||
|                 USER_SPACE_VM_MANAGER.reset_memory(shrink_brk_range)?; | ||||
|             } | ||||
| 
 | ||||
|             // Return the user-specified brk address without page aligned. This is same as Linux.
 | ||||
|             *brk_guard = brk; | ||||
|             Ok(brk) | ||||
|         } else { | ||||
|             if new_brk < heap_start { | ||||
|             if brk < heap_start { | ||||
|                 error!("New brk address is too low"); | ||||
|             } else if new_brk > heap_end { | ||||
|             } else if brk > heap_end { | ||||
|                 error!("New brk address is too high"); | ||||
|             } | ||||
| 
 | ||||
|             Ok(self.get_brk()) | ||||
|             Ok(*brk_guard) | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|  | ||||
| @ -362,6 +362,48 @@ impl VMManager { | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     // Reset memory permission to default (R/W) and reset the memory contents to zero. Currently only used by brk.
 | ||||
|     pub fn reset_memory(&self, reset_range: VMRange) -> Result<()> { | ||||
|         let intersect_chunks = { | ||||
|             let chunks = self | ||||
|                 .internal() | ||||
|                 .chunks | ||||
|                 .iter() | ||||
|                 .filter(|&chunk| chunk.range().intersect(&reset_range).is_some()) | ||||
|                 .map(|chunk| chunk.clone()) | ||||
|                 .collect::<Vec<_>>(); | ||||
| 
 | ||||
|             // In the heap area, there shouldn't be any default chunks or chunks owned by other process.
 | ||||
|             if chunks | ||||
|                 .iter() | ||||
|                 .any(|chunk| !chunk.is_owned_by_current_process() || !chunk.is_single_vma()) | ||||
|             { | ||||
|                 return_errno!(EINVAL, "There is something wrong with the intersect chunks"); | ||||
|             } | ||||
|             chunks | ||||
|         }; | ||||
| 
 | ||||
|         intersect_chunks.iter().for_each(|chunk| { | ||||
|             if let ChunkType::SingleVMA(vma) = chunk.internal() { | ||||
|                 if let Some(intersection_range) = chunk.range().intersect(&reset_range) { | ||||
|                     let mut internal_manager = self.internal(); | ||||
|                     internal_manager.mprotect_single_vma_chunk( | ||||
|                         &chunk, | ||||
|                         intersection_range, | ||||
|                         VMPerms::DEFAULT, | ||||
|                     ); | ||||
| 
 | ||||
|                     unsafe { | ||||
|                         let buf = intersection_range.as_slice_mut(); | ||||
|                         buf.iter_mut().for_each(|b| *b = 0) | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         }); | ||||
| 
 | ||||
|         Ok(()) | ||||
|     } | ||||
| 
 | ||||
|     pub fn msync(&self, addr: usize, size: usize) -> Result<()> { | ||||
|         let sync_range = VMRange::new_with_size(addr, size)?; | ||||
|         let chunk = { | ||||
|  | ||||
| @ -22,7 +22,7 @@ TESTS ?= env empty hello_world malloc mmap file fs_perms getpid spawn sched pipe | ||||
| 	truncate readdir mkdir open stat link symlink chmod chown tls pthread system_info rlimit \
 | ||||
| 	server server_epoll unix_socket cout hostfs cpuid rdtsc device sleep exit_group posix_flock \
 | ||||
| 	ioctl fcntl eventfd emulate_syscall access signal sysinfo prctl rename procfs wait \
 | ||||
| 	spawn_attribute exec statfs random umask pgrp vfork mount flock utimes shm epoll | ||||
| 	spawn_attribute exec statfs random umask pgrp vfork mount flock utimes shm epoll brk | ||||
| # Benchmarks: need to be compiled and run by bench-% target
 | ||||
| BENCHES := spawn_and_exit_latency pipe_throughput unix_socket_throughput | ||||
| 
 | ||||
|  | ||||
							
								
								
									
										5
									
								
								test/brk/Makefile
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										5
									
								
								test/brk/Makefile
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,5 @@ | ||||
| include ../test_common.mk | ||||
| 
 | ||||
| EXTRA_C_FLAGS := | ||||
| EXTRA_LINK_FLAGS := | ||||
| BIN_ARGS := | ||||
							
								
								
									
										195
									
								
								test/brk/main.c
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										195
									
								
								test/brk/main.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,195 @@ | ||||
| #include <unistd.h> | ||||
| #include <stdlib.h> | ||||
| #include <sys/syscall.h> | ||||
| #include <stdint.h> | ||||
| #include <sys/mman.h> | ||||
| #include "test.h" | ||||
| 
 | ||||
| // ============================================================================
 | ||||
| // Helper function
 | ||||
| // ============================================================================
 | ||||
| 
 | ||||
| #define PAGE_SIZE 4096 | ||||
| 
 | ||||
| const static uint8_t magic_num_01 = 0xFF; | ||||
| typedef struct syscall_args { | ||||
|     int num; | ||||
|     unsigned long arg0; | ||||
| } syscall_args_t; | ||||
| 
 | ||||
| static inline uint64_t native_syscall(syscall_args_t *p) { | ||||
|     uint64_t ret; | ||||
|     register int num asm ("rax") = p->num; | ||||
|     register unsigned long arg0 asm ("rdi") = p->arg0; | ||||
| 
 | ||||
|     asm volatile("syscall" | ||||
|                  : "=a" (ret) | ||||
|                  : "r" (num), "r" (arg0)); | ||||
|     return ret; | ||||
| } | ||||
| 
 | ||||
| static uint64_t brk_syscall(uint64_t brk) { | ||||
|     syscall_args_t brk_arg = { | ||||
|         .num = __NR_brk, | ||||
|         .arg0 = brk, | ||||
|     }; | ||||
| 
 | ||||
|     return native_syscall(&brk_arg); | ||||
| } | ||||
| 
 | ||||
| // ============================================================================
 | ||||
| // Test cases for access
 | ||||
| // ============================================================================
 | ||||
| 
 | ||||
| static int test_brk_shrinks() { | ||||
|     char *zero_buf = malloc(PAGE_SIZE * 2); | ||||
|     if (zero_buf == NULL) { | ||||
|         THROW_ERROR("malloc failed"); | ||||
|     } | ||||
|     memset(zero_buf, 0, PAGE_SIZE * 2); | ||||
| 
 | ||||
|     uint64_t original_brk = brk_syscall(0); | ||||
|     if (original_brk == 0) { | ||||
|         THROW_ERROR("sbrk failed"); | ||||
|     } | ||||
|     printf("original brk = %lx\n", original_brk); | ||||
| 
 | ||||
|     // increase brk
 | ||||
|     printf("increase brk\n"); | ||||
|     uint64_t ret = brk_syscall(original_brk + PAGE_SIZE * 4); | ||||
|     if (ret == 0) { | ||||
|         THROW_ERROR("extend brk failed"); | ||||
|     } | ||||
| 
 | ||||
|     // set some values to the brk memory
 | ||||
|     uint64_t test_range_start = original_brk + PAGE_SIZE * 2; | ||||
|     for (int i = 0; i < PAGE_SIZE; i++) { | ||||
|         *(int *)test_range_start = magic_num_01; | ||||
|     } | ||||
| 
 | ||||
|     // decrease brk
 | ||||
|     printf("decrease brk\n"); | ||||
|     ret = brk_syscall(original_brk + PAGE_SIZE * 2); | ||||
|     if (ret != test_range_start) { | ||||
|         THROW_ERROR("shrink brk failed"); | ||||
|     } | ||||
|     printf("test range start = %lx\n", test_range_start); | ||||
| 
 | ||||
|     // increase brk
 | ||||
|     uint64_t test_range_end = brk_syscall(original_brk + PAGE_SIZE * 4); | ||||
|     if (test_range_end != original_brk + PAGE_SIZE * 4) { | ||||
|         THROW_ERROR("extend brk failed"); | ||||
|     } | ||||
| 
 | ||||
|     if ( memcmp((const void *)test_range_start, zero_buf, PAGE_SIZE * 2) != 0) { | ||||
|         THROW_ERROR("sbrk not reset memory"); | ||||
|     } | ||||
| 
 | ||||
|     free(zero_buf); | ||||
| 
 | ||||
|     return 0; | ||||
| } | ||||
| 
 | ||||
| #ifdef SGX_MODE_HW | ||||
| // This test case will fail in simulation mode. Because the raw syscall interface are not handled by Occlum
 | ||||
| // in simulation mode.
 | ||||
| //
 | ||||
| // Use brk to allocate 4 pages and test brk and mprotect
 | ||||
| //    original brk
 | ||||
| //       | page 00          page 02
 | ||||
| //       |         page 01          page 03
 | ||||
| // ...---|-------|-------|-------|-------|
 | ||||
| static int test_brk_shrinks_spans_multiple_chunks() { | ||||
|     const static uint8_t magic_num_02 = 0xFE; | ||||
|     char *zero_buf = malloc(PAGE_SIZE * 4); | ||||
|     if (zero_buf == NULL) { | ||||
|         THROW_ERROR("malloc failed"); | ||||
|     } | ||||
|     memset(zero_buf, 0, PAGE_SIZE * 4); | ||||
| 
 | ||||
|     size_t original_brk = brk_syscall(0); | ||||
|     if (original_brk == 0) { | ||||
|         THROW_ERROR("brk failed"); | ||||
|     } | ||||
|     printf("original brk = %lx\n", original_brk); | ||||
| 
 | ||||
|     // increase brk to the end of page 03
 | ||||
|     size_t ret = brk_syscall(original_brk + PAGE_SIZE * 4); | ||||
|     if (ret != original_brk + PAGE_SIZE * 4) { | ||||
|         THROW_ERROR("extend brk failed"); | ||||
|     } | ||||
| 
 | ||||
|     // set some values to the brk memory page 02
 | ||||
|     size_t test_range_start = original_brk + PAGE_SIZE * 2; | ||||
|     for (int i = 0; i < PAGE_SIZE; i++) { | ||||
|         *(int *)test_range_start = magic_num_01; | ||||
|     } | ||||
| 
 | ||||
|     // mprotect page 01 - 03 to PROT_NONE and decrease brk to the end of page 00
 | ||||
|     int rc = mprotect((void *)(original_brk + PAGE_SIZE * 1), PAGE_SIZE * 3, PROT_NONE); | ||||
|     if (rc < 0) { | ||||
|         THROW_ERROR("mprotect failure"); | ||||
|     } | ||||
|     ret = brk_syscall(original_brk + PAGE_SIZE * 1); | ||||
|     if (ret != original_brk + PAGE_SIZE * 1) { | ||||
|         THROW_ERROR("shrink brk failed"); | ||||
|     } | ||||
| 
 | ||||
|     // increase brk to the end of page 02
 | ||||
|     ret = brk_syscall(original_brk + PAGE_SIZE * 3); | ||||
|     if (ret != original_brk + PAGE_SIZE * 3) { | ||||
|         THROW_ERROR("extend brk failed"); | ||||
|     } | ||||
| 
 | ||||
|     // set some values to the brk memory page 01
 | ||||
|     test_range_start = original_brk + PAGE_SIZE * 1; | ||||
|     for (int i = 0; i < PAGE_SIZE; i++) { | ||||
|         *(int *)test_range_start = magic_num_02; | ||||
|     } | ||||
| 
 | ||||
|     // decrease brk again to the end of page 00
 | ||||
|     rc = mprotect((void *)(original_brk + PAGE_SIZE * 1), PAGE_SIZE * 2, PROT_NONE); | ||||
|     if (rc < 0) { | ||||
|         THROW_ERROR("mprotect failure"); | ||||
|     } | ||||
|     ret = brk_syscall(original_brk + PAGE_SIZE * 1); | ||||
|     if (ret != original_brk + PAGE_SIZE * 1) { | ||||
|         THROW_ERROR("shrink brk failed"); | ||||
|     } | ||||
| 
 | ||||
|     // increase brk to the end of page 03
 | ||||
|     ret = brk_syscall(original_brk + PAGE_SIZE * 4); | ||||
|     if (ret != original_brk + PAGE_SIZE * 4) { | ||||
|         THROW_ERROR("extend brk failed"); | ||||
|     } | ||||
| 
 | ||||
|     if ( memcmp((const void *)original_brk, zero_buf, PAGE_SIZE * 4) != 0) { | ||||
|         THROW_ERROR("brk not reset memory"); | ||||
|     } | ||||
| 
 | ||||
|     // decrease brk to the original brk
 | ||||
|     ret = brk_syscall(original_brk); | ||||
|     if (ret != original_brk) { | ||||
|         THROW_ERROR("shrink brk failed"); | ||||
|     } | ||||
| 
 | ||||
|     free(zero_buf); | ||||
| 
 | ||||
|     return 0; | ||||
| } | ||||
| #endif | ||||
| 
 | ||||
| // ============================================================================
 | ||||
| // Test suite main
 | ||||
| // ============================================================================
 | ||||
| 
 | ||||
| static test_case_t test_cases[] = { | ||||
|     TEST_CASE(test_brk_shrinks), | ||||
| #ifdef SGX_MODE_HW | ||||
|     TEST_CASE(test_brk_shrinks_spans_multiple_chunks), | ||||
| #endif | ||||
| }; | ||||
| 
 | ||||
| int main(int argc, const char *argv[]) { | ||||
|     return test_suite_run(test_cases, ARRAY_SIZE(test_cases)); | ||||
| } | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user