Mercurial > vim
comparison src/if_lua.c @ 32807:0582b3b40319 v9.0.1719
patch 9.0.1719: if_lua: crash for for Lua functions invoked via Vim callbacks
Commit: https://github.com/vim/vim/commit/8a35033782de4e1f43fba15543fc8fb458944194
Author: Jesse Pavel <jpavel@alum.mit.edu>
Date: Sun Aug 13 22:05:45 2023 -0400
patch 9.0.1719: if_lua: crash for for Lua functions invoked via Vim callbacks
Problem: if_lua: crash for Lua functions invoked via Vim callbacks
Solution: Use Lua registry rather than upvalues for udata cache
closes: #12785
Signed-off-by: Christian Brabandt <cb@256bit.org>
Co-authored-by: Jesse Pavel <jpavel@alum.mit.edu>
author | Christian Brabandt <cb@256bit.org> |
---|---|
date | Tue, 15 Aug 2023 23:30:03 +0200 |
parents | 4545f58c8490 |
children | cf6d98955c64 |
comparison
equal
deleted
inserted
replaced
32806:7328cddca686 | 32807:0582b3b40319 |
---|---|
14 | 14 |
15 #include <lua.h> | 15 #include <lua.h> |
16 #include <lualib.h> | 16 #include <lualib.h> |
17 #include <lauxlib.h> | 17 #include <lauxlib.h> |
18 | 18 |
19 #if __STDC_VERSION__ >= 199901L | |
20 # define LUAV_INLINE inline | |
21 #else | |
22 # define LUAV_INLINE | |
23 #endif | |
24 | |
19 // Only do the following when the feature is enabled. Needed for "make | 25 // Only do the following when the feature is enabled. Needed for "make |
20 // depend". | 26 // depend". |
21 #if defined(FEAT_LUA) || defined(PROTO) | 27 #if defined(FEAT_LUA) || defined(PROTO) |
22 | 28 |
23 #define LUAVIM_CHUNKNAME "vim chunk" | 29 #define LUAVIM_CHUNKNAME "vim chunk" |
59 static const char LUAVIM_LUAEVAL[] = "luaV_luaeval"; | 65 static const char LUAVIM_LUAEVAL[] = "luaV_luaeval"; |
60 static const char LUAVIM_SETREF[] = "luaV_setref"; | 66 static const char LUAVIM_SETREF[] = "luaV_setref"; |
61 | 67 |
62 static const char LUA___CALL[] = "__call"; | 68 static const char LUA___CALL[] = "__call"; |
63 | 69 |
64 // most functions are closures with a cache table as first upvalue; | 70 // get/setudata manage references to vim userdata in a cache table through |
65 // get/setudata manage references to vim userdata in cache table through | 71 // object pointers (light userdata). The cache table itself is retrieved |
66 // object pointers (light userdata) | 72 // from the registry. |
67 #define luaV_getudata(L, v) \ | 73 |
68 lua_pushlightuserdata((L), (void *) (v)); \ | 74 static const char LUAVIM_UDATA_CACHE[] = "luaV_udata_cache"; |
69 lua_rawget((L), lua_upvalueindex(1)) | 75 |
70 #define luaV_setudata(L, v) \ | 76 static void LUAV_INLINE |
71 lua_pushlightuserdata((L), (void *) (v)); \ | 77 luaV_getudata(lua_State *L, void *v) |
72 lua_pushvalue((L), -2); \ | 78 { |
73 lua_rawset((L), lua_upvalueindex(1)) | 79 lua_pushlightuserdata(L, (void *) LUAVIM_UDATA_CACHE); |
80 lua_rawget(L, LUA_REGISTRYINDEX); // now the cache table is at the top of the stack | |
81 lua_pushlightuserdata(L, v); | |
82 lua_rawget(L, -2); | |
83 lua_remove(L, -2); // remove the cache table from the stack | |
84 } | |
85 | |
86 static void LUAV_INLINE | |
87 luaV_setudata(lua_State *L, void *v) | |
88 { | |
89 lua_pushlightuserdata(L, (void *) LUAVIM_UDATA_CACHE); | |
90 lua_rawget(L, LUA_REGISTRYINDEX); // cache table is at -1 | |
91 lua_pushlightuserdata(L, v); // ...now at -2 | |
92 lua_pushvalue(L, -3); // copy the userdata (cache at -3) | |
93 lua_rawset(L, -3); // consumes two stack items | |
94 lua_pop(L, 1); // and remove the cache table | |
95 } | |
96 | |
74 #define luaV_getfield(L, s) \ | 97 #define luaV_getfield(L, s) \ |
75 lua_pushlightuserdata((L), (void *)(s)); \ | 98 lua_pushlightuserdata((L), (void *)(s)); \ |
76 lua_rawget((L), LUA_REGISTRYINDEX) | 99 lua_rawget((L), LUA_REGISTRYINDEX) |
77 #define luaV_checksandbox(L) \ | 100 #define luaV_checksandbox(L) \ |
78 if (sandbox) luaL_error((L), "not allowed in sandbox") | 101 if (sandbox) luaL_error((L), "not allowed in sandbox") |
90 static luaV_Funcref *luaV_pushfuncref(lua_State *L, char_u *name); | 113 static luaV_Funcref *luaV_pushfuncref(lua_State *L, char_u *name); |
91 static int luaV_call_lua_func(int argcount, typval_T *argvars, typval_T *rettv, void *state); | 114 static int luaV_call_lua_func(int argcount, typval_T *argvars, typval_T *rettv, void *state); |
92 static void luaV_call_lua_func_free(void *state); | 115 static void luaV_call_lua_func_free(void *state); |
93 | 116 |
94 #if LUA_VERSION_NUM <= 501 | 117 #if LUA_VERSION_NUM <= 501 |
95 #define luaV_openlib(L, l, n) luaL_openlib(L, NULL, l, n) | 118 #define luaV_register(L, l) luaL_register(L, NULL, l) |
96 #define luaL_typeerror luaL_typerror | 119 #define luaL_typeerror luaL_typerror |
97 #else | 120 #else |
98 #define luaV_openlib luaL_setfuncs | 121 #define luaV_register(L, l) luaL_setfuncs(L, l, 0) |
99 #endif | 122 #endif |
100 | 123 |
101 #ifdef DYNAMIC_LUA | 124 #ifdef DYNAMIC_LUA |
102 | 125 |
103 #ifndef MSWIN | 126 #ifndef MSWIN |
877 } | 900 } |
878 | 901 |
879 static int | 902 static int |
880 luaV_list_iter(lua_State *L) | 903 luaV_list_iter(lua_State *L) |
881 { | 904 { |
882 listitem_T *li = (listitem_T *) lua_touserdata(L, lua_upvalueindex(2)); | 905 listitem_T *li = (listitem_T *) lua_touserdata(L, lua_upvalueindex(1)); |
883 if (li == NULL) return 0; | 906 if (li == NULL) return 0; |
884 luaV_pushtypval(L, &li->li_tv); | 907 luaV_pushtypval(L, &li->li_tv); |
885 lua_pushlightuserdata(L, (void *) li->li_next); | 908 lua_pushlightuserdata(L, (void *) li->li_next); |
886 lua_replace(L, lua_upvalueindex(2)); | 909 lua_replace(L, lua_upvalueindex(1)); |
887 return 1; | 910 return 1; |
888 } | 911 } |
889 | 912 |
890 static int | 913 static int |
891 luaV_list_call(lua_State *L) | 914 luaV_list_call(lua_State *L) |
892 { | 915 { |
893 list_T *l = luaV_unbox(L, luaV_List, 1); | 916 list_T *l = luaV_unbox(L, luaV_List, 1); |
894 lua_pushvalue(L, lua_upvalueindex(1)); // pass cache table along | |
895 lua_pushlightuserdata(L, (void *) l->lv_first); | 917 lua_pushlightuserdata(L, (void *) l->lv_first); |
896 lua_pushcclosure(L, luaV_list_iter, 2); | 918 lua_pushcclosure(L, luaV_list_iter, 1); |
897 return 1; | 919 return 1; |
898 } | 920 } |
899 | 921 |
900 static int | 922 static int |
901 luaV_list_index(lua_State *L) | 923 luaV_list_index(lua_State *L) |
1055 | 1077 |
1056 static int | 1078 static int |
1057 luaV_dict_iter(lua_State *L UNUSED) | 1079 luaV_dict_iter(lua_State *L UNUSED) |
1058 { | 1080 { |
1059 #ifdef FEAT_EVAL | 1081 #ifdef FEAT_EVAL |
1060 hashitem_T *hi = (hashitem_T *) lua_touserdata(L, lua_upvalueindex(2)); | 1082 hashitem_T *hi = (hashitem_T *) lua_touserdata(L, lua_upvalueindex(1)); |
1061 int n = lua_tointeger(L, lua_upvalueindex(3)); | 1083 int n = lua_tointeger(L, lua_upvalueindex(2)); |
1062 dictitem_T *di; | 1084 dictitem_T *di; |
1063 if (n <= 0) return 0; | 1085 if (n <= 0) return 0; |
1064 while (HASHITEM_EMPTY(hi)) hi++; | 1086 while (HASHITEM_EMPTY(hi)) hi++; |
1065 di = dict_lookup(hi); | 1087 di = dict_lookup(hi); |
1066 lua_pushstring(L, (char *) hi->hi_key); | 1088 lua_pushstring(L, (char *) hi->hi_key); |
1067 luaV_pushtypval(L, &di->di_tv); | 1089 luaV_pushtypval(L, &di->di_tv); |
1068 lua_pushlightuserdata(L, (void *) (hi + 1)); | 1090 lua_pushlightuserdata(L, (void *) (hi + 1)); |
1091 lua_replace(L, lua_upvalueindex(1)); | |
1092 lua_pushinteger(L, n - 1); | |
1069 lua_replace(L, lua_upvalueindex(2)); | 1093 lua_replace(L, lua_upvalueindex(2)); |
1070 lua_pushinteger(L, n - 1); | |
1071 lua_replace(L, lua_upvalueindex(3)); | |
1072 return 2; | 1094 return 2; |
1073 #else | 1095 #else |
1074 return 0; | 1096 return 0; |
1075 #endif | 1097 #endif |
1076 } | 1098 } |
1078 static int | 1100 static int |
1079 luaV_dict_call(lua_State *L) | 1101 luaV_dict_call(lua_State *L) |
1080 { | 1102 { |
1081 dict_T *d = luaV_unbox(L, luaV_Dict, 1); | 1103 dict_T *d = luaV_unbox(L, luaV_Dict, 1); |
1082 hashtab_T *ht = &d->dv_hashtab; | 1104 hashtab_T *ht = &d->dv_hashtab; |
1083 lua_pushvalue(L, lua_upvalueindex(1)); // pass cache table along | |
1084 lua_pushlightuserdata(L, (void *) ht->ht_array); | 1105 lua_pushlightuserdata(L, (void *) ht->ht_array); |
1085 lua_pushinteger(L, ht->ht_used); // # remaining items | 1106 lua_pushinteger(L, ht->ht_used); // # remaining items |
1086 lua_pushcclosure(L, luaV_dict_iter, 3); | 1107 lua_pushcclosure(L, luaV_dict_iter, 2); |
1087 return 1; | 1108 return 1; |
1088 } | 1109 } |
1089 | 1110 |
1090 static int | 1111 static int |
1091 luaV_dict_index(lua_State *L) | 1112 luaV_dict_index(lua_State *L) |
2320 luaV_setref(lua_State *L) | 2341 luaV_setref(lua_State *L) |
2321 { | 2342 { |
2322 int copyID = lua_tointeger(L, 1); | 2343 int copyID = lua_tointeger(L, 1); |
2323 int abort = FALSE; | 2344 int abort = FALSE; |
2324 | 2345 |
2346 lua_pushlightuserdata(L, (void *) LUAVIM_UDATA_CACHE); | |
2347 lua_rawget(L, LUA_REGISTRYINDEX); // the cache table | |
2348 | |
2325 luaV_getfield(L, LUAVIM_LIST); | 2349 luaV_getfield(L, LUAVIM_LIST); |
2326 luaV_getfield(L, LUAVIM_DICT); | 2350 luaV_getfield(L, LUAVIM_DICT); |
2327 luaV_getfield(L, LUAVIM_FUNCREF); | 2351 luaV_getfield(L, LUAVIM_FUNCREF); |
2328 lua_pushnil(L); | 2352 lua_pushnil(L); |
2329 // traverse cache table | 2353 // traverse cache table |
2330 while (!abort && lua_next(L, lua_upvalueindex(1)) != 0) | 2354 while (!abort && lua_next(L, 2) != 0) |
2331 { | 2355 { |
2332 lua_getmetatable(L, -1); | 2356 lua_getmetatable(L, -1); |
2333 if (lua_rawequal(L, -1, 2)) // list? | 2357 if (lua_rawequal(L, -1, 3)) // list? |
2334 { | 2358 { |
2335 list_T *l = (list_T *)lua_touserdata(L, 5); // key | 2359 list_T *l = (list_T *)lua_touserdata(L, 6); // key |
2336 | 2360 |
2337 abort = set_ref_in_list(l, copyID); | 2361 abort = set_ref_in_list(l, copyID); |
2338 } | 2362 } |
2339 else if (lua_rawequal(L, -1, 3)) // dict? | 2363 else if (lua_rawequal(L, -1, 4)) // dict? |
2340 { | 2364 { |
2341 dict_T *d = (dict_T *)lua_touserdata(L, 5); // key | 2365 dict_T *d = (dict_T *)lua_touserdata(L, 6); // key |
2342 | 2366 |
2343 abort = set_ref_in_dict(d, copyID); | 2367 abort = set_ref_in_dict(d, copyID); |
2344 } | 2368 } |
2345 else if (lua_rawequal(L, -1, 4)) // funcref? | 2369 else if (lua_rawequal(L, -1, 5)) // funcref? |
2346 { | 2370 { |
2347 luaV_Funcref *f = (luaV_Funcref *)lua_touserdata(L, 5); // key | 2371 luaV_Funcref *f = (luaV_Funcref *)lua_touserdata(L, 6); // key |
2348 | 2372 |
2349 abort = set_ref_in_dict(f->self, copyID); | 2373 abort = set_ref_in_dict(f->self, copyID); |
2350 } | 2374 } |
2351 lua_pop(L, 2); // metatable and value | 2375 lua_pop(L, 2); // metatable and value |
2352 } | 2376 } |
2464 "end" | 2488 "end" |
2465 | 2489 |
2466 static int | 2490 static int |
2467 luaopen_vim(lua_State *L) | 2491 luaopen_vim(lua_State *L) |
2468 { | 2492 { |
2469 // set cache table | 2493 lua_newtable(L); // cache table |
2470 lua_newtable(L); | 2494 lua_newtable(L); // cache table's metatable |
2471 lua_newtable(L); | |
2472 lua_pushstring(L, "v"); | 2495 lua_pushstring(L, "v"); |
2473 lua_setfield(L, -2, "__mode"); | 2496 lua_setfield(L, -2, "__mode"); |
2474 lua_setmetatable(L, -2); // cache is weak-valued | 2497 lua_setmetatable(L, -2); // cache is weak-valued |
2498 // put the cache table in the registry for luaV_get/setudata() | |
2499 lua_pushlightuserdata(L, (void *) LUAVIM_UDATA_CACHE); | |
2500 lua_pushvalue(L, -2); | |
2501 lua_rawset(L, LUA_REGISTRYINDEX); | |
2502 lua_pop(L, 1); // we don't need the cache table here anymore | |
2475 // print | 2503 // print |
2476 lua_pushcfunction(L, luaV_print); | 2504 lua_pushcfunction(L, luaV_print); |
2477 lua_setglobal(L, "print"); | 2505 lua_setglobal(L, "print"); |
2478 // debug.debug | 2506 // debug.debug |
2479 lua_getglobal(L, "debug"); | 2507 lua_getglobal(L, "debug"); |
2480 lua_pushcfunction(L, luaV_debug); | 2508 lua_pushcfunction(L, luaV_debug); |
2481 lua_setfield(L, -2, "debug"); | 2509 lua_setfield(L, -2, "debug"); |
2482 lua_pop(L, 1); | 2510 lua_pop(L, 1); |
2483 // free | 2511 // free |
2484 lua_pushlightuserdata(L, (void *) LUAVIM_FREE); | 2512 lua_pushlightuserdata(L, (void *) LUAVIM_FREE); |
2485 lua_pushvalue(L, 1); // cache table | 2513 lua_pushcfunction(L, luaV_free); |
2486 lua_pushcclosure(L, luaV_free, 1); | |
2487 lua_rawset(L, LUA_REGISTRYINDEX); | 2514 lua_rawset(L, LUA_REGISTRYINDEX); |
2488 // luaeval | 2515 // luaeval |
2489 lua_pushlightuserdata(L, (void *) LUAVIM_LUAEVAL); | 2516 lua_pushlightuserdata(L, (void *) LUAVIM_LUAEVAL); |
2490 lua_pushvalue(L, 1); // cache table | 2517 lua_pushcfunction(L, luaV_luaeval); |
2491 lua_pushcclosure(L, luaV_luaeval, 1); | |
2492 lua_rawset(L, LUA_REGISTRYINDEX); | 2518 lua_rawset(L, LUA_REGISTRYINDEX); |
2493 // setref | 2519 // setref |
2494 lua_pushlightuserdata(L, (void *) LUAVIM_SETREF); | 2520 lua_pushlightuserdata(L, (void *) LUAVIM_SETREF); |
2495 lua_pushvalue(L, 1); // cache table | 2521 lua_pushcfunction(L, luaV_setref); |
2496 lua_pushcclosure(L, luaV_setref, 1); | |
2497 lua_rawset(L, LUA_REGISTRYINDEX); | 2522 lua_rawset(L, LUA_REGISTRYINDEX); |
2498 // register | 2523 // register |
2499 luaV_newmetatable(L, LUAVIM_LIST); | 2524 luaV_newmetatable(L, LUAVIM_LIST); |
2500 lua_pushvalue(L, 1); | 2525 luaV_register(L, luaV_List_mt); |
2501 luaV_openlib(L, luaV_List_mt, 1); | 2526 lua_pop(L, 1); |
2502 luaV_newmetatable(L, LUAVIM_DICT); | 2527 luaV_newmetatable(L, LUAVIM_DICT); |
2503 lua_pushvalue(L, 1); | 2528 luaV_register(L, luaV_Dict_mt); |
2504 luaV_openlib(L, luaV_Dict_mt, 1); | 2529 lua_pop(L, 1); |
2505 luaV_newmetatable(L, LUAVIM_BLOB); | 2530 luaV_newmetatable(L, LUAVIM_BLOB); |
2506 lua_pushvalue(L, 1); | 2531 luaV_register(L, luaV_Blob_mt); |
2507 luaV_openlib(L, luaV_Blob_mt, 1); | 2532 lua_pop(L, 1); |
2508 luaV_newmetatable(L, LUAVIM_FUNCREF); | 2533 luaV_newmetatable(L, LUAVIM_FUNCREF); |
2509 lua_pushvalue(L, 1); | 2534 luaV_register(L, luaV_Funcref_mt); |
2510 luaV_openlib(L, luaV_Funcref_mt, 1); | 2535 lua_pop(L, 1); |
2511 luaV_newmetatable(L, LUAVIM_BUFFER); | 2536 luaV_newmetatable(L, LUAVIM_BUFFER); |
2512 lua_pushvalue(L, 1); // cache table | 2537 luaV_register(L, luaV_Buffer_mt); |
2513 luaV_openlib(L, luaV_Buffer_mt, 1); | 2538 lua_pop(L, 1); |
2514 luaV_newmetatable(L, LUAVIM_WINDOW); | 2539 luaV_newmetatable(L, LUAVIM_WINDOW); |
2515 lua_pushvalue(L, 1); // cache table | 2540 luaV_register(L, luaV_Window_mt); |
2516 luaV_openlib(L, luaV_Window_mt, 1); | 2541 lua_pop(L, 1); |
2517 lua_newtable(L); // vim table | 2542 lua_newtable(L); // vim table |
2518 lua_pushvalue(L, 1); // cache table | 2543 luaV_register(L, luaV_module); |
2519 luaV_openlib(L, luaV_module, 1); | |
2520 luaV_pushversion(L); | 2544 luaV_pushversion(L); |
2521 lua_setfield(L, -2, "lua_version"); | 2545 lua_setfield(L, -2, "lua_version"); |
2522 lua_setglobal(L, LUAVIM_NAME); | 2546 lua_setglobal(L, LUAVIM_NAME); |
2523 // custom code | 2547 // custom code |
2524 (void)luaL_dostring(L, LUA_VIM_FN_CODE); | 2548 (void)luaL_dostring(L, LUA_VIM_FN_CODE); |
2525 (void)luaL_dostring(L, LUA_VIM_UPDATE_PACKAGE_PATHS); | 2549 (void)luaL_dostring(L, LUA_VIM_UPDATE_PACKAGE_PATHS); |
2526 (void)luaL_dostring(L, LUA_VIM_SETUP_VARIABLE_DICTS); | 2550 (void)luaL_dostring(L, LUA_VIM_SETUP_VARIABLE_DICTS); |
2527 | 2551 |
2528 lua_getglobal(L, "vim"); | 2552 lua_getglobal(L, LUAVIM_NAME); |
2529 lua_getfield(L, -1, "_update_package_paths"); | 2553 lua_getfield(L, -1, "_update_package_paths"); |
2530 | 2554 |
2531 if (lua_pcall(L, 0, 0, 0)) | 2555 if (lua_pcall(L, 0, 0, 0)) |
2532 luaV_emsg(L); | 2556 luaV_emsg(L); |
2533 | 2557 |