Skip to content

Commit

Permalink
patch 9.0.2001: Vim9: segfault with islocked()
Browse files Browse the repository at this point in the history
Problem:  Vim9: segfault with islocked()
Solution: Check that the lval pointer is not null for objects and
          class variables

closes: #13295

Signed-off-by: Christian Brabandt <cb@256bit.org>
Co-authored-by: Ernie Rael <errael@raelity.com>
  • Loading branch information
errael authored and chrisbra committed Oct 7, 2023
1 parent 1087b8c commit 9771b2a
Show file tree
Hide file tree
Showing 5 changed files with 112 additions and 9 deletions.
6 changes: 2 additions & 4 deletions src/eval.c
Original file line number Diff line number Diff line change
Expand Up @@ -1375,6 +1375,7 @@ get_lval(
&& v_type != VAR_OBJECT
&& v_type != VAR_CLASS)
{
// TODO: have a message with obj/class, not just dict,
if (!quiet)
semsg(_(e_dot_can_only_be_used_on_dictionary_str), name);
return NULL;
Expand All @@ -1385,6 +1386,7 @@ get_lval(
&& v_type != VAR_OBJECT
&& v_type != VAR_CLASS)
{
// TODO: have a message with obj/class, not just dict/list/blob,
if (!quiet)
emsg(_(e_can_only_index_list_dictionary_or_blob));
return NULL;
Expand Down Expand Up @@ -1739,10 +1741,6 @@ get_lval(
}
}

// TODO: dont' check access if inside class
// TODO: is GLV_READ_ONLY the right thing to use
// for class/object member access?
// Probably in some cases. Need inside class check
if (lp->ll_valtype == NULL)
{
int m_idx;
Expand Down
16 changes: 16 additions & 0 deletions src/evalfunc.c
Original file line number Diff line number Diff line change
Expand Up @@ -7347,6 +7347,22 @@ f_islocked(typval_T *argvars, typval_T *rettv)
|| tv_islocked(&di->di_tv));
}
}
else if (lv.ll_object != NULL)
{
typval_T *tv = ((typval_T *)(lv.ll_object + 1)) + lv.ll_oi;
rettv->vval.v_number = tv_islocked(tv);
#ifdef LOG_LOCKVAR
ch_log(NULL, "LKVAR: f_islocked(): name %s (obj)", lv.ll_name);
#endif
}
else if (lv.ll_class != NULL)
{
typval_T *tv = &lv.ll_class->class_members_tv[lv.ll_oi];
rettv->vval.v_number = tv_islocked(tv);
#ifdef LOG_LOCKVAR
ch_log(NULL, "LKVAR: f_islocked(): name %s (cl)", lv.ll_name);
#endif
}
else if (lv.ll_range)
emsg(_(e_range_not_allowed));
else if (lv.ll_newkey != NULL)
Expand Down
17 changes: 12 additions & 5 deletions src/structs.h
Original file line number Diff line number Diff line change
Expand Up @@ -4547,11 +4547,18 @@ typedef struct
* "tv" points to the (first) list item value
* "li" points to the (first) list item
* "range", "n1", "n2" and "empty2" indicate what items are used.
* For a member in a class/object: TODO: verify fields
* For a plain class or object:
* "name" points to the variable name.
* "exp_name" is NULL.
* "tv" points to the variable
* "is_root" TRUE
* For a variable in a class/object: (class is not NULL)
* "name" points to the (expanded) variable name.
* "exp_name" NULL or non-NULL, to be freed later.
* "tv" points to the (first) list item value
* "oi" index into member array, see _type to determine which array
* "tv" May point to class/object variable.
* "object" object containing variable, NULL if class variable
* "class" class of object or class containing variable
* "oi" index into class/object of tv
* For an existing Dict item:
* "name" points to the (expanded) variable name.
* "exp_name" NULL or non-NULL, to be freed later.
Expand Down Expand Up @@ -4591,8 +4598,8 @@ typedef struct lval_S
object_T *ll_object; // The object or NULL, class is not NULL
class_T *ll_class; // The class or NULL, object may be NULL
int ll_oi; // The object/class member index
int ll_is_root; // Special case. ll_tv is lval_root,
// ignore the rest.
int ll_is_root; // TRUE if ll_tv is the lval_root, like a
// plain object/class. ll_tv is variable.
} lval_T;

/**
Expand Down
80 changes: 80 additions & 0 deletions src/testdir/test_vim9_class.vim
Original file line number Diff line number Diff line change
Expand Up @@ -4161,6 +4161,86 @@ def Test_lockvar_general()
v9.CheckSourceFailure(lines, 'E1333: Cannot access private variable "_v1" in class "C"')
enddef

" Test builtin islocked()
def Test_lockvar_islocked()
# Can't lock class/object variable
# Lock class/object variable's value
# Lock item of variabl's value (a list item)
# varible is at index 1 within class/object
var lines =<< trim END
vim9script

class C
this.o0: list<list<number>> = [ [0], [1], [2]]
this.o1: list<list<number>> = [[10], [11], [12]]
static c0: list<list<number>> = [[20], [21], [22]]
static c1: list<list<number>> = [[30], [31], [32]]
endclass

def LockIt(arg: any)
lockvar arg
enddef

def UnlockIt(arg: any)
unlockvar arg
enddef

var obj = C.new()
#lockvar obj.o1 # can't lock something you can't write to

try
lockvar obj.o1 # can't lock something you can't write to
call assert_false(1, '"lockvar obj.o1" should have failed')
catch
call assert_exception('E1335:')
endtry

LockIt(obj.o1) # but can lock it's value
assert_equal(1, islocked("obj.o1"))
assert_equal(1, islocked("obj.o1[0]"))
assert_equal(1, islocked("obj.o1[1]"))
UnlockIt(obj.o1)
assert_equal(0, islocked("obj.o1"))
assert_equal(0, islocked("obj.o1[0]"))

lockvar obj.o1[0]
assert_equal(0, islocked("obj.o1"))
assert_equal(1, islocked("obj.o1[0]"))
assert_equal(0, islocked("obj.o1[1]"))
unlockvar obj.o1[0]
assert_equal(0, islocked("obj.o1"))
assert_equal(0, islocked("obj.o1[0]"))

# Same thing, but with a static

try
lockvar C.c1 # can't lock something you can't write to
call assert_false(1, '"lockvar C.c1" should have failed')
catch
call assert_exception('E1335:')
endtry

LockIt(C.c1) # but can lock it's value
assert_equal(1, islocked("C.c1"))
assert_equal(1, islocked("C.c1[0]"))
assert_equal(1, islocked("C.c1[1]"))
UnlockIt(C.c1)
assert_equal(0, islocked("C.c1"))
assert_equal(0, islocked("C.c1[0]"))

lockvar C.c1[0]
assert_equal(0, islocked("C.c1"))
assert_equal(1, islocked("C.c1[0]"))
assert_equal(0, islocked("C.c1[1]"))
unlockvar C.c1[0]
assert_equal(0, islocked("C.c1"))
assert_equal(0, islocked("C.c1[0]"))
END
v9.CheckSourceSuccess(lines)
lines =<< trim END
END
enddef

" Test for a private object method
def Test_private_object_method()
# Try calling a private method using an object (at the script level)
Expand Down
2 changes: 2 additions & 0 deletions src/version.c
Original file line number Diff line number Diff line change
Expand Up @@ -704,6 +704,8 @@ static char *(features[]) =

static int included_patches[] =
{ /* Add new patch number below this line */
/**/
2001,
/**/
2000,
/**/
Expand Down

0 comments on commit 9771b2a

Please sign in to comment.