Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Threads support #4

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 8 additions & 53 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,63 +1,18 @@
# poc-driver - Linux kernel driver for lunatik

## Compiling
## Compiling and running
To build module you need to:
1. Clone sources for
Driver
https://github.com/luainkernel/poc-driver
Lua (kernel port)
https://github.com/luainkernel/lunatik
1. Clone sources
git clone --recursive https://github.com/luainkernel/poc-driver
2. Assume that you have kernel tree sources in /usr/src/linux
3. create symlinks to lunatik and poc-driver in /usr/src/linux/drivers with corresponding names
3. Then compile:
```
ln -s /where_you_put_lunatik_src /usr/src/linux/drivers/lunatik
ln -s /where_you_put_poc-driver_src /usr/src/linux/drivers/poc-driver
make
```
4. edit drivers/Kconfig to add following:
4. Run (as root):
```
source drivers/lunatik/Kconfig
```
5. lunatik/Kconfig contents:
```
config LUNATIK
tristate "Lunatik"

config LUNATIK_POC
bool "Use poc driver"
depends on LUNATIK
default y
```
6. edit drivers/Makefile to add:
```
obj-$(CONFIG_LUNATIK) += lunatik/
```
7. lunatik/Makefile example code:
```
EXTRA_CFLAGS += -D_KERNEL
# for poc-driver:
EXTRA_CFLAGS += -I$(src)

obj-$(CONFIG_LUNATIK) += lunatik.o

lunatik-objs := lua/lapi.o lua/lcode.o lua/lctype.o lua/ldebug.o lua/ldo.o \
lua/ldump.o lua/lfunc.o lua/lgc.o lua/llex.o lua/lmem.o \
lua/lobject.o lua/lopcodes.o lua/lparser.o lua/lstate.o \
lua/lstring.o lua/ltable.o lua/ltm.o \
lua/lundump.o lua/lvm.o lua/lzio.o lua/lauxlib.o lua/lbaselib.o \
lua/lbitlib.o lua/lcorolib.o lua/ldblib.o lua/lstrlib.o \
lua/ltablib.o lua/lutf8lib.o lua/loslib.o lua/lmathlib.o lua/linit.o

lunatik-objs += arch/$(ARCH)/setjmp.o

lunatik-${CONFIG_LUNATIK_POC} += ../poc-driver/luadrv.o
```
8. Then:
```
cd /usr/src/linux
#compile
make modules -j4 ARCH=x86_64
#load
modprobe -v lunatik
insmod ./dependencies/lunatik/lunatik.ko
insmod poc-driver.ko
```

## Usage
Expand Down
204 changes: 130 additions & 74 deletions luadrv.c
Original file line number Diff line number Diff line change
Expand Up @@ -6,29 +6,46 @@
#include <linux/device.h>
#include <linux/cdev.h>
#include <linux/uaccess.h>
#include <linux/kthread.h>

#include <lua.h>
#include <lualib.h>
#include <lauxlib.h>

MODULE_LICENSE("Dual MIT/GPL");
MODULE_AUTHOR("Pedro Tammela <[email protected]>");
MODULE_DESCRIPTION("sample driver for lunatik proof of concepts");
MODULE_AUTHOR("lunatik team (https://github.com/luainkernel)");
MODULE_DESCRIPTION("Basic kernel module that provides /dev/luadrv character device to load and execute arbitrary lua code");

#define DEVICE_NAME "luadrv"
#define CLASS_NAME "lua"
#define NSTATES 4
// currently supported only one device
#define LUA_MAX_MINORS 1

#define raise_err(msg) pr_warn("[lua] %s - %s\n", __func__, msg);
#define print_info(msg) pr_warn("[lua] %s\n", msg);

static DEFINE_MUTEX(mtx);
typedef struct device_data {
dev_t dev;
struct device *luadev;
struct class *luaclass;
struct cdev luacdev;
struct mutex lock;
} device_data;

