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

Add IOCTLs to the dummy Gramine device #4

Merged
merged 1 commit into from
Jul 25, 2022
Merged
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
31 changes: 21 additions & 10 deletions gramine-device-testing-module/README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,25 @@ all required functionality (e.g shared untrusted memory and ioctl syscall).
Features
========

This driver implements a custom character device, which allows for simple string
manipulations. Each open file handle is associated with a context holding all
necessary data, and allows for the following operations:
This driver implements a custom character device, which allows for simple byte
array manipulations. Each open file handle is associated with a context holding
all necessary data, and allows for the following operations:

- `open` - Create a string instance.
- `write` - Write data to the string at the current offset. If some data was
present at this offset, it's overwritten. The string is automatically
extended if needed.
- `read` - Read that from the string.
- `llseek` - Change the current offset in the string.
- `release` - Releases the string instance.
- `open` - Create a byte array instance.
- `write` - Write data to the byte array at the current offset. If some data
was present at this offset, it's overwritten. The byte array is
automatically extended if needed.
- `read` - Read data from the byte array.
- `llseek` - Change the current offset in the byte array.
- `release` - Releases the byte array instance.
- `unlocked_ioctl` - Perform one of the following IOCTLs:
- `GRAMINE_TEST_DEV_IOCTL_REWIND` - same as `llseek(filp, 0, SEEK_SET)`.
- `GRAMINE_TEST_DEV_IOCTL_WRITE` - same as `write`.
- `GRAMINE_TEST_DEV_IOCTL_READ` - same as `read`.
- `GRAMINE_TEST_DEV_IOCTL_GETSIZE` - Returns size of the byte array.
- `GRAMINE_TEST_DEV_IOCTL_CLEAR` - Frees the byte array.
- `GRAMINE_TEST_DEV_IOCTL_REPLACE_ARR` - Replaces specified characters in
the byte array; character replacements are passed as an array.
- `GRAMINE_TEST_DEV_IOCTL_REPLACE_LIST` - same as
`GRAMINE_TEST_DEV_IOCTL_REPLACE_ARR` but character replacements are passed
as a NULL-terminated list.
49 changes: 49 additions & 0 deletions gramine-device-testing-module/gramine_test_dev_ioctl.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
#pragma once

#include <linux/ioctl.h>

struct gramine_test_dev_ioctl_write {
size_t buf_size; /* in */
const char __user* buf; /* in */
loff_t off; /* in/out -- updated after write */
ssize_t copied; /* out -- how many bytes were actually written */
};

struct gramine_test_dev_ioctl_read {
size_t buf_size; /* in */
char __user* buf; /* out */
loff_t off; /* in/out -- updated after read */
ssize_t copied; /* out -- how many bytes were actually read */
};

struct gramine_test_dev_ioctl_replace_char {
char src; /* in */
char dst; /* in */
char pad[6];
};

struct gramine_test_dev_ioctl_replace_arr {
/* array of replacements, e.g. replacements_cnt == 2 and [`l` -> `$`, `o` -> `0`] */
size_t replacements_cnt;
struct gramine_test_dev_ioctl_replace_char __user* replacements_arr;
};

struct gramine_test_dev_ioctl_replace_list {
/* list of replacements, e.g. [`l` -> `$`, next points to `o` -> `0`, next points to NULL] */
struct gramine_test_dev_ioctl_replace_char replacement;
struct gramine_test_dev_ioctl_replace_list __user* next;
};

#define GRAMINE_TEST_DEV_IOCTL_BASE 0x81

#define GRAMINE_TEST_DEV_IOCTL_REWIND _IO(GRAMINE_TEST_DEV_IOCTL_BASE, 0x00)
#define GRAMINE_TEST_DEV_IOCTL_WRITE _IOWR(GRAMINE_TEST_DEV_IOCTL_BASE, 0x01, \
struct gramine_test_dev_ioctl_write)
#define GRAMINE_TEST_DEV_IOCTL_READ _IOWR(GRAMINE_TEST_DEV_IOCTL_BASE, 0x02, \
struct gramine_test_dev_ioctl_read)
#define GRAMINE_TEST_DEV_IOCTL_GETSIZE _IO(GRAMINE_TEST_DEV_IOCTL_BASE, 0x03)
#define GRAMINE_TEST_DEV_IOCTL_CLEAR _IO(GRAMINE_TEST_DEV_IOCTL_BASE, 0x04)
#define GRAMINE_TEST_DEV_IOCTL_REPLACE_ARR _IOW(GRAMINE_TEST_DEV_IOCTL_BASE, 0x05, \
struct gramine_test_dev_ioctl_replace_arr)
#define GRAMINE_TEST_DEV_IOCTL_REPLACE_LIST _IOW(GRAMINE_TEST_DEV_IOCTL_BASE, 0x06, \
struct gramine_test_dev_ioctl_replace_list)
112 changes: 111 additions & 1 deletion gramine-device-testing-module/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
#include <linux/module.h>
#include <linux/slab.h>

