changeset 35800:6004dd91939b v9.1.0620

patch 9.1.0620: Vim9: segfauls with null objects Commit: https://github.com/vim/vim/commit/be82825687fcf020dc79667cc1cdf62ace2215f2 Author: Ernie Rael <errael@raelity.com> Date: Fri Jul 26 18:37:02 2024 +0200 patch 9.1.0620: Vim9: segfauls with null objects Problem: Vim9: segfauls with null objects (after v9.1.0219) Solution: Check object pointer being NULL (Ernie Rael) fixes: #15338 closes: #15349 Signed-off-by: Ernie Rael <errael@raelity.com> Signed-off-by: Yegappan Lakshmanan <yegappan@yahoo.com> Signed-off-by: Christian Brabandt <cb@256bit.org>
author Christian Brabandt <cb@256bit.org>
date Fri, 26 Jul 2024 18:45:03 +0200
parents b9a5588a755c
children b712ad7f9e45
files src/testdir/test_vim9_class.vim src/typval.c src/version.c src/vim9execute.c
diffstat 4 files changed, 130 insertions(+), 24 deletions(-) [+]
line wrap: on
line diff
--- a/src/testdir/test_vim9_class.vim
+++ b/src/testdir/test_vim9_class.vim
@@ -661,6 +661,31 @@ def Test_object_not_set()
     Func()
   END
   v9.CheckSourceFailure(lines, 'E1363: Incomplete type', 1)
+
+  # Reference a object variable through a null class object which is stored in a
+  # variable of type "any".
+  lines =<< trim END
+    vim9script
+
+    def Z()
+      var o: any = null_object
+      o.v = 4
+    enddef
+    Z()
+  END
+  v9.CheckSourceFailure(lines, 'E1360: Using a null object', 2)
+
+  # Do "echom" of a null object variable.
+  lines =<< trim END
+    vim9script
+
+    def X()
+      var x = null_object
+      echom x
+    enddef
+    X()
+  END
+  v9.CheckSourceFailure(lines, 'E1324: Using an Object as a String', 2)
 enddef
 
 " Null object assignment and comparison
@@ -7203,6 +7228,47 @@ def Test_null_object_method_call()
     T()
   END
   v9.CheckSourceFailure(lines, 'E1360: Using a null object', 2)
+
+  # Calling an object method defined in a class that is extended. This differs
+  # from the previous by invoking ISN_METHODCALL instead of ISN_DCALL.
+  lines =<< trim END
+    vim9script
+
+    class C0
+      def F()
+      enddef
+    endclass
+
+    class C extends C0
+    endclass
+
+    def X()
+      var o: C0 = null_object
+      o.F()
+    enddef
+    X()
+  END
+  v9.CheckSourceFailure(lines, 'E1360: Using a null object', 2)
+
+  # Getting a function ref an object method.
+  lines =<< trim END
+    vim9script
+
+    class C0
+      def F()
+      enddef
+    endclass
+
+    class C extends C0
+    endclass
+
+    def X()
+      var o: C0 = null_object
+      var XXX = o.F
+    enddef
+    X()
+  END
+  v9.CheckSourceFailure(lines, 'E1360: Using a null object', 2)
 enddef
 
 " Test for using a dict as an object member
