diff --git a/gramine-device-testing-module/README.rst b/gramine-device-testing-module/README.rst index 957db7b..35f2a0b 100644 --- a/gramine-device-testing-module/README.rst +++ b/gramine-device-testing-module/README.rst @@ -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. diff --git a/gramine-device-testing-module/gramine_test_dev_ioctl.h b/gramine-device-testing-module/gramine_test_dev_ioctl.h new file mode 100644 index 0000000..e7f69ae --- /dev/null +++ b/gramine-device-testing-module/gramine_test_dev_ioctl.h @@ -0,0 +1,49 @@ +#pragma once + +#include + +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) diff --git a/gramine-device-testing-module/main.c b/gramine-device-testing-module/main.c index 76e641a..3a58104 100644 --- a/gramine-device-testing-module/main.c +++ b/gramine-device-testing-module/main.c @@ -10,6 +10,8 @@ #include #include +#include "gramine_test_dev_ioctl.h" + // TODO: add locking MODULE_AUTHOR("Gramine Authors"); @@ -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; @@ -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) { @@ -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, };