From 12884e9eb7a408fb21d9ccb41a8552f37a99614f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20D=C3=A4schle?= Date: Wed, 21 Jul 2021 22:39:49 +0200 Subject: [PATCH] checker: disallow invalid pointer arithmetic (#10888) --- vlib/v/checker/check_types.v | 4 +-- vlib/v/checker/checker.v | 30 ++++++++++++---- .../tests/disallow_pointer_arithmetic_err.out | 34 +++++++++++++++++++ .../tests/disallow_pointer_arithmetic_err.vv | 9 +++++ .../v/checker/tests/mismatched_ptr_op_ptr.out | 2 +- ...e_pointer_arithmetic_should_be_checked.out | 4 +-- vlib/v/tests/pointer_arithmetic_test.v | 6 ++++ 7 files changed, 77 insertions(+), 12 deletions(-) create mode 100644 vlib/v/checker/tests/disallow_pointer_arithmetic_err.out create mode 100644 vlib/v/checker/tests/disallow_pointer_arithmetic_err.vv create mode 100644 vlib/v/tests/pointer_arithmetic_test.v diff --git a/vlib/v/checker/check_types.v b/vlib/v/checker/check_types.v index 1e975fe9b4..553a1ae495 100644 --- a/vlib/v/checker/check_types.v +++ b/vlib/v/checker/check_types.v @@ -195,13 +195,13 @@ fn (mut c Checker) check_shift(left_type ast.Type, right_type ast.Type, left_pos } pub fn (mut c Checker) promote(left_type ast.Type, right_type ast.Type) ast.Type { - if left_type.is_ptr() || left_type.is_pointer() { + if left_type.is_any_kind_of_pointer() { if right_type.is_int() { return left_type } else { return ast.void_type } - } else if right_type.is_ptr() || right_type.is_pointer() { + } else if right_type.is_any_kind_of_pointer() { if left_type.is_int() { return right_type } else { diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index 33cc111eb3..e85a6cdc99 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -1241,12 +1241,20 @@ pub fn (mut c Checker) infix_expr(mut node ast.InfixExpr) ast.Type { left_pos := node.left.position() right_pos := node.right.position() left_right_pos := left_pos.extend(right_pos) - if (left_type.is_ptr() || left_sym.is_pointer()) && node.op in [.plus, .minus] { - if !c.inside_unsafe && !node.left.is_auto_deref_var() && !node.right.is_auto_deref_var() { - c.warn('pointer arithmetic is only allowed in `unsafe` blocks', left_pos) - } - if left_type == ast.voidptr_type { - c.error('`$node.op` cannot be used with `voidptr`', left_pos) + if left_type.is_any_kind_of_pointer() + && node.op in [.plus, .minus, .mul, .div, .mod, .xor, .amp, .pipe] { + if (right_type.is_any_kind_of_pointer() && node.op != .minus) + || (!right_type.is_any_kind_of_pointer() && node.op !in [.plus, .minus]) { + left_name := c.table.type_to_str(left_type) + right_name := c.table.type_to_str(right_type) + c.error('invalid operator `$node.op` to `$left_name` and `$right_name`', left_right_pos) + } else if node.op in [.plus, .minus] { + if !c.inside_unsafe && !node.left.is_auto_deref_var() && !node.right.is_auto_deref_var() { + c.warn('pointer arithmetic is only allowed in `unsafe` blocks', left_right_pos) + } + if left_type == ast.voidptr_type { + c.error('`$node.op` cannot be used with `voidptr`', left_pos) + } } } mut return_type := left_type @@ -1408,7 +1416,15 @@ pub fn (mut c Checker) infix_expr(mut node ast.InfixExpr) ast.Type { c.error('mismatched types `$left_name` and `$right_name`', left_right_pos) } } else { - promoted_type := c.promote(c.table.unalias_num_type(left_type), c.table.unalias_num_type(right_type)) + unaliased_left_type := c.table.unalias_num_type(left_type) + unalias_right_type := c.table.unalias_num_type(right_type) + mut promoted_type := c.promote(unaliased_left_type, unalias_right_type) + // substract pointers is allowed in unsafe block + is_allowed_pointer_arithmetic := left_type.is_any_kind_of_pointer() + && right_type.is_any_kind_of_pointer() && node.op == .minus + if is_allowed_pointer_arithmetic { + promoted_type = ast.int_type + } if promoted_type.idx() == ast.void_type_idx { left_name := c.table.type_to_str(left_type) right_name := c.table.type_to_str(right_type) diff --git a/vlib/v/checker/tests/disallow_pointer_arithmetic_err.out b/vlib/v/checker/tests/disallow_pointer_arithmetic_err.out new file mode 100644 index 0000000000..3e4422f870 --- /dev/null +++ b/vlib/v/checker/tests/disallow_pointer_arithmetic_err.out @@ -0,0 +1,34 @@ +vlib/v/checker/tests/disallow_pointer_arithmetic_err.vv:4:7: error: invalid operator `+` to `&int` and `&int` + 2 | x := 5 + 3 | p := &x + 4 | _ := p + p //should be error + | ~~~~~ + 5 | _ := p * p //should be error + 6 | _ := p * 2 //should be error +vlib/v/checker/tests/disallow_pointer_arithmetic_err.vv:5:7: error: invalid operator `*` to `&int` and `&int` + 3 | p := &x + 4 | _ := p + p //should be error + 5 | _ := p * p //should be error + | ~~~~~ + 6 | _ := p * 2 //should be error + 7 | _ := p + 5 //OK but only in unsafe block, r is *int +vlib/v/checker/tests/disallow_pointer_arithmetic_err.vv:6:7: error: invalid operator `*` to `&int` and `int literal` + 4 | _ := p + p //should be error + 5 | _ := p * p //should be error + 6 | _ := p * 2 //should be error + | ~~~~~ + 7 | _ := p + 5 //OK but only in unsafe block, r is *int + 8 | _ := p - p //OK even in safe code, but n should be isize +vlib/v/checker/tests/disallow_pointer_arithmetic_err.vv:7:7: error: pointer arithmetic is only allowed in `unsafe` blocks + 5 | _ := p * p //should be error + 6 | _ := p * 2 //should be error + 7 | _ := p + 5 //OK but only in unsafe block, r is *int + | ~~~~~ + 8 | _ := p - p //OK even in safe code, but n should be isize + 9 | } +vlib/v/checker/tests/disallow_pointer_arithmetic_err.vv:8:7: error: pointer arithmetic is only allowed in `unsafe` blocks + 6 | _ := p * 2 //should be error + 7 | _ := p + 5 //OK but only in unsafe block, r is *int + 8 | _ := p - p //OK even in safe code, but n should be isize + | ~~~~~ + 9 | } diff --git a/vlib/v/checker/tests/disallow_pointer_arithmetic_err.vv b/vlib/v/checker/tests/disallow_pointer_arithmetic_err.vv new file mode 100644 index 0000000000..0fc91cd8bb --- /dev/null +++ b/vlib/v/checker/tests/disallow_pointer_arithmetic_err.vv @@ -0,0 +1,9 @@ +fn main() { + x := 5 + p := &x + _ := p + p //should be error + _ := p * p //should be error + _ := p * 2 //should be error + _ := p + 5 //OK but only in unsafe block, r is *int + _ := p - p //OK even in safe code, but n should be isize +} diff --git a/vlib/v/checker/tests/mismatched_ptr_op_ptr.out b/vlib/v/checker/tests/mismatched_ptr_op_ptr.out index b3c505d9d5..a1a1d8af34 100644 --- a/vlib/v/checker/tests/mismatched_ptr_op_ptr.out +++ b/vlib/v/checker/tests/mismatched_ptr_op_ptr.out @@ -5,7 +5,7 @@ vlib/v/checker/tests/mismatched_ptr_op_ptr.vv:5:17: error: mismatched types `&st | ~~~ 6 | println(b+b) 7 | } -vlib/v/checker/tests/mismatched_ptr_op_ptr.vv:6:17: error: mismatched types `&string` and `&string` +vlib/v/checker/tests/mismatched_ptr_op_ptr.vv:6:17: error: invalid operator `+` to `&string` and `&string` 4 | b := &a 5 | println(b+*b) 6 | println(b+b) diff --git a/vlib/v/checker/tests/unsafe_pointer_arithmetic_should_be_checked.out b/vlib/v/checker/tests/unsafe_pointer_arithmetic_should_be_checked.out index 952c7f3f52..9ac0225f81 100644 --- a/vlib/v/checker/tests/unsafe_pointer_arithmetic_should_be_checked.out +++ b/vlib/v/checker/tests/unsafe_pointer_arithmetic_should_be_checked.out @@ -16,13 +16,13 @@ vlib/v/checker/tests/unsafe_pointer_arithmetic_should_be_checked.vv:11:14: error 9 | fn test_ptr_infix() { 10 | v := 4 11 | mut q := &v - 1 - | ^ + | ~~~~~~ 12 | q = q + 3 13 | _ := q vlib/v/checker/tests/unsafe_pointer_arithmetic_should_be_checked.vv:12:9: error: pointer arithmetic is only allowed in `unsafe` blocks 10 | v := 4 11 | mut q := &v - 1 12 | q = q + 3 - | ^ + | ~~~~~ 13 | _ := q 14 | _ := v diff --git a/vlib/v/tests/pointer_arithmetic_test.v b/vlib/v/tests/pointer_arithmetic_test.v new file mode 100644 index 0000000000..6cb1f84e29 --- /dev/null +++ b/vlib/v/tests/pointer_arithmetic_test.v @@ -0,0 +1,6 @@ +fn test_strings() { + s := 'hi' + mut p := unsafe { s.str + 1 } + n := unsafe { p - s.str } + assert typeof(n).name == 'int' +}