builtin: fix memory corruption in array `.clone()` and `.repeat()` (#10394)

pull/10399/head
Uwe Krüger 2021-06-08 22:23:28 +02:00 committed by GitHub
parent 3dc4d13160
commit a2243054a5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 105 additions and 106 deletions

View File

@ -107,7 +107,15 @@ fn (mut a array) ensure_cap(required int) {
}
// repeat returns a new array with the given array elements repeated given times.
// `cgen` will replace this with an apropriate call to `repeat_to_depth()`
pub fn (a array) repeat(count int) array {
return unsafe { a.repeat_to_depth(count, 0) }
}
// version of `repeat()` that handles multi dimensional arrays
// `unsafe` to call directly because `depth` is not checked
[unsafe]
pub fn (a array) repeat_to_depth(count int, depth int) array {
if count < 0 {
panic('array.repeat: count is negative: $count')
}
@ -121,13 +129,10 @@ pub fn (a array) repeat(count int) array {
len: count * a.len
cap: count * a.len
}
size_of_array := int(sizeof(array))
for i in 0 .. count {
if a.len > 0 && a.element_size == size_of_array {
ary := array{}
unsafe { C.memcpy(&ary, a.data, size_of_array) }
ary_clone := ary.clone()
unsafe { C.memcpy(arr.get_unsafe(i * a.len), &ary_clone, a.len * a.element_size) }
if a.len > 0 && depth > 0 {
ary_clone := unsafe { a.clone_to_depth(depth - 1) }
unsafe { C.memcpy(arr.get_unsafe(i * a.len), &byte(ary_clone.data), a.len * a.element_size) }
} else {
unsafe { C.memcpy(arr.get_unsafe(i * a.len), &byte(a.data), a.len * a.element_size) }
}
@ -335,8 +340,19 @@ fn (a array) clone_static() array {
return a.clone()
}
fn (a array) clone_static_to_depth(depth int) array {
return unsafe { a.clone_to_depth(depth) }
}
// clone returns an independent copy of a given array.
// this will be overwritten by `cgen` with an apropriate call to `.clone_to_depth()`
pub fn (a &array) clone() array {
return unsafe { a.clone_to_depth(0) }
}
// recursively clone given array - `unsafe` when called directly because depth is not checked
[unsafe]
pub fn (a &array) clone_to_depth(depth int) array {
mut size := a.cap * a.element_size
if size == 0 {
size++
@ -348,57 +364,20 @@ pub fn (a &array) clone() array {
cap: a.cap
}
// Recursively clone-generated elements if array element is array type
size_of_array := int(sizeof(array))
if a.element_size == size_of_array {
mut is_elem_array := true
if depth > 0 {
for i in 0 .. a.len {
ar := array{}
unsafe { C.memcpy(&ar, a.get_unsafe(i), size_of_array) }
if ar.len > ar.cap || ar.cap <= 0 || ar.element_size <= 0 {
is_elem_array = false
break
}
ar_clone := ar.clone()
unsafe { C.memcpy(&ar, a.get_unsafe(i), int(sizeof(array))) }
ar_clone := unsafe { ar.clone_to_depth(depth - 1) }
unsafe { arr.set_unsafe(i, &ar_clone) }
}
if is_elem_array {
return arr
return arr
} else {
if !isnil(a.data) {
unsafe { C.memcpy(&byte(arr.data), a.data, a.cap * a.element_size) }
}
return arr
}
if !isnil(a.data) {
unsafe { C.memcpy(&byte(arr.data), a.data, a.cap * a.element_size) }
}
return arr
}
fn (a &array) slice_clone(start int, _end int) array {
mut end := _end
$if !no_bounds_checking ? {
if start > end {
panic('array.slice: invalid slice index ($start > $end)')
}
if end > a.len {
panic('array.slice: slice bounds out of range ($end >= $a.len)')
}
if start < 0 {
panic('array.slice: slice bounds out of range ($start < 0)')
}
}
mut data := &byte(0)
offset := start * a.element_size
unsafe {
data = &byte(a.data) + offset
}
l := end - start
res := array{
element_size: a.element_size
data: data
offset: offset
len: l
cap: l
}
return res.clone()
}
// we manually inline this for single operations for performance without -prod
@ -722,7 +701,16 @@ fn new_array_from_c_array_noscan(len int, cap int, elm_size int, c_array voidptr
return arr
}
fn (a array) repeat_noscan(count int) array {
// repeat returns a new array with the given array elements repeated given times.
// `cgen` will replace this with an apropriate call to `repeat_to_depth()`
pub fn (a array) repeat_noscan(count int) array {
return unsafe { a.repeat_to_depth_noscan(count, 0) }
}
// version of `repeat()` that handles multi dimensional arrays
// `unsafe` to call directly because `depth` is not checked
[unsafe]
fn (a array) repeat_to_depth_noscan(count int, depth int) array {
if count < 0 {
panic('array.repeat: count is negative: $count')
}
@ -732,16 +720,16 @@ fn (a array) repeat_noscan(count int) array {
}
arr := array{
element_size: a.element_size
data: vcalloc_noscan(size)
data: if depth > 0 { vcalloc(size) } else { vcalloc_noscan(size) }
len: count * a.len
cap: count * a.len
}
size_of_array := int(sizeof(array))
for i in 0 .. count {
if a.len > 0 && a.element_size == size_of_array {
if a.len > 0 && depth > 0 {
ary := array{}
unsafe { C.memcpy(&ary, a.data, size_of_array) }
ary_clone := ary.clone()
ary_clone := unsafe { ary.clone_to_depth_noscan(depth - 1) }
unsafe { C.memcpy(arr.get_unsafe(i * a.len), &ary_clone, a.len * a.element_size) }
} else {
unsafe { C.memcpy(arr.get_unsafe(i * a.len), &byte(a.data), a.len * a.element_size) }
@ -750,67 +738,32 @@ fn (a array) repeat_noscan(count int) array {
return arr
}
pub fn (a &array) clone_noscan() array {
pub fn (a &array) clone_to_depth_noscan(depth int) array {
mut size := a.cap * a.element_size
if size == 0 {
size++
}
mut arr := array{
element_size: a.element_size
data: vcalloc_noscan(size)
data: if depth > 0 { vcalloc(size) } else { vcalloc_noscan(size) }
len: a.len
cap: a.cap
}
// Recursively clone-generated elements if array element is array type
size_of_array := int(sizeof(array))
if a.element_size == size_of_array {
mut is_elem_array := true
if depth > 0 {
for i in 0 .. a.len {
ar := array{}
unsafe { C.memcpy(&ar, a.get_unsafe(i), size_of_array) }
if ar.len > ar.cap || ar.cap <= 0 || ar.element_size <= 0 {
is_elem_array = false
break
}
ar_clone := ar.clone()
unsafe { C.memcpy(&ar, a.get_unsafe(i), int(sizeof(array))) }
ar_clone := unsafe { ar.clone_to_depth_noscan(depth - 1) }
unsafe { arr.set_unsafe(i, &ar_clone) }
}
if is_elem_array {
return arr
return arr
} else {
if !isnil(a.data) {
unsafe { C.memcpy(&byte(arr.data), a.data, a.cap * a.element_size) }
}
return arr
}
if !isnil(a.data) {
unsafe { C.memcpy(&byte(arr.data), a.data, a.cap * a.element_size) }
}
return arr
}
fn (a &array) slice_clone_noscan(start int, _end int) array {
mut end := _end
$if !no_bounds_checking ? {
if start > end {
panic('array.slice: invalid slice index ($start > $end)')
}
if end > a.len {
panic('array.slice: slice bounds out of range ($end >= $a.len)')
}
if start < 0 {
panic('array.slice: slice bounds out of range ($start < 0)')
}
}
mut data := &byte(0)
unsafe {
data = &byte(a.data) + start * a.element_size
}
l := end - start
res := array{
element_size: a.element_size
data: data
len: l
cap: l
}
return res.clone_noscan()
}
fn (a array) reverse_noscan() array {

View File

@ -233,6 +233,21 @@ fn test_repeat() {
}
}
fn test_deep_repeat() {
mut a3 := [[[1, 1], [2, 2], [3, 3]], [[4, 4], [5, 5], [6, 6]]]
r := a3.repeat(3)
a3[1][1][0] = 17
assert r == [
[[1, 1], [2, 2], [3, 3]],
[[4, 4], [5, 5], [6, 6]],
[[1, 1], [2, 2], [3, 3]],
[[4, 4], [5, 5], [6, 6]],
[[1, 1], [2, 2], [3, 3]],
[[4, 4], [5, 5], [6, 6]],
]
assert a3 == [[[1, 1], [2, 2], [3, 3]], [[4, 4], [17, 5], [6, 6]]]
}
fn test_right() {
a := [1, 2, 3, 4]
c := a[1..a.len]
@ -367,7 +382,7 @@ fn test_copy() {
assert b[2] == 3
}
*/
fn test_mutli_array_clone() {
fn test_multi_array_clone() {
// 2d array_int
mut a2_1 := [[1, 2, 3], [4, 5, 6]]
mut a2_2 := a2_1.clone()
@ -647,7 +662,7 @@ fn test_anon_fn_map() {
assert [1, 2, 3].map(add_num) == [2, 3, 4]
}
fn test_mutli_anon_fn_map() {
fn test_multi_anon_fn_map() {
a := [1, 2, 3].map(fn (i int) int {
return i + 1
})
@ -987,7 +1002,7 @@ fn test_array_with_cap() {
assert a5.cap == 10
}
fn test_mutli_array_index() {
fn test_multi_array_index() {
mut a := [][]int{len: 2, init: []int{len: 3, init: 0}}
a[0][0] = 1
assert '$a' == '[[1, 0, 0], [0, 0, 0]]'

View File

@ -2775,9 +2775,11 @@ fn (mut g Gen) gen_clone_assignment(val ast.Expr, right_sym ast.TypeSymbol, add_
if add_eq {
g.write('=')
}
g.write(' array_clone_static(')
g.write(' array_clone_static_to_depth(')
g.expr(val)
g.write(')')
elem_type := (right_sym.info as ast.Array).elem_type
array_depth := g.get_array_depth(elem_type)
g.write(', $array_depth)')
} else if g.is_autofree && right_sym.kind == .string {
if add_eq {
g.write('=')
@ -6601,6 +6603,17 @@ fn (mut g Gen) trace(fbase string, message string) {
}
}
pub fn (mut g Gen) get_array_depth(el_typ ast.Type) int {
typ := g.unwrap_generic(el_typ)
sym := g.table.get_final_type_symbol(typ)
if sym.kind == .array {
info := sym.info as ast.Array
return 1 + g.get_array_depth(info.elem_type)
} else {
return 0
}
}
// returns true if `t` includes any pointer(s) - during garbage collection heap regions
// that contain no pointers do not have to be scanned
pub fn (mut g Gen) contains_ptr(el_typ ast.Type) bool {

View File

@ -649,7 +649,16 @@ fn (mut g Gen) method_call(node ast.CallExpr) {
}
}
mut name := util.no_dots('${receiver_type_name}_$node.name')
if left_sym.kind == .chan {
mut array_depth := -1
if left_sym.kind == .array {
if node.name in ['clone', 'repeat'] {
elem_type := (left_sym.info as ast.Array).elem_type
array_depth = g.get_array_depth(elem_type)
if node.name == 'repeat' {
array_depth++
}
}
} else if left_sym.kind == .chan {
if node.name in ['close', 'try_pop', 'try_push'] {
name = 'sync__Channel_$node.name'
}
@ -702,6 +711,9 @@ fn (mut g Gen) method_call(node ast.CallExpr) {
if !node.receiver_type.is_ptr() && node.left_type.is_ptr() && node.name == 'str' {
g.write('ptr_str(')
} else {
if array_depth >= 0 {
name = name + '_to_depth'
}
g.write('${name}(')
}
if node.receiver_type.is_ptr() && (!node.left_type.is_ptr()
@ -773,6 +785,9 @@ fn (mut g Gen) method_call(node ast.CallExpr) {
*/
// ///////
g.call_args(node)
if array_depth >= 0 {
g.write(', $array_depth')
}
g.write(')')
}

View File

@ -83,11 +83,14 @@ pub fn mark_used(mut table ast.Table, pref &pref.Preferences, ast_files []&ast.F
'21.set_unsafe',
'21.get_with_check' /* used for `x := a[i] or {}` */,
'21.clone_static',
'21.clone_static_to_depth',
'21.clone_to_depth',
'21.first',
'21.last',
'21.pointers' /* TODO: handle generic methods calling array primitives more precisely in pool_test.v */,
'21.reverse',
'21.repeat',
'21.repeat_to_depth',
'21.slice',
'21.slice2',
'59.get',