Anuncios

Bienvenidos sean a este post, continuando con lo visto en nuestro post anterior observamos que nuestra ultima implementancion posee una gran fallo de seguridad, vamos a suponer que el usuario escribe algo como esto:

array.set(io.stdin, 1, false)
Anuncios

El valor en io.stdin es un userdatum con un puntero a un archivo (FILE*), debido a que es un userdatum, array.set lo aceptara amablemente como un argumento valido, el resultado probable sea un error de memoria, con un poco de suerte se puede obtener un indice fuera de rango en su lugar, y tal conducta es inaceptable para cualquier libreria de Lua, no importa como se use una libreria de C esta no deberia generar datos de C corruptos ni producir un volcado de nucleo desde Lua.

Nota: userdatum es la nomenclatura para decir "datos del usuario".
Anuncios

El metodo mas usual para distinguir un tipo de userdatum de otros userdatum es crear una unica metatabla para ese tipo, cada vez que creamos un userdatum lo marcamos con la correspondiente metatabla, y cada vez que conseguimos un userdatum chequeamos si tiene la metatabla correcta porque Lua no puede cambiar la metatabla de un userdatum, no se puede engañar a nuestro codigo.

Anuncios

Tambien necesitamos un lugar para almacenar esta nueva metatabla, asi que podemos accederlo para crear un nuevo userdatum y chequear si un userdatum informado tiene el tipo correcto, como vimos anteriormente hay tres opciones para almacenar metatablas:

  • En el registro
  • En el entorno
  • En un upvalue
Anuncios

Es costumbre en Lua registrar cualquier nuevo tipo de C en el registro usando un nombre de tipo como el indice y la metatabla como el valor, tal como cualquier indice de registro debemos elegir un nombre de tipo con cuidado para evitar conflictos, para nuestro ejemplo usaremos el nombre “LuaBook.array” para su nuevo tipo, como siempre la libreria auxiliar ofrece algunas funciones para ayudarnos, las nuevas funciones son las siguientes:

int  luaL_newmetatable (lua_State *L, const char *tnombre);
void luaL_getmetatable (lua_State *L, const char *tnombre);
void luaL_checkudata   (lua_State *L, int indice, const char *tnombre);
Anuncios

La funcion luaL_newmetatable crea una nueva tabla (para ser usada como una metatabla), deja la nueva tabla en la parte superior de la pila y asocia la tabla al nombre informado en el registro, la funcion luaL_getmetatable recupera la metatabla asociada con tnombre del registro, por ultimo luaL_checkudata chequea si el objeto en la posicion informada de la pila es un userdatum con una metatabla que coincida con el nombre informado, devuelve un error si el objeto no tiene la metatabla correcta o si no es un userdatum, de lo contrario devuelve la direccion del userdatum, con esto podemos comenzar nuestra implementacion, el primer paso es cambiar la funcion que abre la libreria, la nueva version debe crear la metatabla para los arrays:

int luaopen_array(lua_State *L)
{
	luaL_newmetatable(L, "LuaBook.array");
	luaL_register(L, "array", arraylib);
	return 1;
}
Anuncios

Nuestro siguiente paso sera cambiar nuevoarray para que setee esta metatabla en todos los arrays que crea:

static int nuevoarray(lua_State *L)
{
	int i, n;
	size_t nBytes;
	NumArray *a;

	n = luaL_checkint(L, 1);
	luaL_argcheck(L, n >= 1, 1, "tamaño invalido");
	nBytes = sizeof(NumArray) + I_WORD(n - 1)*sizeof(unsigned int);
	a = (NumArray *)lua_newuserdata(L, nBytes);
	
	a->tamano = n;
	for(i = 0; i <= I_WORD(n - 1); i++)
		a->valores[i] = 0;

	luaL_getmetatable(L, "LuaBook.array");
	lua_setmetatable(L, -2);

	return 1;
}
Anuncios

La funcion lua_setmetatable quita una tabla de la pila y la setea como la metatabla del objeto en el indice informado, en nuestro caso este objeto es la nueva userdatum, por ultimo setarray, getarray y getsize tienen que chequear si tiene un array valido como primer argumento, para simplificar sus tareas definimos la siguiente macro:

#define checkarray(L) \ (NumArray *)luaL_checkudata(L, 1, "LuaBook.array")

Usando esta macro la nueva definicion para getsize es mas sencilla:

static int getsize(lua_State *L)
{
	NumArray *a = checkarray(L);
	lua_pushinteger(L, a->tamano);
	return 1;
}
Anuncios

Como setarray y getarray tambien comparten codigo para chequear el indice como su segundo argumento, factorizamos su parte comun como la siguiente funcion:

static int getindice (lua_State *L, unsigned int *mask)
{
	NumArray *a = checkarray(L);
	int indice = luaL_checkint(L, 2) - 1;

	luaL_argcheck(L, 0 <= indice && indice < a->tamano, 2,
					"indice fuera de rango");

	*mask = I_BIT(indice);
	return &a->valores[I_WORD(indice)];
}
Anuncios

Luego de la definicion de getindex, las definiciones de setarray y getarray son mucho mas sencillas:

static int setarray(lua_State *L)
{
	unsigned int mask;
	unsigned int *entrada = getindice(L, &mask);
	luaL_checkany(L, 3);
	if (lua_toboolean(L, 3))
		*entrada |= mask;
	else
		*entrada &= ~mask;
	
	return 0;
}

static int getarray(lua_State *L)
{
	unsigned int mask;
	unsigned int *entrada = getindex(L, &mask);
	lua_pushboolean(L, *entrada & mask);
	return 1;
}
Anuncios

Con estas modificaciones si volvemos a utilizar el ejemplo del principio del post nos devolvera el siguiente mensaje:

array.get(io.stdin, 10)
error: bad argument #1 to 'getarray' ('array' expected)

Si bien el error ocurrira ahora no tendremos un volcado de memoria sino un notificacion de error mas “monona”.

Anuncios

En resumen, hoy hemos visto como son las metatablas para nuevos tipos, la solucion que nos pueden proporcionar, para que se usan, como se deben utilizar, y como nos permitieron optimizar nuestro ejemplo anterior, espero les haya sido util sigueme en tumblr, Twitter o Facebook para recibir una notificacion cada vez que subo un nuevo post en este blog, nos vemos en el proximo post.

Anuncios

Tengo un Patreon donde podes acceder de manera exclusiva a material para este blog antes de ser publicado, sigue los pasos del link para saber como.

Tambien podes donar

Es para mantenimiento del sitio, gracias!

$1.00