Last active
July 31, 2020 15:20
-
-
Save stevenjohnstone/2236f632bb58697311cd01ea1cafbbc6 to your computer and use it in GitHub Desktop.
A Lua AFL integration using the debug hook functionality which fires as Lua traverses lines
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Using the approach of afl-python to make a | |
// Lua fuzzer. | |
// Build with "gcc -I/usr/include/lua5.3/ -L/usr/local/lib -llua5.3 -rdynamic afl-fuzz.c" | |
// (or whatever works on your platform). | |
// | |
// Write a script which has a global function "fuzz" which reads all of stdin and processes it | |
// to exercise some code in which you'd like to find logic bugs. | |
#include <assert.h> | |
#include <fcntl.h> | |
#include <lauxlib.h> | |
#include <lua.h> | |
#include <lualib.h> | |
#include <stdio.h> | |
#include <stdlib.h> | |
#include <string.h> | |
#include <sys/types.h> | |
#include <sys/shm.h> | |
#include <sys/wait.h> | |
#include <unistd.h> | |
// The presence of this string is enough to allow | |
// AFL fuzz to run without using the env variable | |
// AFL_SKIP_BIN_CHECK | |
const char *SHM_ENV = "__AFL_SHM_ID"; | |
const char *NOFORK = "AFL_NO_FORKSRV"; | |
const int afl_read_fd = 198; | |
const int afl_write_fd = afl_read_fd + 1; | |
static unsigned char *afl_shm; | |
static size_t afl_shm_size = 1 << 16; | |
static int shm_init() { | |
const char *shm = getenv(SHM_ENV); | |
assert(shm); | |
afl_shm = shmat(atoi(shm), NULL, 0); | |
assert(afl_shm > 0); | |
return 0; | |
} | |
static int fork_write(int pid) { | |
const int ok = (4 == write(afl_write_fd, &pid, 4)); | |
assert(ok); | |
return 0; | |
} | |
static int fork_read() { | |
int tmp; | |
const int ok = (4 == read(afl_read_fd, &tmp, 4)); | |
assert(ok); | |
return 0; | |
} | |
static int fork_close() { | |
close(afl_read_fd); | |
close(afl_write_fd); | |
return 0; | |
} | |
static int run_target(lua_State *L) { | |
if(lua_pcall(L, 0, 0, 0)) { | |
abort(); | |
} | |
return 0; | |
} | |
/** | |
* From afl-python | |
* https://github.com/jwilk/python-afl/blob/8df6bfefac5de78761254bf5d7724e0a52d254f5/afl.pyx#L74-L87 | |
*/ | |
#define LHASH_INIT 0x811C9DC5 | |
#define LHASH_MAGIC_MULT 0x01000193 | |
#define LHASH_NEXT(x) h = ((h ^ (unsigned char)(x)) * LHASH_MAGIC_MULT) | |
static inline unsigned int lhash(const char *key, size_t offset) { | |
const char *const last = &key[strlen(key) - 1]; | |
uint32_t h = LHASH_INIT; | |
while (key <= last) LHASH_NEXT(*key++); | |
for (; offset != 0; offset >>= 8) LHASH_NEXT(offset); | |
return h; | |
} | |
static unsigned int current_location; | |
static void hook(lua_State *L, lua_Debug *ar) { | |
lua_getinfo(L, "Sl", ar); | |
if (ar && ar->source && ar->currentline) { | |
const unsigned int new_location = lhash(ar->source, ar->currentline) % afl_shm_size; | |
afl_shm[current_location ^ new_location] += 1; | |
current_location = new_location / 2; | |
} | |
} | |
int main(int argc, const char **argv) { | |
shm_init(); | |
assert(argc > 1); | |
const char *fuzz_target = argv[1]; | |
lua_State *L = luaL_newstate(); | |
assert(L); | |
luaL_openlibs(L); | |
lua_sethook(L, hook, LUA_MASKLINE, 0); | |
int rc = luaL_dofile(L, fuzz_target); | |
assert(rc == 0); | |
lua_getglobal(L, "fuzz"); | |
if (!lua_isfunction(L, -1)) { | |
assert(0); | |
} | |
if (getenv(NOFORK)) { | |
run_target(L); | |
return 0; | |
} | |
fork_write(0); // let AFL know we're here | |
while (1) { | |
fork_read(); // discard report from AFL | |
pid_t child = fork(); | |
if (child == 0) { | |
fork_close(); | |
run_target(L); | |
return 0; | |
} | |
fork_write(child); | |
int status = 0; | |
rc = wait(&status); | |
fork_write(status); | |
} | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment