From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from localhost (localhost [127.0.0.1]) by turing.freelists.org (Avenir Technologies Mail Multiplex) with ESMTP id 34A2529446 for ; Thu, 28 Mar 2019 13:54:14 -0400 (EDT) Received: from turing.freelists.org ([127.0.0.1]) by localhost (turing.freelists.org [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id LywAFDzCucAp for ; Thu, 28 Mar 2019 13:54:14 -0400 (EDT) Received: from smtpng1.m.smailru.net (smtpng1.m.smailru.net [94.100.181.251]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by turing.freelists.org (Avenir Technologies Mail Multiplex) with ESMTPS id B54DF255A8 for ; Thu, 28 Mar 2019 13:54:13 -0400 (EDT) Date: Thu, 28 Mar 2019 20:54:10 +0300 From: Mergen Imeev Subject: [tarantool-patches] Re: [PATCH v9 4/7] lua: remove exceptions from function luaL_tofield() Message-ID: <20190328175410.GC4864@tarantool.org> References: MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Disposition: inline In-Reply-To: Sender: tarantool-patches-bounce@freelists.org Errors-to: tarantool-patches-bounce@freelists.org Reply-To: tarantool-patches@freelists.org List-Help: List-Unsubscribe: List-software: Ecartis version 1.0.0 List-Id: tarantool-patches List-Subscribe: List-Owner: List-post: List-Archive: To: Vladislav Shpilevoy Cc: tarantool-patches@freelists.org Hi! Thank you for review. I created a new function, but it turned out to be quite complex. Sorry, I have not found a simpler solution. My answer, diff and new patch below. On Wed, Mar 27, 2019 at 12:48:32AM +0300, Vladislav Shpilevoy wrote: > Thanks for the patch! > > >> 2. Looking at lua_field_inspect_table I found that if a table has __serialize > >> metamethod, it is called without a protection (utils.c:409). __serialize is an > >> arbitrary unprotected user code, that can throw an error deliberately. What are > >> we gonna do with that? Personally I've already faced with some user code, throwing > >> an error from __serialize, deliberately. On not critical errors not meaning panic. > >> > >> As a solution we could 1) do not care, again; 2) finally accept the fact that > >> wrap into a pcall was not so bad and use it; 3) use lua_pcall in that > >> particular place. Please, consult Kostja, what does he choose. > > > > I checked it this way: > > > > diff --git a/src/lua/utils.c b/src/lua/utils.c > > index bbfc549..eae3b5f 100644 > > --- a/src/lua/utils.c > > +++ b/src/lua/utils.c > > @@ -398,6 +398,20 @@ lua_field_inspect_ucdata(struct lua_State *L, struct luaL_serializer *cfg, > > lua_settop(L, top); /* remove temporary objects */ > > } > > > > +/** > > + * Check that the field LUAL_SERIALIZE of the metatable is > > + * available. > > + * > > + * @param L Lua State. > > + */ > > +static int > > +get_metafield_serialize(struct lua_State *L) > > +{ > > + int idx = *(int *)lua_topointer(L, -1); > > + luaL_getmetafield(L, idx, LUAL_SERIALIZE); > > + return 0; > > +} > > + > > static int > > lua_field_inspect_table(struct lua_State *L, struct luaL_serializer *cfg, > > int idx, struct luaL_field *field) > > @@ -408,6 +422,9 @@ lua_field_inspect_table(struct lua_State *L, struct luaL_serializer *cfg, > > uint32_t max = 0; > > > > /* Try to get field LUAL_SERIALIZER_TYPE from metatable */ > > + if (!cfg->encode_load_metatables && > > + luaT_cpcall(L, get_metafield_serialize, &idx) != 0) > > + return -1; > > if (!cfg->encode_load_metatables || > > !luaL_getmetafield(L, idx, LUAL_SERIALIZE)) > > goto skip; > > > > Unfortunately, it was not the place I told about. You > wrapped just a fetch of __serialize function, but not its > call. It is still unsafe on line 435. In other words it was: > > 1 func = obj.__serialize > 2 func(obj) > > You made it: > > 1 func = pcall(function() obj.__serialize end) > 2 func(obj) > > The second line is still unsafe. And much more unsafe than > the first one. > > But it is a good catch too, because a user could define > such an __index for his metatable, that it throws an error. > You should wrap both __serialize fetch and call into a single > lua_cpcall. Fixed. Diff: diff --git a/src/lua/utils.c b/src/lua/utils.c index eae3b5f..6150e84 100644 --- a/src/lua/utils.c +++ b/src/lua/utils.c @@ -400,72 +400,110 @@ lua_field_inspect_ucdata(struct lua_State *L, struct luaL_serializer *cfg, /** * Check that the field LUAL_SERIALIZE of the metatable is - * available. + * available. If so, create field using it. + * + * This function returns two values on stack. On top of the stack + * is boolean that shows if error happened. On the next position + * can be boolean or result of execution of function from the + * metatable. If it is boolean, that if its value is false than + * there were no metatables.It its value is true than there was + * string in that field of metatable. * * @param L Lua State. */ static int get_metafield_serialize(struct lua_State *L) { - int idx = *(int *)lua_topointer(L, -1); - luaL_getmetafield(L, idx, LUAL_SERIALIZE); - return 0; -} - -static int -lua_field_inspect_table(struct lua_State *L, struct luaL_serializer *cfg, - int idx, struct luaL_field *field) -{ - assert(lua_type(L, idx) == LUA_TTABLE); - const char *type; - uint32_t size = 0; - uint32_t max = 0; - - /* Try to get field LUAL_SERIALIZER_TYPE from metatable */ - if (!cfg->encode_load_metatables && - luaT_cpcall(L, get_metafield_serialize, &idx) != 0) - return -1; - if (!cfg->encode_load_metatables || - !luaL_getmetafield(L, idx, LUAL_SERIALIZE)) - goto skip; + if (luaL_getmetafield(L, 1, LUAL_SERIALIZE) == 0) { + lua_pushboolean(L, false); + lua_pushboolean(L, false); + return 2; + } + bool is_error = false; + struct luaL_serializer *cfg = + (struct luaL_serializer *)lua_touserdata(L, 2); + struct luaL_field *field = + (struct luaL_field *)lua_touserdata(L, 3); if (lua_isfunction(L, -1)) { /* copy object itself */ - lua_pushvalue(L, idx); + lua_pushvalue(L, 1); lua_call(L, 1, 1); - /* replace obj with the unpacked value */ - lua_replace(L, idx); - return luaL_tofield(L, cfg, idx, field); - } else if (!lua_isstring(L, -1)) { + is_error = (luaL_tofield(L, cfg, -1, field) != 0); + lua_pushboolean(L, is_error); + return 2; + } + + if (!lua_isstring(L, -1)) { diag_set(LuajitError, "invalid " LUAL_SERIALIZE " value"); - return -1; + lua_pushboolean(L, true); + return 2; } - type = lua_tostring(L, -1); + const char *type = lua_tostring(L, -1); if (strcmp(type, "array") == 0 || strcmp(type, "seq") == 0 || strcmp(type, "sequence") == 0) { field->type = MP_ARRAY; /* Override type */ - field->size = luaL_arrlen(L, idx); + field->size = luaL_arrlen(L, 1); /* YAML: use flow mode if __serialize == 'seq' */ if (cfg->has_compact && type[3] == '\0') field->compact = true; lua_pop(L, 1); /* type */ - - return 0; + lua_pushboolean(L, true); + lua_pushboolean(L, false); + return 2; } else if (strcmp(type, "map") == 0 || strcmp(type, "mapping") == 0) { field->type = MP_MAP; /* Override type */ - field->size = luaL_maplen(L, idx); + field->size = luaL_maplen(L, 1); /* YAML: use flow mode if __serialize == 'map' */ if (cfg->has_compact && type[3] == '\0') field->compact = true; lua_pop(L, 1); /* type */ - return 0; + lua_pushboolean(L, true); + lua_pushboolean(L, false); + return 2; } else { diag_set(LuajitError, "invalid " LUAL_SERIALIZE " value"); - return -1; + lua_pushboolean(L, true); + return 2; + } +} + +static int +lua_field_inspect_table(struct lua_State *L, struct luaL_serializer *cfg, + int idx, struct luaL_field *field) +{ + assert(lua_type(L, idx) == LUA_TTABLE); + uint32_t size = 0; + uint32_t max = 0; + + if (cfg->encode_load_metatables) { + lua_pushcfunction(L, get_metafield_serialize); + lua_pushvalue(L, idx); + lua_pushlightuserdata(L, cfg); + lua_pushlightuserdata(L, field); + if (lua_pcall(L, 3, 2, 0) != 0) { + diag_set(LuajitError, lua_tostring(L, -1)); + return -1; + } + bool is_error = lua_toboolean(L, -1); + bool is_bool = lua_isboolean(L, -2); + if (is_error) { + lua_pop(L, 2); + return -1; + } + if (is_bool) { + bool is_metatable_available = lua_toboolean(L, -2); + lua_pop(L, 2); + if (is_metatable_available) + return 0; + } else { + lua_pop(L, 1); + lua_replace(L, idx); + return 0; + } } -skip: field->type = MP_ARRAY; /* Calculate size and check that table can represent an array */ New patch: commit 0c9ec5f6d4c935c3811600ca166727afd5005e7a Author: Mergen Imeev Date: Fri Jan 11 21:16:12 2019 +0300 lua: remove exceptions from function luaL_tofield() Before this commit, the luaL_tofield() function threw a Lua exception when an error occurred. This behavior has been changed in this patch. Now it sets diag_set() and returns -1 in case of an error. Needed for #3505 diff --git a/src/box/lua/call.c b/src/box/lua/call.c index 52939ae..2049ee5 100644 --- a/src/box/lua/call.c +++ b/src/box/lua/call.c @@ -164,7 +164,8 @@ luamp_encode_call_16(lua_State *L, struct luaL_serializer *cfg, */ for (int i = 1; i <= nrets; ++i) { struct luaL_field field; - luaL_tofield(L, cfg, i, &field); + if (luaL_tofield(L, cfg, i, &field) < 0) + return luaT_error(L); struct tuple *tuple; if (field.type == MP_EXT && (tuple = luaT_istuple(L, i)) != NULL) { @@ -192,7 +193,8 @@ luamp_encode_call_16(lua_State *L, struct luaL_serializer *cfg, * Inspect the first result */ struct luaL_field root; - luaL_tofield(L, cfg, 1, &root); + if (luaL_tofield(L, cfg, 1, &root) < 0) + return luaT_error(L); struct tuple *tuple; if (root.type == MP_EXT && (tuple = luaT_istuple(L, 1)) != NULL) { /* `return box.tuple()` */ @@ -221,7 +223,8 @@ luamp_encode_call_16(lua_State *L, struct luaL_serializer *cfg, for (uint32_t t = 1; t <= root.size; t++) { lua_rawgeti(L, 1, t); struct luaL_field field; - luaL_tofield(L, cfg, -1, &field); + if (luaL_tofield(L, cfg, -1, &field) < 0) + return luaT_error(L); if (field.type == MP_EXT && (tuple = luaT_istuple(L, -1))) { tuple_to_mpstream(tuple, stream); } else if (field.type != MP_ARRAY) { diff --git a/src/box/lua/tuple.c b/src/box/lua/tuple.c index 60c1a39..1903ee8 100644 --- a/src/box/lua/tuple.c +++ b/src/box/lua/tuple.c @@ -229,7 +229,8 @@ luamp_convert_key(struct lua_State *L, struct luaL_serializer *cfg, return tuple_to_mpstream(tuple, stream); struct luaL_field field; - luaL_tofield(L, cfg, index, &field); + if (luaL_tofield(L, cfg, index, &field) < 0) + luaT_error(L); if (field.type == MP_ARRAY) { lua_pushvalue(L, index); luamp_encode_r(L, cfg, stream, &field, 0); diff --git a/src/lua/msgpack.c b/src/lua/msgpack.c index b470060..1b1874e 100644 --- a/src/lua/msgpack.c +++ b/src/lua/msgpack.c @@ -149,10 +149,12 @@ restart: /* used by MP_EXT */ lua_pushnil(L); /* first key */ while (lua_next(L, top) != 0) { lua_pushvalue(L, -2); /* push a copy of key to top */ - luaL_tofield(L, cfg, lua_gettop(L), field); + if (luaL_tofield(L, cfg, lua_gettop(L), field) < 0) + return luaT_error(L); luamp_encode_r(L, cfg, stream, field, level + 1); lua_pop(L, 1); /* pop a copy of key */ - luaL_tofield(L, cfg, lua_gettop(L), field); + if (luaL_tofield(L, cfg, lua_gettop(L), field) < 0) + return luaT_error(L); luamp_encode_r(L, cfg, stream, field, level + 1); lua_pop(L, 1); /* pop value */ } @@ -168,7 +170,8 @@ restart: /* used by MP_EXT */ mpstream_encode_array(stream, size); for (uint32_t i = 0; i < size; i++) { lua_rawgeti(L, top, i + 1); - luaL_tofield(L, cfg, top + 1, field); + if (luaL_tofield(L, cfg, top + 1, field) < 0) + return luaT_error(L); luamp_encode_r(L, cfg, stream, field, level + 1); lua_pop(L, 1); } @@ -203,7 +206,8 @@ luamp_encode(struct lua_State *L, struct luaL_serializer *cfg, } struct luaL_field field; - luaL_tofield(L, cfg, lua_gettop(L), &field); + if (luaL_tofield(L, cfg, lua_gettop(L), &field) < 0) + return luaT_error(L); enum mp_type top_type = luamp_encode_r(L, cfg, stream, &field, 0); if (!on_top) { diff --git a/src/lua/utils.c b/src/lua/utils.c index a418b95..6150e84 100644 --- a/src/lua/utils.c +++ b/src/lua/utils.c @@ -392,61 +392,118 @@ lua_field_inspect_ucdata(struct lua_State *L, struct luaL_serializer *cfg, lua_pcall(L, 1, 1, 0); /* replace obj with the unpacked value */ lua_replace(L, idx); - luaL_tofield(L, cfg, idx, field); + if (luaL_tofield(L, cfg, idx, field) < 0) + luaT_error(L); } /* else ignore lua_gettable exceptions */ lua_settop(L, top); /* remove temporary objects */ } -static void -lua_field_inspect_table(struct lua_State *L, struct luaL_serializer *cfg, - int idx, struct luaL_field *field) +/** + * Check that the field LUAL_SERIALIZE of the metatable is + * available. If so, create field using it. + * + * This function returns two values on stack. On top of the stack + * is boolean that shows if error happened. On the next position + * can be boolean or result of execution of function from the + * metatable. If it is boolean, that if its value is false than + * there were no metatables.It its value is true than there was + * string in that field of metatable. + * + * @param L Lua State. + */ +static int +get_metafield_serialize(struct lua_State *L) { - assert(lua_type(L, idx) == LUA_TTABLE); - const char *type; - uint32_t size = 0; - uint32_t max = 0; - - /* Try to get field LUAL_SERIALIZER_TYPE from metatable */ - if (!cfg->encode_load_metatables || - !luaL_getmetafield(L, idx, LUAL_SERIALIZE)) - goto skip; + if (luaL_getmetafield(L, 1, LUAL_SERIALIZE) == 0) { + lua_pushboolean(L, false); + lua_pushboolean(L, false); + return 2; + } + bool is_error = false; + struct luaL_serializer *cfg = + (struct luaL_serializer *)lua_touserdata(L, 2); + struct luaL_field *field = + (struct luaL_field *)lua_touserdata(L, 3); if (lua_isfunction(L, -1)) { /* copy object itself */ - lua_pushvalue(L, idx); + lua_pushvalue(L, 1); lua_call(L, 1, 1); - /* replace obj with the unpacked value */ - lua_replace(L, idx); - luaL_tofield(L, cfg, idx, field); - return; - } else if (!lua_isstring(L, -1)) { - luaL_error(L, "invalid " LUAL_SERIALIZE " value"); + is_error = (luaL_tofield(L, cfg, -1, field) != 0); + lua_pushboolean(L, is_error); + return 2; + } + + if (!lua_isstring(L, -1)) { + diag_set(LuajitError, "invalid " LUAL_SERIALIZE " value"); + lua_pushboolean(L, true); + return 2; } - type = lua_tostring(L, -1); + const char *type = lua_tostring(L, -1); if (strcmp(type, "array") == 0 || strcmp(type, "seq") == 0 || strcmp(type, "sequence") == 0) { field->type = MP_ARRAY; /* Override type */ - field->size = luaL_arrlen(L, idx); + field->size = luaL_arrlen(L, 1); /* YAML: use flow mode if __serialize == 'seq' */ if (cfg->has_compact && type[3] == '\0') field->compact = true; lua_pop(L, 1); /* type */ - - return; + lua_pushboolean(L, true); + lua_pushboolean(L, false); + return 2; } else if (strcmp(type, "map") == 0 || strcmp(type, "mapping") == 0) { field->type = MP_MAP; /* Override type */ - field->size = luaL_maplen(L, idx); + field->size = luaL_maplen(L, 1); /* YAML: use flow mode if __serialize == 'map' */ if (cfg->has_compact && type[3] == '\0') field->compact = true; lua_pop(L, 1); /* type */ - return; + lua_pushboolean(L, true); + lua_pushboolean(L, false); + return 2; } else { - luaL_error(L, "invalid " LUAL_SERIALIZE " value"); + diag_set(LuajitError, "invalid " LUAL_SERIALIZE " value"); + lua_pushboolean(L, true); + return 2; + } +} + +static int +lua_field_inspect_table(struct lua_State *L, struct luaL_serializer *cfg, + int idx, struct luaL_field *field) +{ + assert(lua_type(L, idx) == LUA_TTABLE); + uint32_t size = 0; + uint32_t max = 0; + + if (cfg->encode_load_metatables) { + lua_pushcfunction(L, get_metafield_serialize); + lua_pushvalue(L, idx); + lua_pushlightuserdata(L, cfg); + lua_pushlightuserdata(L, field); + if (lua_pcall(L, 3, 2, 0) != 0) { + diag_set(LuajitError, lua_tostring(L, -1)); + return -1; + } + bool is_error = lua_toboolean(L, -1); + bool is_bool = lua_isboolean(L, -2); + if (is_error) { + lua_pop(L, 2); + return -1; + } + if (is_bool) { + bool is_metatable_available = lua_toboolean(L, -2); + lua_pop(L, 2); + if (is_metatable_available) + return 0; + } else { + lua_pop(L, 1); + lua_replace(L, idx); + return 0; + } } -skip: field->type = MP_ARRAY; /* Calculate size and check that table can represent an array */ @@ -465,7 +522,7 @@ skip: } field->type = MP_MAP; field->size = size; - return; + return 0; } if (k > max) max = k; @@ -475,15 +532,18 @@ skip: if (cfg->encode_sparse_ratio > 0 && max > size * (uint32_t)cfg->encode_sparse_ratio && max > (uint32_t)cfg->encode_sparse_safe) { - if (!cfg->encode_sparse_convert) - luaL_error(L, "excessively sparse array"); + if (!cfg->encode_sparse_convert) { + diag_set(LuajitError, "excessively sparse array"); + return -1; + } field->type = MP_MAP; field->size = size; - return; + return 0; } assert(field->type == MP_ARRAY); field->size = max; + return 0; } static void @@ -496,12 +556,13 @@ lua_field_tostring(struct lua_State *L, struct luaL_serializer *cfg, int idx, lua_call(L, 1, 1); lua_replace(L, idx); lua_settop(L, top); - luaL_tofield(L, cfg, idx, field); + if (luaL_tofield(L, cfg, idx, field) < 0) + luaT_error(L); } -void +int luaL_tofield(struct lua_State *L, struct luaL_serializer *cfg, int index, - struct luaL_field *field) + struct luaL_field *field) { if (index < 0) index = lua_gettop(L) + index + 1; @@ -510,10 +571,12 @@ luaL_tofield(struct lua_State *L, struct luaL_serializer *cfg, int index, double intpart; size_t size; -#define CHECK_NUMBER(x) ({\ - if (!isfinite(x) && !cfg->encode_invalid_numbers) { \ - if (!cfg->encode_invalid_as_nil) \ - luaL_error(L, "number must not be NaN or Inf"); \ +#define CHECK_NUMBER(x) ({ \ + if (!isfinite(x) && !cfg->encode_invalid_numbers) { \ + if (!cfg->encode_invalid_as_nil) { \ + diag_set(LuajitError, "number must not be NaN or Inf"); \ + return -1; \ + } \ field->type = MP_NIL; \ }}) @@ -534,94 +597,93 @@ luaL_tofield(struct lua_State *L, struct luaL_serializer *cfg, int index, field->dval = num; CHECK_NUMBER(num); } - return; + return 0; case LUA_TCDATA: { - uint32_t ctypeid = 0; - void *cdata = luaL_checkcdata(L, index, &ctypeid); + GCcdata *cd = cdataV(L->base + index - 1); + void *cdata = (void *)cdataptr(cd); + int64_t ival; - switch (ctypeid) { + switch (cd->ctypeid) { case CTID_BOOL: field->type = MP_BOOL; field->bval = *(bool*) cdata; - return; + return 0; case CTID_CCHAR: case CTID_INT8: ival = *(int8_t *) cdata; field->type = (ival >= 0) ? MP_UINT : MP_INT; field->ival = ival; - return; + return 0; case CTID_INT16: ival = *(int16_t *) cdata; field->type = (ival >= 0) ? MP_UINT : MP_INT; field->ival = ival; - return; + return 0; case CTID_INT32: ival = *(int32_t *) cdata; field->type = (ival >= 0) ? MP_UINT : MP_INT; field->ival = ival; - return; + return 0; case CTID_INT64: ival = *(int64_t *) cdata; field->type = (ival >= 0) ? MP_UINT : MP_INT; field->ival = ival; - return; + return 0; case CTID_UINT8: field->type = MP_UINT; field->ival = *(uint8_t *) cdata; - return; + return 0; case CTID_UINT16: field->type = MP_UINT; field->ival = *(uint16_t *) cdata; - return; + return 0; case CTID_UINT32: field->type = MP_UINT; field->ival = *(uint32_t *) cdata; - return; + return 0; case CTID_UINT64: field->type = MP_UINT; field->ival = *(uint64_t *) cdata; - return; + return 0; case CTID_FLOAT: field->type = MP_FLOAT; field->fval = *(float *) cdata; CHECK_NUMBER(field->fval); - return; + return 0; case CTID_DOUBLE: field->type = MP_DOUBLE; field->dval = *(double *) cdata; CHECK_NUMBER(field->dval); - return; + return 0; case CTID_P_CVOID: case CTID_P_VOID: if (*(void **) cdata == NULL) { field->type = MP_NIL; - return; + return 0; } /* Fall through */ default: field->type = MP_EXT; - return; } - return; + return 0; } case LUA_TBOOLEAN: field->type = MP_BOOL; field->bval = lua_toboolean(L, index); - return; + return 0; case LUA_TNIL: field->type = MP_NIL; - return; + return 0; case LUA_TSTRING: field->sval.data = lua_tolstring(L, index, &size); field->sval.len = (uint32_t) size; field->type = MP_STR; - return; + return 0; case LUA_TTABLE: { field->compact = false; - lua_field_inspect_table(L, cfg, index, field); - return; + return lua_field_inspect_table(L, cfg, index, field); } case LUA_TLIGHTUSERDATA: case LUA_TUSERDATA: @@ -629,14 +691,14 @@ luaL_tofield(struct lua_State *L, struct luaL_serializer *cfg, int index, field->sval.len = 0; if (lua_touserdata(L, index) == NULL) { field->type = MP_NIL; - return; + return 0; } /* Fall through */ default: field->type = MP_EXT; - return; } #undef CHECK_NUMBER + return 0; } void diff --git a/src/lua/utils.h b/src/lua/utils.h index 96311b7..df4d50e 100644 --- a/src/lua/utils.h +++ b/src/lua/utils.h @@ -311,8 +311,11 @@ struct luaL_field { * @param cfg configuration * @param index stack index * @param field conversion result + * + * @retval 0 Success. + * @retval -1 Error. */ -void +int luaL_tofield(struct lua_State *L, struct luaL_serializer *cfg, int index, struct luaL_field *field); @@ -352,7 +355,8 @@ static inline void luaL_checkfield(struct lua_State *L, struct luaL_serializer *cfg, int idx, struct luaL_field *field) { - luaL_tofield(L, cfg, idx, field); + if (luaL_tofield(L, cfg, idx, field) < 0) + luaT_error(L); if (field->type != MP_EXT) return; luaL_convertfield(L, cfg, idx, field);