parser,gen: fix `arr << map[key] using map_get_and_set_1, leading to double free

pull/8790/head
Delyan Angelov 2021-02-16 15:08:01 +02:00
parent 982e35909d
commit 843de10442
No known key found for this signature in database
GPG Key ID: 66886C0F12D595ED
6 changed files with 70 additions and 21 deletions

View File

@ -1092,7 +1092,9 @@ fn test_push_arr_string_free() {
mut lines := ['hi'] mut lines := ['hi']
s := 'a' + 'b' s := 'a' + 'b'
lines << s lines << s
s.free() // make sure the data in the array is valid after freeing the string // make sure the data in the array is valid after freeing the string
unsafe { s.free() }
//
println(lines) println(lines)
assert lines.len == 2 assert lines.len == 2
assert lines[0] == 'hi' assert lines[0] == 'hi'

View File

@ -110,12 +110,12 @@ pub:
pub struct SelectorExpr { pub struct SelectorExpr {
pub: pub:
pos token.Position pos token.Position
expr Expr // expr.field_name
field_name string field_name string
is_mut bool // is used for the case `if mut ident.selector is MyType {`, it indicates if the root ident is mutable is_mut bool // is used for the case `if mut ident.selector is MyType {`, it indicates if the root ident is mutable
mut_pos token.Position mut_pos token.Position
next_token token.Kind next_token token.Kind
pub mut: pub mut:
expr Expr // expr.field_name
expr_type table.Type // type of `Foo` in `Foo.bar` expr_type table.Type // type of `Foo` in `Foo.bar`
typ table.Type // type of the entire thing (`Foo.bar`) typ table.Type // type of the entire thing (`Foo.bar`)
name_type table.Type // T in `T.name` or typeof in `typeof(expr).name` name_type table.Type // T in `T.name` or typeof in `typeof(expr).name`
@ -609,12 +609,15 @@ pub mut:
pub struct IndexExpr { pub struct IndexExpr {
pub: pub:
pos token.Position pos token.Position
left Expr
index Expr // [0], RangeExpr [start..end] or map[key] index Expr // [0], RangeExpr [start..end] or map[key]
or_expr OrExpr or_expr OrExpr
pub mut: pub mut:
left Expr
left_type table.Type // array, map, fixed array left_type table.Type // array, map, fixed array
is_setter bool is_setter bool
is_map bool
is_array bool
is_farray bool
} }
pub struct IfExpr { pub struct IfExpr {
@ -1543,3 +1546,13 @@ pub fn (expr Expr) is_mut_ident() bool {
} }
return false return false
} }
// helper for dealing with `m[k1][k2][k3][k3] = value`
pub fn (mut lx IndexExpr) recursive_mapset_is_setter(val bool) {
lx.is_setter = val
if mut lx.left is IndexExpr {
if lx.left.is_map {
lx.left.recursive_mapset_is_setter(val)
}
}
}

View File

@ -2695,6 +2695,12 @@ pub fn (mut c Checker) assign_stmt(mut assign_stmt ast.AssignStmt) {
} }
} }
else { else {
if mut left is ast.IndexExpr {
// eprintln('>>> left.is_setter: ${left.is_setter:10} | left.is_map: ${left.is_map:10} | left.is_array: ${left.is_array:10}')
if left.is_map && left.is_setter {
left.recursive_mapset_is_setter(true)
}
}
if is_decl { if is_decl {
c.error('non-name `$left` on left side of `:=`', left.position()) c.error('non-name `$left` on left side of `:=`', left.position())
} }
@ -5223,6 +5229,18 @@ pub fn (mut c Checker) index_expr(mut node ast.IndexExpr) table.Type {
mut typ := c.expr(node.left) mut typ := c.expr(node.left)
node.left_type = typ node.left_type = typ
typ_sym := c.table.get_final_type_symbol(typ) typ_sym := c.table.get_final_type_symbol(typ)
match typ_sym.kind {
.map {
node.is_map = true
}
.array {
node.is_array = true
}
.array_fixed {
node.is_farray = true
}
else {}
}
if typ_sym.kind !in [.array, .array_fixed, .string, .map] && !typ.is_ptr() if typ_sym.kind !in [.array, .array_fixed, .string, .map] && !typ.is_ptr()
&& typ !in [table.byteptr_type, table.charptr_type] && !typ.has_flag(.variadic) { && typ !in [table.byteptr_type, table.charptr_type] && !typ.has_flag(.variadic) {
c.error('type `$typ_sym.name` does not support indexing', node.pos) c.error('type `$typ_sym.name` does not support indexing', node.pos)

View File

@ -4373,7 +4373,11 @@ fn (mut g Gen) index_expr(node ast.IndexExpr) {
g.is_array_set = true g.is_array_set = true
g.write('map_set_1(') g.write('map_set_1(')
} else { } else {
if node.is_setter {
g.write('(*(($elem_type_str*)map_get_and_set_1(') g.write('(*(($elem_type_str*)map_get_and_set_1(')
} else {
g.write('(*(($elem_type_str*)map_get_1(')
}
} }
if !left_is_ptr || node.left_type.has_flag(.shared_f) { if !left_is_ptr || node.left_type.has_flag(.shared_f) {
g.write('&') g.write('&')
@ -4409,7 +4413,11 @@ fn (mut g Gen) index_expr(node ast.IndexExpr) {
|| g.inside_map_index || g.inside_map_index
|| (g.is_assign_lhs && !g.is_array_set && get_and_set_types) { || (g.is_assign_lhs && !g.is_array_set && get_and_set_types) {
zero := g.type_default(info.value_type) zero := g.type_default(info.value_type)
if node.is_setter {
g.write('(*($elem_type_str*)map_get_and_set_1(') g.write('(*($elem_type_str*)map_get_and_set_1(')
} else {
g.write('(*($elem_type_str*)map_get_1(')
}
if !left_is_ptr { if !left_is_ptr {
g.write('&') g.write('&')
} }

View File

@ -348,6 +348,9 @@ pub fn (mut p Parser) expr_with_left(left ast.Expr, precedence int, is_stmt_iden
p.next() p.next()
right := p.expr(precedence - 1) right := p.expr(precedence - 1)
pos.update_last_line(p.prev_tok.line_nr) pos.update_last_line(p.prev_tok.line_nr)
if mut node is ast.IndexExpr {
node.recursive_mapset_is_setter(true)
}
node = ast.InfixExpr{ node = ast.InfixExpr{
left: node left: node
right: right right: right
@ -381,6 +384,9 @@ pub fn (mut p Parser) expr_with_left(left ast.Expr, precedence int, is_stmt_iden
p.error_with_pos('$p.tok must be on the same line as the previous token', p.error_with_pos('$p.tok must be on the same line as the previous token',
p.tok.position()) p.tok.position())
} }
if mut node is ast.IndexExpr {
node.recursive_mapset_is_setter(true)
}
node = ast.PostfixExpr{ node = ast.PostfixExpr{
op: p.tok.kind op: p.tok.kind
expr: node expr: node

View File

@ -1,14 +1,4 @@
struct Foo { fn test_map_of_map() {
mut:
name string
}
fn test_nested_maps() {
if true {
}
//
else {
}
mut x := map[string]map[string]int{} mut x := map[string]map[string]int{}
x['a'] = map[string]int{} x['a'] = map[string]int{}
assert x['a']['b'] == 0 assert x['a']['b'] == 0
@ -16,6 +6,9 @@ fn test_nested_maps() {
assert x['a']['b'] == 5 assert x['a']['b'] == 5
x['a']['b'] = 7 x['a']['b'] = 7
assert x['a']['b'] == 7 assert x['a']['b'] == 7
}
fn test_map_of_map_of_map() {
mut y := map[string]map[string]map[string]int{} mut y := map[string]map[string]map[string]int{}
y['a'] = map[string]map[string]int{} y['a'] = map[string]map[string]int{}
y['a']['b'] = map[string]int{} y['a']['b'] = map[string]int{}
@ -24,9 +17,18 @@ fn test_nested_maps() {
assert y['a']['b']['c'] == 5 assert y['a']['b']['c'] == 5
y['a']['b']['c'] = 7 y['a']['b']['c'] = 7
assert y['a']['b']['c'] == 7 assert y['a']['b']['c'] == 7
mut foos := map[string]map[string]Foo{} }
foos['a']['b'] = Foo{'bar'}
assert foos['a']['b'].name == 'bar' struct Foo {
foos['a']['b'].name = 'baz' mut:
assert foos['a']['b'].name == 'baz' name string
}
fn test_map_of_map_to_struct() {
mut foos := map[string]map[string]Foo{}
foos['zza']['zzb'] = Foo{'bar'}
assert foos['zza']['zzb'].name == 'bar'
//
foos['zza']['zzb'].name = 'baz'
assert foos['zza']['zzb'].name == 'baz'
} }