checker: disallow invalid pointer arithmetic (#10888)

pull/10353/head
Daniel Däschle 2021-07-21 22:39:49 +02:00 committed by GitHub
parent 4f22ae4a30
commit 12884e9eb7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 77 additions and 12 deletions

View File

@ -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 {

View File

@ -1241,14 +1241,22 @@ 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 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_pos)
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
if node.op != .key_is {
match mut node.left {
@ -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)

View File

@ -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 | }

View File

@ -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
}

View File

@ -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)

View File

@ -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

View File

@ -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'
}