--- a/src/typval.c
+++ b/src/typval.c
@@ -267,11 +267,16 @@ tv_get_bool_or_number_chk(
 	    break;
 	case VAR_OBJECT:
 	    {
-		class_T *cl = varp->vval.v_object->obj_class;
-		if (cl != NULL && IS_ENUM(cl))
-		    semsg(_(e_using_enum_str_as_number), cl->class_name);
+		if (varp->vval.v_object == NULL)
+		    emsg(_(e_using_object_as_string));
 		else
-		    emsg(_(e_using_object_as_number));
+		{
+		    class_T *cl = varp->vval.v_object->obj_class;
+		    if (cl != NULL && IS_ENUM(cl))
+			semsg(_(e_using_enum_str_as_number), cl->class_name);
+		    else
+			emsg(_(e_using_object_as_number));
+		}
 	    }
 	    break;
 	case VAR_VOID:
@@ -1146,11 +1151,16 @@ tv_get_string_buf_chk_strict(typval_T *v
 	    break;
 	case VAR_OBJECT:
 	    {
-		class_T *cl = varp->vval.v_object->obj_class;
-		if (cl != NULL && IS_ENUM(cl))
-		    semsg(_(e_using_enum_str_as_string), cl->class_name);
+		if (varp->vval.v_object == NULL)
+		    emsg(_(e_using_object_as_string));
 		else
-		    emsg(_(e_using_object_as_string));
+		{
+		    class_T *cl = varp->vval.v_object->obj_class;
+		    if (cl != NULL && IS_ENUM(cl))
+			semsg(_(e_using_enum_str_as_string), cl->class_name);
+		    else
+			emsg(_(e_using_object_as_string));
+		}
 	    }
 	    break;
 	case VAR_JOB:
--- a/src/version.c
+++ b/src/version.c
@@ -705,6 +705,8 @@ static char *(features[]) =
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    620,
+/**/
     619,
 /**/
     618,
--- a/src/vim9execute.c
+++ b/src/vim9execute.c
@@ -2254,26 +2254,35 @@ execute_storeindex(isn_T *iptr, ectx_T *
 	{
 	    // Need to get the member index now that the class is known.
 	    object_T *obj = tv_dest->vval.v_object;
-	    class_T *cl = obj->obj_class;
-	    char_u  *member = tv_idx->vval.v_string;
-
-	    int		m_idx;
-	    ocmember_T *m = object_member_lookup(cl, member, 0, &m_idx);
-	    if (m != NULL)
+	    if (obj == NULL)
 	    {
-		if (*member == '_')
-		{
-		    emsg_var_cl_define(e_cannot_access_protected_variable_str,
-							m->ocm_name, 0, cl);
-		    status = FAIL;
-		}
-
-		lidx = m_idx;
+		emsg(_(e_using_null_object));
+		status = FAIL;
 	    }
 	    else
 	    {
-		member_not_found_msg(cl, VAR_OBJECT, member, 0);
-		status = FAIL;
+		class_T *cl = obj->obj_class;
+		char_u  *member = tv_idx->vval.v_string;
+
+		int		m_idx;
+		ocmember_T *m = object_member_lookup(cl, member, 0, &m_idx);
+		if (m != NULL)
+		{
+		    if (*member == '_')
+		    {
+			emsg_var_cl_define(
+					e_cannot_access_protected_variable_str,
+					m->ocm_name, 0, cl);
+			status = FAIL;
+		    }
+
+		    lidx = m_idx;
+		}
+		else
+		{
+		    member_not_found_msg(cl, VAR_OBJECT, member, 0);
+		    status = FAIL;
+		}
 	    }
 	}
 	else if ((dest_type == VAR_LIST || dest_type == VAR_OBJECT)
@@ -3567,7 +3576,10 @@ exec_instructions(ectx_T *ectx)
 				p = tv_get_string_buf(tv, buf);
 			}
 			else
+			{
+			    SOURCING_LNUM = iptr->isn_lnum;
 			    p = tv_stringify(tv, buf);
+			}
 
 			len = (int)STRLEN(p);
 			if (GA_GROW_FAILS(&ga, len + 2))
@@ -4380,7 +4392,14 @@ exec_instructions(ectx_T *ectx)
 			object_required_error(tv);
 			goto on_error;
 		    }
+
 		    object_T *obj = tv->vval.v_object;
+		    if (obj == NULL)
+		    {
+			emsg(_(e_using_null_object));
+			goto on_error;
+		    }
+
 		    class_T *cl = obj->obj_class;
 
 		    // convert the interface index to the object index
@@ -4536,12 +4555,21 @@ exec_instructions(ectx_T *ectx)
 			    tv = STACK_TV_BOT(-1);
 			    if (tv->v_type != VAR_OBJECT)
 			    {
+				SOURCING_LNUM = iptr->isn_lnum;
 				object_required_error(tv);
 				vim_free(pt);
 				goto on_error;
 			    }
 
 			    object_T *obj = tv->vval.v_object;
+			    if (obj == NULL)
+			    {
+				SOURCING_LNUM = iptr->isn_lnum;
+				emsg(_(e_using_null_object));
+				vim_free(pt);
+				goto on_error;
+			    }
+
 			    cl = obj->obj_class;
 			    // drop the value from the stack
 			    clear_tv(tv);