]> granicus.if.org Git - vim/commitdiff
patch 9.0.1320: checking the type of a null object causes a crash v9.0.1320
authorBram Moolenaar <Bram@vim.org>
Sat, 18 Feb 2023 14:42:44 +0000 (14:42 +0000)
committerBram Moolenaar <Bram@vim.org>
Sat, 18 Feb 2023 14:42:44 +0000 (14:42 +0000)
Problem:    Checking the type of a null object causes a crash.
Solution:   Don't try to get the class of a null object. (closes #12005)
            Handle error from calling a user function better.

src/evalfunc.c
src/if_lua.c
src/proto/evalfunc.pro
src/proto/userfunc.pro
src/testdir/test_vim9_class.vim
src/userfunc.c
src/version.c
src/vim.h
src/vim9execute.c
src/vim9type.c

index 08f88471e3452ccacb13a88bbd62e3d2818921e7..7f6cbac0411d04a8aee3622cf44d77c7cbf12542 100644 (file)
@@ -3056,8 +3056,8 @@ internal_func_is_map(int idx)
     int
 check_internal_func(int idx, int argcount)
 {
-    int            res;
-    char    *name;
+    funcerror_T            res;
+    char           *name;
 
     if (argcount < global_functions[idx].f_min_argc)
        res = FCERR_TOOFEW;
@@ -3074,7 +3074,7 @@ check_internal_func(int idx, int argcount)
     return -1;
 }
 