static lua_State *L;
static bool hasreturn = 0; /* does the lua state have anything for us? */
static dev_t dev;
static struct device *luadev;
static struct class *luaclass;
static struct cdev luacdev;
static device_data devs[LUA_MAX_MINORS];

typedef struct lua_exec {
int id;
lua_State *L;
int stacktop;
char *script;
struct task_struct *kthread;
struct mutex lock;
} lua_exec;

static lua_exec lua_states[NSTATES];

static int dev_open(struct inode*, struct file*);
static int dev_release(struct inode*, struct file*);
Expand All @@ -45,66 +62,85 @@ static struct file_operations fops =

static int __init luadrv_init(void)
{
int ret;
int ret, i, j;
device_data *dev = &devs[0];

ret = alloc_chrdev_region(&dev, 0, LUA_MAX_MINORS, "lua");
ret = alloc_chrdev_region(&dev->dev, 0, LUA_MAX_MINORS, "lua");
if (ret) {
raise_err("alloc_chrdev_region failed");
goto error;
}

cdev_init(&luacdev, &fops);
ret = cdev_add(&luacdev, dev, LUA_MAX_MINORS);
cdev_init(&dev->luacdev, &fops);
ret = cdev_add(&dev->luacdev, dev->dev, LUA_MAX_MINORS);
if (ret) {
raise_err("cdev_add failed");
goto error_free_region;
}

luaclass = class_create(THIS_MODULE, CLASS_NAME);
if (IS_ERR(luaclass)) {
dev->luaclass = class_create(THIS_MODULE, CLASS_NAME);
if (IS_ERR(dev->luaclass)) {
raise_err("class_create failed");
ret = PTR_ERR(luaclass);
ret = PTR_ERR(dev->luaclass);
goto error_free_cdev;
}

luadev = device_create(luaclass, NULL, dev,
dev->luadev = device_create(dev->luaclass, NULL, dev->dev,
NULL, "%s", DEVICE_NAME);
if (IS_ERR(luadev)) {
if (IS_ERR(dev->luadev)) {
raise_err("device_create failed");
ret = PTR_ERR(luadev);
ret = PTR_ERR(dev->luadev);
goto error_free_class;
}

L = luaL_newstate();
if (L == NULL) {
raise_err("no memory");
ret = -ENOMEM;
goto error_free_device;
mutex_init(&dev->lock);

for (i = 0; i < NSTATES; i++) {
lua_states[i].id = i;
lua_states[i].L = luaL_newstate();

if (lua_states[i].L == NULL) {
raise_err("no memory");
ret = -ENOMEM;

for (j = 0; j < i; j++) {
lua_close(lua_states[j].L);
}

goto error_free_device;
}

luaL_openlibs(lua_states[i].L);
mutex_init(&lua_states[i].lock);
}
luaL_openlibs(L);

return 0;

error_free_device:
device_destroy(luaclass, dev);
device_destroy(dev->luaclass, dev->dev);
error_free_class:
class_destroy(luaclass);
class_destroy(dev->luaclass);
error_free_cdev:
cdev_del(&luacdev);
cdev_del(&dev->luacdev);
error_free_region:
unregister_chrdev_region(dev, LUA_MAX_MINORS);
unregister_chrdev_region(dev->dev, LUA_MAX_MINORS);
error:
return ret;
}

static void __exit luadrv_exit(void)
{
lua_close(L);
int i;
device_data *dev = &devs[0];

for (i = 0; i < NSTATES; i++) {
lua_close(lua_states[i].L);
}

device_destroy(luaclass, dev);
class_destroy(luaclass);
cdev_del(&luacdev);
unregister_chrdev_region(dev, LUA_MAX_MINORS);
device_destroy(dev->luaclass, dev->dev);
class_destroy(dev->luaclass);
cdev_del(&dev->luacdev);
unregister_chrdev_region(dev->dev, LUA_MAX_MINORS);
}

static int dev_open(struct inode *i, struct file *f)
Expand All @@ -114,75 +150,95 @@ static int dev_open(struct inode *i, struct file *f)

static ssize_t dev_read(struct file *f, char *buf, size_t len, loff_t *off)
{
const char *msg = "Nothing yet.\n";
int msglen;
int err;
mutex_lock(&mtx);
if (hasreturn) {
msg = lua_tostring(L, -1);
hasreturn = false;
}
if ((err = copy_to_user(buf, msg, len)) < 0) {
raise_err("copy to user failed");
mutex_unlock(&mtx);
return -ECANCELED;
}
mutex_unlock(&mtx);
msglen = strlen(msg);
return msglen < len ? msglen : len;
return 0;
}

static int flushL(void)
static lua_State* flush(lua_State *L)
{
lua_close(L);
L = luaL_newstate();
if (L == NULL) {
raise_err("flushL failed, giving up");
mutex_unlock(&mtx);
return 1;
return NULL;
}
luaL_openlibs(L);
raise_err("lua state flushed");
return 0;
return L;
}

static int thread_fn(void *arg)
{
int ret = 0;
lua_exec *lua = arg;
set_current_state(TASK_INTERRUPTIBLE);

printk("[lua] running thread %d\n", lua->id);
if (luaL_dostring(lua->L, lua->script)) {
raise_err("script error, flushing the state\n");
printk("%s\n", lua_tostring(lua->L, -1));
lua->L = flush(lua->L);
ret = -ECANCELED;
} else if (lua_gettop(lua->L) > lua->stacktop) {
printk("[lua] thread %d result: %s\n", lua->id, lua_tostring(lua->L, -1));
}

kfree(lua->script);
mutex_unlock(&lua->lock);

printk("[lua] thread %d finished\n", lua->id);
return ret;
}

static ssize_t dev_write(struct file *f, const char *buf, size_t len,
loff_t* off)
{
device_data *dev = &devs[0];
int ret, i;
char *script = NULL;
int idx = lua_gettop(L);
int err;
mutex_lock(&mtx);
script = kmalloc(len, GFP_KERNEL);

mutex_lock(&dev->lock);

script = kmalloc(len + 1, GFP_KERNEL);
if (script == NULL) {
raise_err("no memory");
return -ENOMEM;
ret = -ENOMEM;
goto return_unlock;
}
if ((err = copy_from_user(script, buf, len)) < 0) {

if (copy_from_user(script, buf, len) < 0) {
raise_err("copy from user failed");
mutex_unlock(&mtx);
return -ECANCELED;
ret = -ECANCELED;
goto return_free;
}
script[len - 1] = '\0';
if (luaL_dostring(L, script)) {
raise_err(lua_tostring(L, -1));
if (flushL()) {
return -ECANCELED;
script[len] = '\0';

for (i = 0; i < NSTATES; i++) {
if (lua_states[i].L != NULL && mutex_trylock(&lua_states[i].lock)) {
lua_states[i].stacktop = lua_gettop(lua_states[i].L);
lua_states[i].script = script;
lua_states[i].kthread = kthread_run(thread_fn, &lua_states[i], "lua kthread %d", lua_states[i].id);
if(IS_ERR(lua_states[i].kthread)) {
ret = PTR_ERR(lua_states[i].kthread);
goto return_free;
}

ret = len;
goto return_unlock;
}
mutex_unlock(&mtx);
return -ECANCELED;
}

raise_err("all lua states are busy");
ret = -EBUSY;

return_free:
kfree(script);
hasreturn = lua_gettop(L) > idx ? true : false;
mutex_unlock(&mtx);
return len;
return_unlock:
mutex_unlock(&dev->lock);
return ret;
}

static int dev_release(struct inode *i, struct file *f)
{
mutex_lock(&mtx);
hasreturn = false;
mutex_unlock(&mtx);
return 0;
}

Expand Down