Browse Source

Greatly enhanced infinite loop mitigation.

Marco Bambini 8 years ago
parent
commit
c67888e6d2
4 changed files with 55 additions and 25 deletions
  1. 16 10
      src/runtime/gravity_core.c
  2. 32 13
      src/runtime/gravity_vm.c
  3. 6 2
      src/runtime/gravity_vmmacros.h
  4. 1 0
      src/utils/gravity_utils.c

+ 16 - 10
src/runtime/gravity_core.c

@@ -425,7 +425,9 @@ static bool object_real_load (gravity_vm *vm, gravity_value_t *args, uint16_t na
 		if (!meta->is_inited) {
 		if (!meta->is_inited) {
 			meta->is_inited = true;
 			meta->is_inited = true;
 			gravity_closure_t *closure = gravity_class_lookup_constructor(meta, 0);
 			gravity_closure_t *closure = gravity_class_lookup_constructor(meta, 0);
-			if (closure) gravity_vm_runclosure(vm, closure, VALUE_FROM_OBJECT(meta), NULL, 0);
+            if (closure) {
+                if (!gravity_vm_runclosure(vm, closure, VALUE_FROM_OBJECT(meta), NULL, 0)) return false;
+            }
 		}
 		}
 	}
 	}
 	
 	
@@ -507,7 +509,9 @@ static bool object_store (gravity_vm *vm, gravity_value_t *args, uint16_t nargs,
 		if (!meta->is_inited) {
 		if (!meta->is_inited) {
 			meta->is_inited = true;
 			meta->is_inited = true;
 			gravity_closure_t *closure = gravity_class_lookup_constructor(meta, 0);
 			gravity_closure_t *closure = gravity_class_lookup_constructor(meta, 0);
-			if (closure) gravity_vm_runclosure(vm, closure, VALUE_FROM_OBJECT(meta), NULL, 0);
+            if (closure) {
+                if (!gravity_vm_runclosure(vm, closure, VALUE_FROM_OBJECT(meta), NULL, 0)) return false;
+            }
 		}
 		}
 	}
 	}
 	
 	
