PageRenderTime 24ms CodeModel.GetById 1ms app.highlight 19ms RepoModel.GetById 1ms app.codeStats 0ms

/src/interface.c

http://lbcv.googlecode.com/
C | 331 lines | 271 code | 20 blank | 40 comment | 57 complexity | f99bd580c171cfc8b9decb6bb77bb25e MD5 | raw file
  1/* Copyright (c) 2010 Peter Cawley
  2
  3Permission is hereby granted, free of charge, to any person obtaining a copy of
  4this software and associated documentation files (the "Software"), to deal in
  5the Software without restriction, including without limitation the rights to
  6use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
  7of the Software, and to permit persons to whom the Software is furnished to do
  8so, subject to the following conditions:
  9
 10The above copyright notice and this permission notice shall be included in all
 11copies or substantial portions of the Software.
 12
 13THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 14IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 15FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 16AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 17LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 18OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 19SOFTWARE.
 20
 21Based in part on code from lbaselib.c in Lua 5.2, which is:
 22Copyright (C) 1994-2010 Lua.org, PUC-Rio.  All rights reserved.
 23
 24*/
 25
 26#define LUA_LIB
 27#include "decoder.h"
 28#include "verifier.h"
 29#include <lauxlib.h>
 30#include <string.h>
 31
 32static int l_cleanup_decode_state(lua_State* L)
 33{
 34    decoded_prototype_t* proto;
 35    decode_state_t* ds = *(decode_state_t**)lua_touserdata(L, 1);
 36    if(ds)
 37    {
 38        proto = decode_bytecode_finish(ds);
 39        if(proto)
 40        {
 41            void* allocud;
 42            lua_Alloc alloc = lua_getallocf(L, &allocud);
 43            free_prototype(proto, alloc, allocud);
 44        }
 45    }
 46    return 0;
 47}
 48
 49static int decode_fail(lua_State* L, int status)
 50{
 51    lua_pushnil(L);
 52    switch(status)
 53    {
 54    case DECODE_UNSAFE:
 55        lua_pushliteral(L, "verification failed during decode");
 56        break;
 57
 58    case DECODE_ERROR:
 59        lua_pushliteral(L, "unknown decoding error");
 60        break;
 61
 62    case DECODE_ERROR_MEM:
 63        lua_pushliteral(L, "insufficient memory");
 64        break;
 65
 66    default:
 67        lua_pushliteral(L, "unable to load bytecode");
 68        break;
 69    }
 70    return 2;
 71}
 72
 73static int verify_fail(lua_State* L)
 74{
 75    lua_pushnil(L);
 76    lua_pushliteral(L, "verification failed");
 77    return 2;
 78}
 79
 80static int not_string_err(lua_State* L)
 81{
 82    lua_pushliteral(L, "reader function must return a string");
 83    return lua_error(L);
 84}
 85
 86static int l_verify(lua_State* L)
 87{
 88    decoded_prototype_t* proto = NULL;
 89    void* allocud;
 90    lua_Alloc alloc = lua_getallocf(L, &allocud);
 91    decode_state_t* ds = NULL;
 92    decode_state_t** pds = &ds;
 93    int status = DECODE_YIELD;
 94    size_t len;
 95    const char* str;
 96
 97    if(lua_type(L, 1) != LUA_TSTRING)
 98    {
 99        if(lua_getctx(L, NULL) == LUA_OK)
100        {
101            /* If the reader function throws an error, then the decode state
102              should still get cleaned. This is achieved by creating a userdata
103              whose garbage collection metamethod cleans up the decode state.
104              This also allows the decode state to be maintained if the reader
105              function yields. */
106            lua_settop(L, 1);
107            pds = (decode_state_t**)lua_newuserdata(L, sizeof(decode_state_t*));
108            lua_createtable(L, 0, 1);
109            lua_pushcfunction(L, l_cleanup_decode_state);
110            lua_setfield(L, 3, "__gc");
111            lua_setmetatable(L, 2);
112        }
113        else
114        {
115            pds = (decode_state_t**)lua_touserdata(L, 2);
116            ds = *pds;
117            goto resume_continuation;
118        }
119    }
120
121    ds = decode_bytecode_init(alloc, allocud);
122    *pds = ds;
123    if(lua_type(L, 1) == LUA_TSTRING)
124    {
125        str = lua_tolstring(L, 1, &len);
126        status = decode_bytecode_pump(ds, (const unsigned char*)str, len);
127    }
128    else
129    {
130        luaL_checktype(L, 1, LUA_TFUNCTION);
131        while(status == DECODE_YIELD)
132        {
133            lua_settop(L, 2);
134            lua_pushvalue(L, 1);
135            lua_callk(L, 0, 1, 1, l_verify);
136resume_continuation:
137            str = lua_tolstring(L, 3, &len);
138            if(str == NULL || len == 0)
139            {
140                if(str == NULL && lua_type(L, 3) != LUA_TNIL)
141                    return not_string_err(L);
142                break;
143            }
144            status = decode_bytecode_pump(ds, (const unsigned char*)str, len);
145        }
146    }
147    proto = decode_bytecode_finish(ds);
148    *pds = NULL;
149    if(proto == NULL)
150    {
151        return decode_fail(L, status);
152    }
153    else
154    {
155        bool good = verify(proto, alloc, allocud);
156        free_prototype(proto, alloc, allocud);
157        if(good)
158        {
159            lua_pushboolean(L, 1);
160            return 1;
161        }
162        else
163        {
164            lua_pushnil(L);
165            lua_pushliteral(L, "verification failed");
166            return 2;
167        }
168    }
169}
170
171static const char *checkrights(lua_State *L, const char *mode, const char *s)
172{
173    if(strchr(mode, 'b') == NULL && *s == LUA_SIGNATURE[0])
174        return lua_pushliteral(L, "attempt to load a binary chunk");
175    if(strchr(mode, 't') == NULL && *s != LUA_SIGNATURE[0])
176        return lua_pushliteral(L, "attempt to load a text chunk");
177    return NULL;  /* chunk in allowed format */
178}
179
180#define RESERVEDSLOT 5
181
182/*
183** Reader for generic `load' function: `lua_load' uses the
184** stack for internal stuff, so the reader cannot change the
185** stack top. Instead, it keeps its resulting string in a
186** reserved slot inside the stack.
187*/
188typedef struct {  /* reader state */
189  int f;  /* position of reader function on stack */
190  const char *mode;  /* allowed modes (binary/text) */
191  decode_state_t *ds; /* for binary chunks, the decode state */
192  int decode_status; /* for binary chunks, result of decode_bytecode_pump */
193} Readstat;
194
195static const char *generic_reader(lua_State *L, void *ud, size_t *size)
196{
197    const char *s;
198    size_t len;
199    Readstat *stat = (Readstat *)ud;
200    luaL_checkstack(L, 2, "too many nested functions");
201    lua_pushvalue(L, stat->f);  /* get function */
202    lua_call(L, 0, 1);  /* call it */
203    if(lua_isnil(L, -1))
204    {
205        *size = 0;
206        return NULL;
207    }
208    else if((s = lua_tolstring(L, -1, &len)) != NULL)
209    {
210        if(stat->mode != NULL)  /* first time? */
211        {
212            if(checkrights(L, stat->mode, s))  /* check mode */
213                lua_error(L);
214            stat->mode = NULL;  /* to avoid further checks */
215            if(s[0] == LUA_SIGNATURE[0])
216            {
217                void* allocud;
218                lua_Alloc alloc = lua_getallocf(L, &allocud);
219                stat->ds = decode_bytecode_init(alloc, allocud);
220            }
221        }
222        if(stat->ds)
223        {
224            int status = decode_bytecode_pump(stat->ds, (const unsigned char*)s, len);
225            if(status != DECODE_YIELD)
226            {
227                lua_pop(L, 1);
228                decode_fail(L, status);
229                lua_error(L);
230            }
231        }
232        lua_replace(L, RESERVEDSLOT);  /* save string in reserved slot */
233        return lua_tolstring(L, RESERVEDSLOT, size);
234    }
235    else
236    {
237        not_string_err(L);
238        return NULL;  /* to avoid warnings */
239    }
240}
241
242static int check_ds(lua_State *L, Readstat* stat)
243{
244    bool verified;
245    void* allocud;
246    lua_Alloc alloc = lua_getallocf(L, &allocud);
247    decoded_prototype_t* proto = decode_bytecode_finish(stat->ds);
248    if(proto == NULL)
249        return decode_fail(L, stat->decode_status);
250    verified = verify(proto, alloc, allocud);
251    free_prototype(proto, alloc, allocud);
252    if(!verified)
253        return verify_fail(L);
254    return 0;
255}
256
257static int l_load(lua_State *L)
258{
259    Readstat stat;
260    size_t len;
261    const char *str;
262    void* allocud;
263    lua_Alloc alloc = lua_getallocf(L, &allocud);
264    int status;
265    int top = lua_gettop(L);
266    stat.f = 1;
267    stat.mode = luaL_optstring(L, 3, "bt");
268    stat.ds = NULL;
269    stat.decode_status = DECODE_YIELD;
270    str = lua_tolstring(L, stat.f, &len);
271
272    if(str == NULL)
273    {
274        /* Load from a reader function. */
275        const char *chunkname = luaL_optstring(L, stat.f + 1, "=(load)");
276        luaL_checktype(L, stat.f, LUA_TFUNCTION);
277        lua_settop(L, RESERVEDSLOT);
278        status = lua_load(L, generic_reader, (void*)&stat, chunkname);
279        if(stat.ds)
280        {
281            if(check_ds(L, &stat) && status == LUA_OK)
282                return 2;
283        }
284    }
285    else
286    {
287        /* Load from a single string. */
288        const char *chunkname = luaL_optstring(L, stat.f + 1, str);
289        /* First check that mode is respected. */
290        if(checkrights(L, stat.mode, str))
291        {
292            lua_pushnil(L);
293            lua_insert(L, -2);
294            return 2;
295        }
296        /* If it is bytecode, verify the bytecode before loading it. */
297        if(str[0] == LUA_SIGNATURE[0])
298        {
299            stat.ds = decode_bytecode_init(alloc, allocud);
300            stat.decode_status = decode_bytecode_pump(stat.ds, (const unsigned char*)str, len);
301            if(check_ds(L, &stat))
302                return 2;
303        }
304        /* Do the actual loading. */
305        status = luaL_loadbuffer(L, str, len, chunkname);
306    }
307    if (status == LUA_OK) {
308        if (top >= 4) {  /* is there an 'env' argument */
309            lua_pushvalue(L, 4);  /* environment for loaded function */
310            lua_setupvalue(L, -2, 1);  /* set it as 1st upvalue */
311        }
312        return 1;
313    } else {
314        lua_pushnil(L);
315        lua_insert(L, -2);  /* put before error message */
316        return 2;  /* return nil plus error message */
317    }
318}
319
320const luaL_Reg lib[] = {
321    {"verify", l_verify},
322    {"load", l_load},
323    {NULL, NULL}
324};
325
326LUAMOD_API int luaopen_lbcv(lua_State* L)
327{
328    lua_createtable(L, 0, sizeof(lib)/sizeof(*lib));
329    luaL_setfuncs(L, lib, 0);
330    return 1;
331}