-    int
+    funcerror_T
 call_internal_func(
        char_u      *name,
        int         argcount,
@@ -3107,7 +3107,7 @@ call_internal_func_by_idx(
 /*
  * Invoke a method for base->method().
  */
-    int
+    funcerror_T
 call_internal_method(
        char_u      *name,
        int         argcount,
index 45f233fdecb0b5d49dc503f804ad18f7e74b4b1a..655955081518be8cf9f85a12de327e82cf6f2078 100644 (file)
@@ -2776,11 +2776,11 @@ luaV_call_lua_func(
     if (lua_pcall(funcstate->L, luaargcount, 1, 0))
     {
        luaV_emsg(funcstate->L);
-       return FCERR_OTHER;
+       return (int)FCERR_OTHER;
     }
 
     luaV_checktypval(funcstate->L, -1, rettv, "get return value");
-    return FCERR_NONE;
+    return (int)FCERR_NONE;
 }
 
 /*
index 78998ff62d6eada49f90f65edccb576700153469..a720b643e479060a243bfedff635c6d29de97bc5 100644 (file)
@@ -10,9 +10,9 @@ void internal_func_get_argcount(int idx, int *argcount, int *min_argcount);
 type_T *internal_func_ret_type(int idx, int argcount, type2_T *argtypes, type_T **decl_type, garray_T *type_gap);
 int internal_func_is_map(int idx);
 int check_internal_func(int idx, int argcount);
-int call_internal_func(char_u *name, int argcount, typval_T *argvars, typval_T *rettv);
+funcerror_T call_internal_func(char_u *name, int argcount, typval_T *argvars, typval_T *rettv);
 void call_internal_func_by_idx(int idx, typval_T *argvars, typval_T *rettv);
-int call_internal_method(char_u *name, int argcount, typval_T *argvars, typval_T *rettv, typval_T *basetv);
+funcerror_T call_internal_method(char_u *name, int argcount, typval_T *argvars, typval_T *rettv, typval_T *basetv);
 int non_zero_arg(typval_T *argvars);
 buf_T *get_buf_arg(typval_T *arg);
 win_T *get_optional_window(typval_T *argvars, int idx);
index 94b53f6766df60302129eb7112c933b665dc80ea..cda0389b86d003921a56bc26efac9dd840b287fd 100644 (file)
@@ -9,7 +9,7 @@ char_u *deref_func_name(char_u *name, int *lenp, partial_T **partialp, type_T **
 void emsg_funcname(char *ermsg, char_u *name);
 int get_func_arguments(char_u **arg, evalarg_T *evalarg, int partial_argc, typval_T *argvars, int *argcount);
 int get_func_tv(char_u *name, int len, typval_T *rettv, char_u **arg, evalarg_T *evalarg, funcexe_T *funcexe);
-char_u *fname_trans_sid(char_u *name, char_u *fname_buf, char_u **tofree, int *error);
+char_u *fname_trans_sid(char_u *name, char_u *fname_buf, char_u **tofree, funcerror_T *error);
 void func_name_with_sid(char_u *name, int sid, char_u *buffer);
 ufunc_T *find_func_even_dead(char_u *name, int flags);
 ufunc_T *find_func(char_u *name, int is_global);
@@ -24,8 +24,8 @@ int funcdepth_get(void);
 void funcdepth_restore(int depth);
 funccall_T *create_funccal(ufunc_T *fp, typval_T *rettv);
 void remove_funccal(void);
-int check_user_func_argcount(ufunc_T *fp, int argcount);
-int call_user_func_check(ufunc_T *fp, int argcount, typval_T *argvars, typval_T *rettv, funcexe_T *funcexe, dict_T *selfdict);
+funcerror_T check_user_func_argcount(ufunc_T *fp, int argcount);
+funcerror_T call_user_func_check(ufunc_T *fp, int argcount, typval_T *argvars, typval_T *rettv, funcexe_T *funcexe, dict_T *selfdict);
 void save_funccal(funccal_entry_T *entry);
 void restore_funccal(void);
 funccall_T *get_current_funccal(void);
@@ -37,7 +37,7 @@ int func_call(char_u *name, typval_T *args, partial_T *partial, dict_T *selfdict
 int get_callback_depth(void);
 int call_callback(callback_T *callback, int len, typval_T *rettv, int argcount, typval_T *argvars);
 varnumber_T call_callback_retnr(callback_T *callback, int argcount, typval_T *argvars);
-void user_func_error(int error, char_u *name, int found_var);
+void user_func_error(funcerror_T error, char_u *name, int found_var);
 int call_func(char_u *funcname, int len, typval_T *rettv, int argcount_in, typval_T *argvars_in, funcexe_T *funcexe);
 int call_simple_func(char_u *funcname, int len, typval_T *rettv);
 char_u *printable_func_name(ufunc_T *fp);
index e73b31c9b3943073c7ca2172617ed5e550d07e58..2cd3bdd1b2fac9fbfabfbf4d977dab054bddecc8 100644 (file)
@@ -195,6 +195,26 @@ def Test_object_not_set()
       echo db[state.value]
   END
   v9.CheckScriptFailure(lines, 'E1360:')
+
+  lines =<< trim END
+      vim9script
+
+      class Background
+        this.background = 'dark'
+      endclass
+
+      class Colorscheme
+        this._bg: Background
+
+        def GetBackground(): string
+          return this._bg.background
+        enddef
+      endclass
+
+      var bg: Background           # UNINITIALIZED
+      echo Colorscheme.new(bg).GetBackground()
+  END
+  v9.CheckScriptFailure(lines, 'E1012: Type mismatch; expected object<Background> but got object<Unknown>')
 enddef
 
 def Test_class_member_initializer()
index 1009cd7ff0606f4c2425966dacf3428756e637e6..d5dd3698983496c25b60bdcbed731da5f2dae275 100644 (file)
@@ -1979,7 +1979,11 @@ eval_fname_sid(char_u *p)
  * and set "tofree".
  */
     char_u *
-fname_trans_sid(char_u *name, char_u *fname_buf, char_u **tofree, int *error)
+fname_trans_sid(
+       char_u      *name,
+       char_u      *fname_buf,
+       char_u      **tofree,
+       funcerror_T *error)
 {
     int                llen;
     char_u     *fname;
@@ -2716,7 +2720,7 @@ remove_funccal()
 /*
  * Call a user function.
  */
-    static void
+    static funcerror_T
 call_user_func(
     ufunc_T    *fp,            // pointer to function
     int                argcount,       // nr of args
@@ -2731,6 +2735,7 @@ call_user_func(
     int                save_sticky_cmdmod_flags = sticky_cmdmod_flags;
     funccall_T *fc;
     int                save_did_emsg;
+    funcerror_T retval = FCERR_NONE;
     int                default_arg_err = FALSE;
     dictitem_T *v;
     int                fixvar_idx = 0; // index in fc_fixvar[]
@@ -2755,14 +2760,14 @@ call_user_func(
     {
        rettv->v_type = VAR_NUMBER;
        rettv->vval.v_number = -1;
-       return;
+       return FCERR_FAILED;
     }
 
     line_breakcheck();         // check for CTRL-C hit
 
     fc = create_funccal(fp, rettv);
     if (fc == NULL)
-       return;
+       return FCERR_OTHER;
     fc->fc_level = ex_nesting_level;
     // Check if this function has a breakpoint.
     fc->fc_breakpoint = dbg_find_breakpoint(FALSE, fp->uf_name, (linenr_T)0);
@@ -2781,8 +2786,9 @@ call_user_func(
            profile_may_start_func(&profile_info, fp, caller);
 #endif
        sticky_cmdmod_flags = 0;
-       call_def_function(fp, argcount, argvars, 0,
-                          funcexe->fe_partial, funcexe->fe_object, fc, rettv);
+       if (call_def_function(fp, argcount, argvars, 0,
+                  funcexe->fe_partial, funcexe->fe_object, fc, rettv) == FAIL)
+           retval = FCERR_FAILED;
        funcdepth_decrement();
 #ifdef FEAT_PROFILE
        if (do_profiling == PROF_YES && (fp->uf_profiling
@@ -2791,7 +2797,7 @@ call_user_func(
 #endif
        remove_funccal();
        sticky_cmdmod_flags = save_sticky_cmdmod_flags;
-       return;
+       return retval;
     }
 
     islambda = fp->uf_flags & FC_LAMBDA;
@@ -3024,7 +3030,10 @@ call_user_func(
     did_emsg = FALSE;
 
     if (default_arg_err && (fp->uf_flags & FC_ABORT))
+    {
        did_emsg = TRUE;
+       retval = FCERR_FAILED;
+    }
     else if (islambda)
     {
        char_u *p = *(char_u **)fp->uf_lines.ga_data + 7;
@@ -3051,6 +3060,7 @@ call_user_func(
        clear_tv(rettv);
        rettv->v_type = VAR_NUMBER;
        rettv->vval.v_number = -1;
+       retval = FCERR_FAILED;
     }
 
 #ifdef FEAT_PROFILE
@@ -3134,13 +3144,15 @@ call_user_func(
     for (i = 0; i < tv_to_free_len; ++i)
        clear_tv(tv_to_free[i]);
     cleanup_function_call(fc);
+
+    return retval;
 }
 
 /*
  * Check the argument count for user function "fp".
  * Return FCERR_UNKNOWN if OK, FCERR_TOOFEW or FCERR_TOOMANY otherwise.
  */
-    int
+    funcerror_T
 check_user_func_argcount(ufunc_T *fp, int argcount)
 {
     int regular_args = fp->uf_args.ga_len;
@@ -3155,7 +3167,7 @@ check_user_func_argcount(ufunc_T *fp, int argcount)
 /*
  * Call a user function after checking the arguments.
  */
-    int
+    funcerror_T
 call_user_func_check(
        ufunc_T     *fp,
        int         argcount,
@@ -3164,7 +3176,7 @@ call_user_func_check(
        funcexe_T   *funcexe,
        dict_T      *selfdict)
 {
-    int error;
+    funcerror_T error = FCERR_NONE;
 
 #ifdef FEAT_LUA
     if (fp->uf_flags & FC_CFUNC)
@@ -3180,8 +3192,11 @@ call_user_func_check(
     error = check_user_func_argcount(fp, argcount);
     if (error != FCERR_UNKNOWN)
        return error;
+
     if ((fp->uf_flags & FC_DICT) && selfdict == NULL)
+    {
        error = FCERR_DICT;
+    }
     else
     {
        int             did_save_redo = FALSE;
@@ -3199,7 +3214,7 @@ call_user_func_check(
            did_save_redo = TRUE;
        }
        ++fp->uf_calls;
-       call_user_func(fp, argcount, argvars, rettv, funcexe,
+       error = call_user_func(fp, argcount, argvars, rettv, funcexe,
                                   (fp->uf_flags & FC_DICT) ? selfdict : NULL);
        if (--fp->uf_calls <= 0 && fp->uf_refcount <= 0)
            // Function was unreferenced while being used, free it now.
@@ -3207,8 +3222,8 @@ call_user_func_check(
        if (did_save_redo)
            restoreRedobuff(&save_redo);
        restore_search_patterns();
-       error = FCERR_NONE;
     }
+
     return error;
 }
 
@@ -3542,7 +3557,7 @@ call_callback_retnr(
  * Nothing if "error" is FCERR_NONE.
  */
     void
-user_func_error(int error, char_u *name, int found_var)
+user_func_error(funcerror_T error, char_u *name, int found_var)
 {
     switch (error)
     {
@@ -3571,6 +3586,12 @@ user_func_error(int error, char_u *name, int found_var)
                emsg_funcname(e_calling_dict_function_without_dictionary_str,
                                                                         name);
                break;
+       case FCERR_OTHER:
+       case FCERR_FAILED:
+               // assume the error message was already given
+               break;
+       case FCERR_NONE:
+               break;
     }
 }
 
@@ -3591,7 +3612,7 @@ call_func(
     funcexe_T  *funcexe)       // more arguments
 {
     int                ret = FAIL;
-    int                error = FCERR_NONE;
+    funcerror_T        error = FCERR_NONE;
     int                i;
     ufunc_T    *fp = NULL;
     char_u     fname_buf[FLEN_FIXED + 1];
@@ -3823,7 +3844,7 @@ call_simple_func(
     typval_T   *rettv)         // return value goes here
 {
     int                ret = FAIL;
-    int                error = FCERR_NONE;
+    funcerror_T        error = FCERR_NONE;
     char_u     fname_buf[FLEN_FIXED + 1];
     char_u     *tofree = NULL;
     char_u     *name;
@@ -5973,8 +5994,7 @@ ex_defer_inner(
            // we tolerate an unknown function here, it might be defined later
            if (ufunc != NULL)
            {
-               int error = check_user_func_argcount(ufunc, argcount);
-
+               funcerror_T error = check_user_func_argcount(ufunc, argcount);
                if (error != FCERR_UNKNOWN)
                {
                    user_func_error(error, name, FALSE);
@@ -6449,7 +6469,6 @@ make_partial(dict_T *selfdict_in, typval_T *rettv)
     char_u     *fname;
     ufunc_T    *fp = NULL;
     char_u     fname_buf[FLEN_FIXED + 1];
-    int                error;
     dict_T     *selfdict = selfdict_in;
 
     if (rettv->v_type == VAR_PARTIAL  && rettv->vval.v_partial != NULL
@@ -6470,6 +6489,7 @@ make_partial(dict_T *selfdict_in, typval_T *rettv)
        else
        {
            char_u      *tofree = NULL;
+           funcerror_T error;
 
            // Translate "s:func" to the stored function name.
            fname = fname_trans_sid(fname, fname_buf, &tofree, &error);
@@ -6881,7 +6901,7 @@ set_ref_in_func(char_u *name, ufunc_T *fp_in, int copyID)
 {
     ufunc_T    *fp = fp_in;
     funccall_T *fc;
-    int                error = FCERR_NONE;
+    funcerror_T        error = FCERR_NONE;
     char_u     fname_buf[FLEN_FIXED + 1];
     char_u     *tofree = NULL;
     char_u     *fname;
index d6cee20a703b2fd89f59e677f2204478f8a7499e..20df49098a0e8e647948a658901ce137db485bf9 100644 (file)
@@ -695,6 +695,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    1320,
 /**/
     1319,
 /**/
index 67bb23e515955a8c4906366ca521225c233005a6..45b5fa925c7beb0beddb227d9c6784369f769e1a 100644 (file)
--- a/src/vim.h
+++ b/src/vim.h
@@ -2270,6 +2270,20 @@ typedef enum {
     KEYPROTOCOL_FAIL
 } keyprot_T;
 
+// errors for when calling a function
+typedef enum {
+    FCERR_NONE,                // no error
+    FCERR_UNKNOWN,     // unknown function
+    FCERR_TOOMANY,     // too many arguments
+    FCERR_TOOFEW,      // too few arguments
+    FCERR_SCRIPT,      // missing script context
+    FCERR_DICT,                // missing dict
+    FCERR_OTHER,       // another kind of error
+    FCERR_DELETED,     // function was deleted
+    FCERR_NOTMETHOD,   // function cannot be used as a method
+    FCERR_FAILED,      // error while executing the function
+} funcerror_T;
+
 // Flags for assignment functions.
 #define ASSIGN_VAR     0     // ":var" (nothing special)
 #define ASSIGN_FINAL   0x01  // ":final"
@@ -2703,17 +2717,6 @@ typedef enum {
 #define DO_NOT_FREE_CNT 99999  // refcount for dict or list that should not
                                // be freed.
 
-// errors for when calling a function
-#define FCERR_UNKNOWN  0
-#define FCERR_TOOMANY  1
-#define FCERR_TOOFEW   2
-#define FCERR_SCRIPT   3
-#define FCERR_DICT     4
-#define FCERR_NONE     5
-#define FCERR_OTHER    6
-#define FCERR_DELETED  7
-#define FCERR_NOTMETHOD        8   // function cannot be used as a method
-
 // fixed buffer length for fname_trans_sid()
 #define FLEN_FIXED 40
 
index 49042a4afec1532b77e3ba11b6e9cf0d6588a2cb..4bbed825940a979c8eee6f9deca9b4a2efe14dc6 100644 (file)
@@ -1283,7 +1283,7 @@ call_ufunc(
 {
     typval_T   argvars[MAX_FUNC_ARGS];
     funcexe_T   funcexe;
-    int                error;
+    funcerror_T        error;
     int                idx;
     int                did_emsg_before = did_emsg;
     compiletype_T compile_type = get_compile_type(ufunc);
@@ -1464,10 +1464,10 @@ call_partial(
        name = tv->vval.v_string;
     if (name != NULL)
     {
-       char_u  fname_buf[FLEN_FIXED + 1];
-       char_u  *tofree = NULL;
-       int     error = FCERR_NONE;
-       char_u  *fname;
+       char_u      fname_buf[FLEN_FIXED + 1];
+       char_u      *tofree = NULL;
+       funcerror_T error = FCERR_NONE;
+       char_u      *fname;
 
        // May need to translate <SNR>123_ to K_SNR.
        fname = fname_trans_sid(name, fname_buf, &tofree, &error);
index 075eb60d54c2f9ec164fdea85fbd296f11dfba91..c9f72df91b1a4f53953417f70fe86fc30d1537a5 100644 (file)
@@ -1659,7 +1659,8 @@ type_name(type_T *type, char **tofree)
 
     if (type->tt_type == VAR_OBJECT || type->tt_type == VAR_CLASS)
     {
-       char_u *class_name = ((class_T *)type->tt_member)->class_name;
+       char_u *class_name = type->tt_member == NULL ? (char_u *)"Unknown"
+                                   : ((class_T *)type->tt_member)->class_name;
        size_t len = STRLEN(name) + STRLEN(class_name) + 3;
        *tofree = alloc(len);
        if (*tofree != NULL)