Pārlūkot izejas kodu

Fixed all crash issues reported by American Fuzzy Lop (afl-fuzz) on https://github.com/rwhitworth/gravity-fuzz/

Marco Bambini 8 gadi atpakaļ
vecāks
revīzija
6e7035b054

+ 4 - 2
src/compiler/gravity_ast.c

@@ -446,6 +446,8 @@ gnode_t *gnode_list_expr_create (gtoken_s token, gnode_r *list1, gnode_r *list2,
 // MARK: -
 
 gnode_t *gnode_duplicate (gnode_t *node, bool deep) {
+	if (!node) return NULL;
+	
 	if (deep == true) {
 		// deep is true so I need to examine node and perform a real duplication (only of the outer nodes)
 		// deep is true ONLY when node can also be part of an assignment and its assignment flag can be
@@ -472,8 +474,8 @@ gnode_t *gnode_duplicate (gnode_t *node, bool deep) {
 			gnode_array_each(expr->list, {gnode_array_push(list, gnode_duplicate(val, false));});
 			return gnode_postfix_expr_create(expr->base.token, id, list);
 		} else {
-			printf("gnode_duplicate UNHANDLED case\n");
-			assert(0); // should never reach this point
+			// gnode_duplicate UNHANDLED case
+			return NULL;
 		}
 		// just return the original node and since it is invalid for an assignment a semantic error will be generated
 	}

+ 1 - 1
src/compiler/gravity_ircode.c

@@ -287,7 +287,7 @@ void ircode_dump  (void *_code) {
 			case 2: {
 				if (op == LOADI) {
 					if (inst->tag == DOUBLE_TAG) printf("%05d\t%s %d %.2f\n", line, opcode_name(op), p1, inst->d);
-					else printf("%05d\t%s %d %ld\n", line, opcode_name(op), p1, inst->n);
+					else printf("%05d\t%s %d %lld\n", line, opcode_name(op), p1, inst->n);
 				} else if (op == LOADK) {
 					if (p2 < CPOOL_INDEX_MAX) printf("%05d\t%s %d %d\n", line, opcode_name(op), p1, p2);
 					else printf("%05d\t%s %d %s\n", line, opcode_name(op), p1, opcode_constname(p2));

+ 13 - 9
src/compiler/gravity_lexer.c

@@ -62,7 +62,7 @@ typedef enum {
 #define DEC_TOKLEN				--lexer->token.bytes; --lexer->token.length
 #define SET_TOKTYPE(t)			lexer->token.type = t
 
-#define LEXER_CALL_CALLBACK()	if ((lexer->peeking == false) && (lexer->delegate) && (lexer->delegate->parser_callback)) {	\
+#define LEXER_CALL_CALLBACK()	if ((!lexer->peeking) && (lexer->delegate) && (lexer->delegate->parser_callback)) {	\
 									lexer->delegate->parser_callback(&lexer->token, lexer->delegate->xdata); }
 
 // MARK: -
@@ -170,21 +170,22 @@ static gtoken_t lexer_error(gravity_lexer_t *lexer, const char *message) {
 	return TOK_ERROR;
 }
 
-static inline int next_utf8(gravity_lexer_t *lexer) {
+static inline bool next_utf8(gravity_lexer_t *lexer, int *result) {
 	int c = NEXT;
 	INC_TOKLEN;
 	
 	uint32_t len = utf8_charbytes((const char *)&c, 0);
-	if (len == 1) return c;
+	if (len == 0) return false;
 	
 	switch(len) {
-		case 0: lexer_error(lexer, "Unknown character inside a string literal"); return 0;
+		case 1: break;
 		case 2: INC_OFFSET; INC_TOKBYTES; break;
 		case 3: INC_OFFSET; INC_OFFSET; INC_TOKBYTES; INC_TOKBYTES; break;
 		case 4: INC_OFFSET; INC_OFFSET; INC_OFFSET; INC_TOKBYTES; INC_TOKBYTES; INC_TOKBYTES; INC_POSITION; INC_TOKUTF8LEN; break;
 	}
 	
-	return c;
+	if (result) *result = c;
+	return true;
 }
 
 static gtoken_t lexer_scan_comment(gravity_lexer_t *lexer) {
@@ -200,11 +201,13 @@ static gtoken_t lexer_scan_comment(gravity_lexer_t *lexer) {
 	// count necessary only to support nested comments
 	int count = 1;
 	while (!IS_EOF) {
-		int c = next_utf8(lexer);
+		int c;
+		next_utf8(lexer, &c);
 		
 		if (isLineComment){
 			if (is_newline(lexer, c)) {INC_LINE; break;}
 		} else {
+			if (IS_EOF) break;
 			int c2 = PEEK_CURRENT;
 			if ((c == '/') && (c2 == '*')) ++count;
 			if ((c == '*') && (c2 == '/')) {--count; NEXT; INC_TOKLEN; if (count == 0) break;}
@@ -341,7 +344,7 @@ static gtoken_t lexer_scan_string(gravity_lexer_t *lexer) {
 	TOKEN_RESET;				// save offset
 	
 	while ((c2 = (unsigned char)PEEK_CURRENT) != c) {
-		if (IS_EOF) {return lexer_error(lexer, "Unexpected EOF inside a string literal");}
+		if (IS_EOF) return lexer_error(lexer, "Unexpected EOF inside a string literal");
 		if (is_newline(lexer, c2)) INC_LINE;
 		
 		// handle escaped characters
@@ -354,7 +357,7 @@ static gtoken_t lexer_scan_string(gravity_lexer_t *lexer) {
 		}
 		
 		// scan next
-		next_utf8(lexer);
+		if (!next_utf8(lexer, NULL)) return lexer_error(lexer, "Unknown character inside a string literal");
 	}
 	
 	// skip last escape character
@@ -600,7 +603,8 @@ gtoken_t gravity_lexer_token_type (gravity_lexer_t *lexer) {
 
 void gravity_lexer_skip_line (gravity_lexer_t *lexer) {
 	while (!IS_EOF) {
-		int c = next_utf8(lexer);
+		int c;
+		next_utf8(lexer, &c);
 		if (is_newline(lexer, c)) {
 			INC_LINE;
 			break;

+ 3 - 2
src/compiler/gravity_optimizer.c

@@ -23,7 +23,7 @@
 #define IS_SKIP(inst)					(inst->tag == SKIP_TAG)
 #define IS_LABEL(inst)					(inst->tag == LABEL_TAG)
 #define IS_NOTNULL(inst)				(inst)
-#define IS_PRAGMA_MOVE_OPT(inst)		(inst->tag == PRAGMA_MOVE_OPTIMIZATION)
+#define IS_PRAGMA_MOVE_OPT(inst)		((inst) && (inst->tag == PRAGMA_MOVE_OPTIMIZATION))
 
 // http://www.mathsisfun.com/binary-decimal-hexadecimal-converter.html
 #define OPCODE_SET(op,code)								op = (code & 0x3F) << 26
@@ -299,12 +299,13 @@ static bool optimize_const_instruction (inst_t *inst, inst_t *inst1, inst_t *ins
 			
 		case DIV:
 			// don't optimize in case of division by 0
-			if (d2 == 0) return false;
+			if ((int64_t)d2 == 0) return false;
 			if (type == DOUBLE_TAG) d = d1 / d2;
 			else n = n1 / n2;
 			break;
 			
 		case REM:
+			if ((int64_t)d2 == 0) return false;
 			if (type == DOUBLE_TAG) d = (double)((int64_t)d1 % (int64_t)d2);
 			else n = n1 % n2;
 			break;

+ 13 - 11
src/compiler/gravity_parser.c

@@ -109,7 +109,7 @@ static gnode_t *parse_compound_statement (gravity_parser_t *parser);
 static gnode_t *parse_expression (gravity_parser_t *parser);
 static gnode_t *parse_declaration_statement (gravity_parser_t *parser);
 static gnode_t *parse_function (gravity_parser_t *parser, bool is_declaration, gtoken_t access_specifier, gtoken_t storage_specifier);
-static gnode_t *adjust_assignment_expression (gtoken_t tok, gnode_t *lnode, gnode_t *rnode);
+static gnode_t *adjust_assignment_expression (gravity_parser_t *parser, gtoken_t tok, gnode_t *lnode, gnode_t *rnode);
 
 // MARK: - Utils functions -
 
@@ -865,17 +865,17 @@ static gnode_t *parse_precedence(gravity_parser_t *parser, prec_level precedence
 	gtoken_t type = gravity_lexer_peek(lexer);
 	if (type == TOK_EOF) return NULL;
 	
+	// execute prefix callback (if any)
 	parse_func prefix = rules[type].prefix;
-	if (prefix == NULL) {
+	gnode_t *node = (prefix) ? prefix(parser) : NULL;
+	
+	if (!prefix || !node) {
 		// we need to consume next token because error was triggered in peek
 		gravity_lexer_next(lexer);
 		REPORT_ERROR(gravity_lexer_token(lexer), "Expected expression but found %s.", token_name(type));
 		return NULL;
 	}
 	
-	// execute prefix callback
-	gnode_t *node = prefix(parser);
-	
 	// peek next and check for EOF
 	gtoken_t peek = gravity_lexer_peek(lexer);
 	if (peek == TOK_EOF) return node;
@@ -922,13 +922,13 @@ static gnode_t *parse_infix (gravity_parser_t *parser) {
 	prec_level precedence = (rule->right) ? rule->precedence-1 : rule->precedence;
 	
 	gnode_t *rnode = parse_precedence(parser, precedence);
-	if ((tok != TOK_OP_ASSIGN) && token_isassignment(tok)) return adjust_assignment_expression(tok, lnode, rnode);
+	if ((tok != TOK_OP_ASSIGN) && token_isassignment(tok)) return adjust_assignment_expression(parser, tok, lnode, rnode);
 	return gnode_binary_expr_create(tok, lnode, rnode);
 }
 
 // MARK: -
 
-static gnode_t *adjust_assignment_expression (gtoken_t tok, gnode_t *lnode, gnode_t *rnode) {
+static gnode_t *adjust_assignment_expression (gravity_parser_t *parser, gtoken_t tok, gnode_t *lnode, gnode_t *rnode) {
 	DEBUG_PARSER("adjust_assignment_expression");
 	
 	// called when tok is an assignment != TOK_OP_ASSIGN
@@ -962,7 +962,9 @@ static gnode_t *adjust_assignment_expression (gtoken_t tok, gnode_t *lnode, gnod
 	}
 	
 	// duplicate node is mandatory here, otherwise the deallocator will try to free memory occopied by the same node twice
-	rnode = gnode_binary_expr_create(t, gnode_duplicate(lnode, true), rnode);
+	gnode_t *duplicate = gnode_duplicate(lnode, true);
+	if (!duplicate) {DECLARE_LEXER; REPORT_ERROR(gravity_lexer_token(lexer), "An unexpected error occurred in %s", token_name(tok)); return NULL;}
+	rnode = gnode_binary_expr_create(t, duplicate, rnode);
 	tok = TOK_OP_ASSIGN;
 	
 	// its an assignment expression so switch the order
@@ -1881,14 +1883,14 @@ static gnode_t *parse_flow_statement (gravity_parser_t *parser) {
 	gtoken_s token = gravity_lexer_token(lexer);
 	assert((type == TOK_KEY_IF) || (type == TOK_KEY_SWITCH));
 	
-	// check optional TOK_OP_OPEN_PARENTHESIS
-	bool is_parenthesize = parse_optional(parser, TOK_OP_OPEN_PARENTHESIS);
+	// check required TOK_OP_OPEN_PARENTHESIS
+	parse_required(parser, TOK_OP_OPEN_PARENTHESIS);
 	
 	// parse common expression
 	gnode_t *cond = parse_expression(parser);
 	
 	// check and consume TOK_OP_CLOSED_PARENTHESIS
-	if (is_parenthesize) parse_required(parser, TOK_OP_CLOSED_PARENTHESIS);
+	parse_required(parser, TOK_OP_CLOSED_PARENTHESIS);
 	
 	// parse common statement
 	gnode_t *stmt1 = parse_statement(parser);

+ 4 - 0
src/compiler/gravity_semacheck2.c

@@ -555,6 +555,10 @@ static void visit_flow_stmt (gvisitor_t *self, gnode_flow_stmt_t *node) {
 		visit(node->cond);
 		visit(node->stmt);
 		POP_STATEMENT();
+	} else if (type == TOK_OP_TERNARY) {
+		visit(node->cond);
+		visit(node->stmt);
+		visit(node->elsestmt);
 	}
 }
 

+ 4 - 2
src/runtime/gravity_core.c

@@ -1204,6 +1204,8 @@ static bool operator_int_rem (gravity_vm *vm, gravity_value_t *args, uint16_t na
 	#pragma unused (nargs)
 	DECLARE_2VARIABLES(v1, v2, 0, 1);
 	INTERNAL_CONVERT_INT(v2);
+	
+	if (v2.n == 0) RETURN_ERROR("Reminder by 0 error.");
 	RETURN_VALUE(VALUE_FROM_INT(v1.n % v2.n), rindex);
 }
 
@@ -1456,8 +1458,8 @@ static bool operator_string_neg (gravity_vm *vm, gravity_value_t *args, uint16_t
 	
 	// reverse the string
 	gravity_string_t *s1 = VALUE_AS_STRING(v1);
-	char *s = (char *)string_dup(s1->s);
-	utf8_reverse(s);
+	char *s = (char *)string_ndup(s1->s, s1->len);
+	if (!utf8_reverse(s)) RETURN_ERROR("Unable to reverse a malformed string.");
 	
 	gravity_string_t *string = gravity_string_new(vm, s, s1->len, s1->len);
 	RETURN_VALUE(VALUE_FROM_OBJECT(string), rindex);

+ 1 - 0
src/runtime/gravity_vmmacros.h

@@ -211,6 +211,7 @@
 
 #define CHECK_FAST_BINARY_REM(r1,r2,r3,v2,v3)		DEFINE_STACK_VARIABLE(v2,r2);																\
 													DEFINE_STACK_VARIABLE(v3,r3);																\
+													CHECK_ZERO(v3);																				\
 													if (VALUE_ISA_INT(v2) && VALUE_ISA_INT(v3)) FMATH_BIN_INT(r1, v2.n, v3.n, %)
 
 #define CHECK_FAST_BINARY_BIT(r1,r2,r3,v2,v3,OP)	DEFINE_STACK_VARIABLE(v2,r2);																\

+ 5 - 1
src/utils/gravity_utils.c

@@ -447,7 +447,7 @@ uint32_t utf8_len (const char *s, uint32_t nbytes) {
 }
 
 // From: http://stackoverflow.com/questions/198199/how-do-you-reverse-a-string-in-place-in-c-or-c
-void utf8_reverse (char *p) {
+bool utf8_reverse (char *p) {
 	char *q = p;
 	string_reverse(p);
 	
@@ -456,20 +456,24 @@ void utf8_reverse (char *p) {
 	while(p < --q)
 		switch( (*q & 0xF0) >> 4 ) {
 			case 0xF: /* U+010000-U+10FFFF: four bytes. */
+				if (q-p < 4) return false;
 				SWP(*(q-0), *(q-3));
 				SWP(*(q-1), *(q-2));
 				q -= 3;
 				break;
 			case 0xE: /* U+000800-U+00FFFF: three bytes. */
+				if (q-p < 3) return false;
 				SWP(*(q-0), *(q-2));
 				q -= 2;
 				break;
 			case 0xC: /* fall-through */
 			case 0xD: /* U+000080-U+0007FF: two bytes. */
+				if (q-p < 1) return false;
 				SWP(*(q-0), *(q-1));
 				q--;
 				break;
 		}
+	return true;
 }
 
 // MARK: - Math -

+ 1 - 1
src/utils/gravity_utils.h

@@ -56,7 +56,7 @@ uint32_t	utf8_charbytes (const char *s, uint32_t i);
 uint32_t	utf8_nbytes (uint32_t n);
 uint32_t	utf8_encode(char *buffer, uint32_t value);
 uint32_t	utf8_len (const char *s, uint32_t nbytes);
-void		utf8_reverse (char *p);
+bool		utf8_reverse (char *p);
 
 // MATH and NUMBERS
 uint32_t	power_of2_ceil (uint32_t n);