#include "gramine_test_dev_ioctl.h"

// TODO: add locking

MODULE_AUTHOR("Gramine Authors");
Expand All @@ -19,6 +21,8 @@ MODULE_DESCRIPTION("Dummy module just for testing purposes");
#define GRAMINE_TEST_DEV_MINOR_NUMS 1
#define GRAMINE_TEST_DEV_MAX_SIZE (0x100 * PAGE_SIZE)

#define LIST_ITEMS_MAX 128

struct gramine_test_dev_data {
size_t size;
char* buf;
Expand All @@ -29,6 +33,14 @@ static dev_t dev_num = 0;
struct cdev cdev;
struct device* device = NULL;

static void replace_all_occurences(struct gramine_test_dev_data* data, char src, char dst) {
size_t idx;
for (idx = 0; idx < data->size; idx++) {
if (data->buf[idx] == src)
data->buf[idx] = dst;
}
}

static int gramine_test_dev_open(struct inode* inode, struct file* filp) {
struct gramine_test_dev_data* data = kmalloc(sizeof(*data), GFP_KERNEL);
if (!data) {
Expand Down Expand Up @@ -110,11 +122,109 @@ static ssize_t gramine_test_dev_read(struct file* filp, char __user* buf, size_t
return copy_size;
}

static int gramine_test_dev_replace_arr(struct gramine_test_dev_data* data,
void __user* argp_user) {
size_t i;
struct gramine_test_dev_ioctl_replace_arr arg;

if (copy_from_user(&arg, argp_user, sizeof(arg))) {
return -EFAULT;
}

for (i = 0; i < arg.replacements_cnt; i++) {
struct gramine_test_dev_ioctl_replace_char replace_char;
if (copy_from_user(&replace_char, &arg.replacements_arr[i], sizeof(replace_char))) {
return -EFAULT;
}
replace_all_occurences(data, replace_char.src, replace_char.dst);
}

return 0;
}

static int gramine_test_dev_replace_list(struct gramine_test_dev_data* data,
void __user* argp_user) {
size_t list_items_cnt = 0;
struct gramine_test_dev_ioctl_replace_list __user* list_item_user = argp_user;

do {
struct gramine_test_dev_ioctl_replace_list list_item;
if (list_items_cnt++ > LIST_ITEMS_MAX) {
return -ELOOP;
}
if (copy_from_user(&list_item, list_item_user, sizeof(list_item))) {
return -EFAULT;
}
replace_all_occurences(data, list_item.replacement.src, list_item.replacement.dst);
list_item_user = list_item.next;
} while (list_item_user);

return 0;
}

static ssize_t gramine_test_dev_ioctl(struct file *filp, unsigned int cmd, unsigned long argp) {
struct gramine_test_dev_data* data = filp->private_data;
void __user* argp_user = (void __user*)argp;

switch (cmd) {
case GRAMINE_TEST_DEV_IOCTL_REWIND:
return default_llseek(filp, /*offset=0*/0, SEEK_SET);
case GRAMINE_TEST_DEV_IOCTL_WRITE: {
ssize_t copied;
struct gramine_test_dev_ioctl_write arg;
if (copy_from_user(&arg, argp_user, sizeof(arg))) {
return -EFAULT;
}
copied = gramine_test_dev_write(filp, arg.buf, arg.buf_size, &arg.off);
if (copied < 0) {
return copied;
}
arg.copied = copied;
if (copy_to_user(argp_user, &arg, sizeof(arg))) {
return -EFAULT;
}
return 0;
}
case GRAMINE_TEST_DEV_IOCTL_READ: {
ssize_t copied;
struct gramine_test_dev_ioctl_read arg;
if (copy_from_user(&arg, argp_user, sizeof(arg))) {
return -EFAULT;
}
copied = gramine_test_dev_read(filp, arg.buf, arg.buf_size, &arg.off);
if (copied < 0) {
return copied;
}
arg.copied = copied;
if (copy_to_user(argp_user, &arg, sizeof(arg))) {
return -EFAULT;
}
return 0;
}
case GRAMINE_TEST_DEV_IOCTL_GETSIZE:
return (ssize_t)data->size;
case GRAMINE_TEST_DEV_IOCTL_CLEAR:
kfree(data->buf);
data->size = 0;
data->buf = NULL;
return 0;
case GRAMINE_TEST_DEV_IOCTL_REPLACE_ARR:
return gramine_test_dev_replace_arr(data, argp_user);
case GRAMINE_TEST_DEV_IOCTL_REPLACE_LIST:
return gramine_test_dev_replace_list(data, argp_user);
default:
return -EINVAL;
}

return 0;
}

static struct file_operations fops = {
.owner = THIS_MODULE,
.llseek = default_llseek,
.read = gramine_test_dev_read,
.write = gramine_test_dev_write,
.read = gramine_test_dev_read,
.unlocked_ioctl = gramine_test_dev_ioctl,
.open = gramine_test_dev_open,
.release = gramine_test_dev_release,
};
Expand Down