@@ -773,7 +777,7 @@ static bool list_loop (gravity_vm *vm, gravity_value_t *args, uint16_t nargs, ui
 	
 	
 	nanotime_t t1 = nanotime();
 	nanotime_t t1 = nanotime();
 	while (i < n) {
 	while (i < n) {
-		gravity_vm_runclosure(vm, closure, value, &marray_get(list->array, i), 1);
+        if (!gravity_vm_runclosure(vm, closure, value, &marray_get(list->array, i), 1)) return false;
 		++i;
 		++i;
 	}
 	}
 	nanotime_t t2 = nanotime();
 	nanotime_t t2 = nanotime();
@@ -798,7 +802,10 @@ static bool list_join (gravity_vm *vm, gravity_value_t *args, uint16_t nargs, ui
 	// traverse list and append each item
 	// traverse list and append each item
 	while (i < n) {
 	while (i < n) {
 		gravity_value_t value = convert_value2string(vm, marray_get(list->array, i));
 		gravity_value_t value = convert_value2string(vm, marray_get(list->array, i));
-		if (VALUE_ISA_ERROR(value)) RETURN_VALUE(value, rindex);
+        if (VALUE_ISA_ERROR(value)) {
+            mem_free(buffer);
+            RETURN_VALUE(value, rindex);
+        }
 		
 		
 		const char *s2 = VALUE_AS_STRING(value)->s;
 		const char *s2 = VALUE_AS_STRING(value)->s;
 		uint32_t req = VALUE_AS_STRING(value)->len;
 		uint32_t req = VALUE_AS_STRING(value)->len;
@@ -916,7 +923,7 @@ static bool map_loop (gravity_vm *vm, gravity_value_t *args, uint16_t nargs, uin
 	
 	
 	nanotime_t t1 = nanotime();
 	nanotime_t t1 = nanotime();
 	while (i < n) {
 	while (i < n) {
-		gravity_vm_runclosure(vm, closure, value, &marray_get(list->array, i), 1);
+        if (!gravity_vm_runclosure(vm, closure, value, &marray_get(list->array, i), 1)) return false;
 		++i;
 		++i;
 	}
 	}
 	nanotime_t t2 = nanotime();
 	nanotime_t t2 = nanotime();
@@ -946,14 +953,14 @@ static bool range_loop (gravity_vm *vm, gravity_value_t *args, uint16_t nargs, u
 		register gravity_int_t n = range->to;
 		register gravity_int_t n = range->to;
 		register gravity_int_t i = range->from;
 		register gravity_int_t i = range->from;
 		while (i <= n) {
 		while (i <= n) {
-			gravity_vm_runclosure(vm, closure, value, &VALUE_FROM_INT(i), 1);
+            if (!gravity_vm_runclosure(vm, closure, value, &VALUE_FROM_INT(i), 1)) return false;
 			++i;
 			++i;
 		}
 		}
 	} else {
 	} else {
 		register gravity_int_t n = range->from;			// 5...1
 		register gravity_int_t n = range->from;			// 5...1
 		register gravity_int_t i = range->to;
 		register gravity_int_t i = range->to;
 		while (n >= i) {
 		while (n >= i) {
-			gravity_vm_runclosure(vm, closure, value, &VALUE_FROM_INT(n), 1);
+            if (!gravity_vm_runclosure(vm, closure, value, &VALUE_FROM_INT(n), 1)) return false;
 			--n;
 			--n;
 		}
 		}
 	}
 	}
@@ -1093,7 +1100,7 @@ static bool closure_apply (gravity_vm *vm, gravity_value_t *args, uint16_t nargs
 	gravity_value_t self_value = GET_VALUE(1);
 	gravity_value_t self_value = GET_VALUE(1);
 	gravity_list_t *list = VALUE_AS_LIST(GET_VALUE(2));
 	gravity_list_t *list = VALUE_AS_LIST(GET_VALUE(2));
 	
 	
-	gravity_vm_runclosure(vm, closure, self_value, list->array.p, (uint16_t)marray_size(list->array));
+    if (!gravity_vm_runclosure(vm, closure, self_value, list->array.p, (uint16_t)marray_size(list->array))) return false;
 	gravity_value_t result = gravity_vm_result(vm);
 	gravity_value_t result = gravity_vm_result(vm);
 	
 	
 	RETURN_VALUE(result, rindex);
 	RETURN_VALUE(result, rindex);
@@ -1319,7 +1326,7 @@ static bool int_loop (gravity_vm *vm, gravity_value_t *args, uint16_t nargs, uin
 	
 	
 	nanotime_t t1 = nanotime();
 	nanotime_t t1 = nanotime();
 	while (i < n) {
 	while (i < n) {
-		gravity_vm_runclosure(vm, closure, value, &VALUE_FROM_INT(i), 1);
+        if (!gravity_vm_runclosure(vm, closure, value, &VALUE_FROM_INT(i), 1)) return false;
 		++i;
 		++i;
 	}
 	}
 	nanotime_t t2 = nanotime();
 	nanotime_t t2 = nanotime();
@@ -1753,7 +1760,6 @@ static bool string_loadat (gravity_vm *vm, gravity_value_t *args, uint16_t nargs
 		
 		
 		// Reverse the string, and reverse the indices
 		// Reverse the string, and reverse the indices
 		first_index = original_len - first_index -1;
 		first_index = original_len - first_index -1;
-		second_index = original_len - second_index -1;
 
 
 		// reverse the String
 		// reverse the String
 		int i = original_len - 1;
 		int i = original_len - 1;

+ 32 - 13
src/runtime/gravity_vm.c

@@ -167,7 +167,13 @@ static inline gravity_callframe_t *gravity_new_callframe (gravity_vm *vm, gravit
 	// check if there are enought slots in the call frame and optionally create new cframes
 	// check if there are enought slots in the call frame and optionally create new cframes
 	if (fiber->framesalloc - fiber->nframes < 1) {
 	if (fiber->framesalloc - fiber->nframes < 1) {
 		uint32_t new_size = fiber->framesalloc * 2;
 		uint32_t new_size = fiber->framesalloc * 2;
-		fiber->frames = (gravity_callframe_t *) mem_realloc(fiber->frames, sizeof(gravity_callframe_t) * new_size);
+        void *ptr = mem_realloc(fiber->frames, sizeof(gravity_callframe_t) * new_size);
+        if (!ptr) {
+            // frames reallocation failed means that there is a very high probability to be into an infinite loop
+            report_runtime_error(vm, GRAVITY_ERROR_RUNTIME, "Infinite loop detected. Current execution must be aborted.");
+            return NULL;
+        }
+        fiber->frames = (gravity_callframe_t *)ptr;
 		fiber->framesalloc = new_size;
 		fiber->framesalloc = new_size;
 		STAT_FRAMES_REALLOCATED(vm);
 		STAT_FRAMES_REALLOCATED(vm);
 	}
 	}
@@ -179,7 +185,7 @@ static inline gravity_callframe_t *gravity_new_callframe (gravity_vm *vm, gravit
 	return &fiber->frames[fiber->nframes - 1];
 	return &fiber->frames[fiber->nframes - 1];
 }
 }
 
 
-static inline void gravity_check_stack (gravity_vm *vm, gravity_fiber_t *fiber, uint32_t rneeds, gravity_value_t **stackstart) {
+static inline bool gravity_check_stack (gravity_vm *vm, gravity_fiber_t *fiber, uint32_t rneeds, gravity_value_t **stackstart) {
 	#pragma unused(vm)
 	#pragma unused(vm)
 	
 	
 	// update stacktop pointer before a call
 	// update stacktop pointer before a call
@@ -188,17 +194,25 @@ static inline void gravity_check_stack (gravity_vm *vm, gravity_fiber_t *fiber,
 	// check stack size
 	// check stack size
 	uint32_t stack_size = (uint32_t)(fiber->stacktop - fiber->stack);
 	uint32_t stack_size = (uint32_t)(fiber->stacktop - fiber->stack);
 	uint32_t stack_needed = MAXNUM(stack_size + rneeds, DEFAULT_MINSTACK_SIZE);
 	uint32_t stack_needed = MAXNUM(stack_size + rneeds, DEFAULT_MINSTACK_SIZE);
-	if (fiber->stackalloc >= stack_needed) return;
+	if (fiber->stackalloc >= stack_needed) return true;
 	
 	
-	// perform stack reallocation
+	// perform stack reallocation (power_of2_ceil returns 0 if argument is bigger than 2^31)
 	uint32_t new_size = power_of2_ceil(fiber->stackalloc + stack_needed);
 	uint32_t new_size = power_of2_ceil(fiber->stackalloc + stack_needed);
 	gravity_value_t *old_stack = fiber->stack;
 	gravity_value_t *old_stack = fiber->stack;
-	fiber->stack = (gravity_value_t *) mem_realloc(fiber->stack, sizeof(gravity_value_t) * new_size);
+    void *ptr = (new_size) ? mem_realloc(fiber->stack, sizeof(gravity_value_t) * new_size) : NULL;
+    if (!ptr) {
+        // restore stacktop to previous state
+        fiber->stacktop -= rneeds;
+        
+        // stack reallocation failed means that there is a very high probability to be into an infinite loop
+        RUNTIME_ERROR("Infinite loop detected. Current execution must be aborted.");
+    }
+    fiber->stack = (gravity_value_t *)ptr;
 	fiber->stackalloc = new_size;
 	fiber->stackalloc = new_size;
 	STAT_STACK_REALLOCATED(vm);
 	STAT_STACK_REALLOCATED(vm);
 	
 	
 	// check if reallocation moved the stack
 	// check if reallocation moved the stack
-	if (fiber->stack == old_stack) return;
+	if (fiber->stack == old_stack) return true;
 	
 	
 	// re-compute ptr offset
 	// re-compute ptr offset
 	ptrdiff_t offset = (ptrdiff_t)(fiber->stack - old_stack);
 	ptrdiff_t offset = (ptrdiff_t)(fiber->stack - old_stack);
@@ -220,6 +234,8 @@ static inline void gravity_check_stack (gravity_vm *vm, gravity_fiber_t *fiber,
 	
 	
 	// stack is changed so update currently used stackstart
 	// stack is changed so update currently used stackstart
 	*stackstart += offset;
 	*stackstart += offset;
+    
+    return true;
 }
 }
 
 
 static gravity_upvalue_t *gravity_capture_upvalue (gravity_vm *vm, gravity_fiber_t *fiber, gravity_value_t *value) {
 static gravity_upvalue_t *gravity_capture_upvalue (gravity_vm *vm, gravity_fiber_t *fiber, gravity_value_t *value) {
@@ -346,6 +362,7 @@ static bool gravity_vm_exec (gravity_vm *vm) {
 						// backup register r1 because it can be overwrite to return a closure
 						// backup register r1 because it can be overwrite to return a closure
 						gravity_value_t r1copy = STACK_GET(r1);
 						gravity_value_t r1copy = STACK_GET(r1);
 						if (!closure->f->internal(vm, &stackstart[rwin], 2, r1)) {
 						if (!closure->f->internal(vm, &stackstart[rwin], 2, r1)) {
+                            if (vm->aborted) return false;
 							
 							
 							// check for special getter trick
 							// check for special getter trick
 							if (VALUE_ISA_CLOSURE(STACK_GET(r1))) {
 							if (VALUE_ISA_CLOSURE(STACK_GET(r1))) {
@@ -474,6 +491,8 @@ static bool gravity_vm_exec (gravity_vm *vm) {
 						// backup register r1 because it can be overwrite to return a closure
 						// backup register r1 because it can be overwrite to return a closure
 						gravity_value_t r1copy = STACK_GET(r1);
 						gravity_value_t r1copy = STACK_GET(r1);
 						if (!closure->f->internal(vm, &stackstart[rwin], 2, r1)) {
 						if (!closure->f->internal(vm, &stackstart[rwin], 2, r1)) {
+                            if (vm->aborted) return false;
+                            
 							// check for special getter trick
 							// check for special getter trick
 							if (VALUE_ISA_CLOSURE(STACK_GET(r1))) {
 							if (VALUE_ISA_CLOSURE(STACK_GET(r1))) {
 								closure = VALUE_AS_CLOSURE(STACK_GET(r1));
 								closure = VALUE_AS_CLOSURE(STACK_GET(r1));
@@ -951,7 +970,7 @@ static bool gravity_vm_exec (gravity_vm *vm) {
 						// prepare func call
 						// prepare func call
 						uint32_t rwin = FN_COUNTREG(func, frame->nargs);
 						uint32_t rwin = FN_COUNTREG(func, frame->nargs);
 						uint32_t _rneed = FN_COUNTREG(closure->f, 1);
 						uint32_t _rneed = FN_COUNTREG(closure->f, 1);
-						gravity_check_stack(vm, fiber, _rneed, &stackstart);
+                        if (!gravity_check_stack(vm, fiber, _rneed, &stackstart)) return false;
 						SETVALUE(rwin, v1);
 						SETVALUE(rwin, v1);
 						
 						
 						// call func and check result
 						// call func and check result
@@ -1007,7 +1026,7 @@ static bool gravity_vm_exec (gravity_vm *vm) {
 				
 				
 				// check stack size
 				// check stack size
 				uint32_t _rneed = FN_COUNTREG(closure->f, r3);
 				uint32_t _rneed = FN_COUNTREG(closure->f, r3);
-				gravity_check_stack(vm, fiber, _rneed, &stackstart);
+                if (!gravity_check_stack(vm, fiber, _rneed, &stackstart)) return false;
 				
 				
 				// if less arguments are passed then fill the holes with UNDEFINED values
 				// if less arguments are passed then fill the holes with UNDEFINED values
 				while (r3 < closure->f->nparams) {
 				while (r3 < closure->f->nparams) {
@@ -1029,6 +1048,8 @@ static bool gravity_vm_exec (gravity_vm *vm) {
 						// backup register r1 because it can be overwrite to return a closure
 						// backup register r1 because it can be overwrite to return a closure
 						gravity_value_t r1copy = STACK_GET(r1);
 						gravity_value_t r1copy = STACK_GET(r1);
 						if (!closure->f->internal(vm, &stackstart[rwin], r3, r1)) {
 						if (!closure->f->internal(vm, &stackstart[rwin], r3, r1)) {
+                            if (vm->aborted) return false;
+                            
 							// check for special getter trick
 							// check for special getter trick
 							if (VALUE_ISA_CLOSURE(STACK_GET(r1))) {
 							if (VALUE_ISA_CLOSURE(STACK_GET(r1))) {
 								closure = VALUE_AS_CLOSURE(STACK_GET(r1));
 								closure = VALUE_AS_CLOSURE(STACK_GET(r1));
@@ -1442,7 +1463,7 @@ bool gravity_vm_runclosure (gravity_vm *vm, gravity_closure_t *closure, gravity_
 		rwin = FN_COUNTREG(frame->closure->f, frame->nargs);
 		rwin = FN_COUNTREG(frame->closure->f, frame->nargs);
 		
 		
 		// check stack size
 		// check stack size
-		gravity_check_stack(vm, vm->fiber, FN_COUNTREG(f,nparams+1), &stackstart);
+        if (!gravity_check_stack(vm, vm->fiber, FN_COUNTREG(f,nparams+1), &stackstart)) return false;
 		
 		
 		// setup params (first param is self)
 		// setup params (first param is self)
 		SETVALUE(rwin, selfvalue);
 		SETVALUE(rwin, selfvalue);
@@ -1539,6 +1560,7 @@ gravity_closure_t *gravity_vm_getclosure (gravity_vm *vm) {
 }
 }
 
 
 void gravity_vm_setslot (gravity_vm *vm, gravity_value_t value, uint32_t index) {
 void gravity_vm_setslot (gravity_vm *vm, gravity_value_t value, uint32_t index) {
+    if (vm->aborted) return;
 	if (index == GRAVITY_FIBER_REGISTER) {
 	if (index == GRAVITY_FIBER_REGISTER) {
 		vm->fiber->result = value;
 		vm->fiber->result = value;
 		return;
 		return;
@@ -1816,12 +1838,9 @@ gravity_closure_t *gravity_vm_loadbuffer (gravity_vm *vm, const char *buffer, si
 	
 	
 abort_load:
 abort_load:
 	report_runtime_error(vm, GRAVITY_ERROR_RUNTIME, "%s", "Unable to parse JSON executable file.");
 	report_runtime_error(vm, GRAVITY_ERROR_RUNTIME, "%s", "Unable to parse JSON executable file.");
-	marray_destroy(objects);
-	if (json) json_value_free(json);
-	gravity_gc_setenabled(vm, true);
-	return NULL;
 	
 	
 abort_generic:
 abort_generic:
+    marray_destroy(objects);
 	if (json) json_value_free(json);
 	if (json) json_value_free(json);
 	gravity_gc_setenabled(vm, true);
 	gravity_gc_setenabled(vm, true);
 	return NULL;
 	return NULL;

+ 6 - 2
src/runtime/gravity_vmmacros.h

@@ -137,7 +137,8 @@
 
 
 #define STORE_FRAME()				frame->ip = ip
 #define STORE_FRAME()				frame->ip = ip
 
 
-#define LOAD_FRAME()				frame = &fiber->frames[fiber->nframes - 1];															\
+#define LOAD_FRAME()				if (vm->aborted) return false;                                                                      \
+                                    frame = &fiber->frames[fiber->nframes - 1];															\
 									stackstart = frame->stackstart;																		\
 									stackstart = frame->stackstart;																		\
 									ip = frame->ip;																						\
 									ip = frame->ip;																						\
 									func = frame->closure->f;																			\
 									func = frame->closure->f;																			\
@@ -145,6 +146,7 @@
 
 
 #define USE_ARGS(_c)				(_c->f->tag == EXEC_TYPE_NATIVE && _c->f->useargs)
 #define USE_ARGS(_c)				(_c->f->tag == EXEC_TYPE_NATIVE && _c->f->useargs)
 #define PUSH_FRAME(_c,_s,_r,_n)		gravity_callframe_t *cframe = gravity_new_callframe(vm, fiber);										\
 #define PUSH_FRAME(_c,_s,_r,_n)		gravity_callframe_t *cframe = gravity_new_callframe(vm, fiber);										\
+                                    if (!cframe) return false;                                                                          \
 									cframe->closure = _c;																				\
 									cframe->closure = _c;																				\
 									cframe->stackstart = _s;																			\
 									cframe->stackstart = _s;																			\
 									cframe->ip = _c->f->bytecode;																		\
 									cframe->ip = _c->f->bytecode;																		\
@@ -213,7 +215,8 @@
 													if (!_c || !_c->f) RUNTIME_ERROR("Unable to perform operator %s on object", opcode_name(op));	\
 													if (!_c || !_c->f) RUNTIME_ERROR("Unable to perform operator %s on object", opcode_name(op));	\
 													uint32_t _w = FN_COUNTREG(func, frame->nargs);													\
 													uint32_t _w = FN_COUNTREG(func, frame->nargs);													\
 													uint32_t _rneed = FN_COUNTREG(_c->f, _N);														\
 													uint32_t _rneed = FN_COUNTREG(_c->f, _N);														\
-													gravity_check_stack(vm, fiber, _rneed, &stackstart)
+													if (!gravity_check_stack(vm, fiber, _rneed, &stackstart)) return false;                         \
+                                                    if (vm->aborted) return false
 
 
 #define PREPARE_FUNC_CALL1(_c,_v1,_i,_w)			PREPARE_FUNC_CALLN(_c,_i,_w,1);															\
 #define PREPARE_FUNC_CALL1(_c,_v1,_i,_w)			PREPARE_FUNC_CALLN(_c,_i,_w,1);															\
 													SETVALUE(_w, _v1)
 													SETVALUE(_w, _v1)
@@ -237,6 +240,7 @@
 													} break;																				\
 													} break;																				\
 													case EXEC_TYPE_INTERNAL: {																\
 													case EXEC_TYPE_INTERNAL: {																\
 														if (!_c->f->internal(vm, &stackstart[rwin], nargs, r1)) {							\
 														if (!_c->f->internal(vm, &stackstart[rwin], nargs, r1)) {							\
+                                                            if (vm->aborted) return false;                                                  \
 															if (VALUE_ISA_CLOSURE(STACK_GET(r1))) {											\
 															if (VALUE_ISA_CLOSURE(STACK_GET(r1))) {											\
 																closure = VALUE_AS_CLOSURE(STACK_GET(r1));									\
 																closure = VALUE_AS_CLOSURE(STACK_GET(r1));									\
 																SETVALUE(r1, VALUE_FROM_NULL);												\
 																SETVALUE(r1, VALUE_FROM_NULL);												\

+ 1 - 0
src/utils/gravity_utils.c

@@ -479,6 +479,7 @@ bool utf8_reverse (char *p) {
 // MARK: - Math -
 // MARK: - Math -
 
 
 // From: http://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2Float
 // From: http://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2Float
+// WARNING: this function returns 0 if n is greater than 2^31
 uint32_t power_of2_ceil (uint32_t n) {
 uint32_t power_of2_ceil (uint32_t n) {
 	n--;
 	n--;
 	n |= n >> 1;
 	n |= n >> 1;