/* write callback */ static ssize_t kplugs_write(struct file *filp, const char *buf, size_t count, loff_t *f_pos) { kplugs_command_t *cmd = NULL; context_t *file_cont = NULL; context_t *cont = NULL; bytecode_t *code = NULL; function_t *func = NULL; exception_t excep; stack_t stack; word iter, arg; word args; byte little_endian; byte func_name[MAX_FUNC_NAME + 1]; int err = 0; #ifdef __LITTLE_ENDIAN little_endian = 1; #else little_endian = 0; #endif /* get the user's command */ cmd = (kplugs_command_t *)buf; file_cont = (context_t *)filp->private_data; if (count < sizeof(byte) * 3) { /* three bytes of header */ return create_error(file_cont, -ERROR_PARAM); } if (cmd->word_size != sizeof(word) || cmd->l_endian != little_endian) { return create_error(file_cont, -ERROR_ARCH); } if (cmd->version_major != VERSION_MAJOR || cmd->version_minor != VERSION_MINOR) { return create_error(file_cont, -ERROR_VERSION); } if (count != sizeof(kplugs_command_t)) { return create_error(file_cont, -ERROR_PARAM); } cont = cmd->is_global ? GLOBAL_CONTEXT : file_cont; switch (cmd->type) { case KPLUGS_LOAD: /* load a new function */ if (cmd->len2 != 0 || cmd->ptr2 != NULL) { return create_error(file_cont, -ERROR_PARAM); } code = memory_alloc(cmd->len1); if (NULL == code) { ERROR(create_error(file_cont, -ERROR_MEM)); } err = memory_copy_from_outside(code, cmd->uptr1, cmd->len1); if (err < 0) { err = create_error(file_cont, err); goto clean; } /* create the function */ err = function_create(code, cmd->len1, &func); if (err < 0) { err = create_error(file_cont, err); goto clean; } err = context_add_function(cont, func); if (err < 0) { err = create_error(file_cont, err); goto clean; } /* return the function's address */ context_create_reply(file_cont, (word)&func->func_code, NULL); return count; case KPLUGS_UNLOAD: /* unload a function with a name */ if (NULL != cmd->uptr2) { return create_error(file_cont, -ERROR_PARAM); } case KPLUGS_EXECUTE: /* execute (and unload) a function with a name */ if (cmd->len1 > MAX_FUNC_NAME || (cmd->len2 % sizeof(word)) != 0) { return create_error(file_cont, -ERROR_PARAM); } err = memory_copy_from_outside(func_name, cmd->uptr1, cmd->len1); if (err < 0) { return create_error(file_cont, err); } func_name[cmd->len1] = '\0'; /* find the function */ if (cmd->type == KPLUGS_UNLOAD) { func = context_find_function(cont, func_name); if (NULL == func) { return create_error(file_cont, -ERROR_UFUNC); } /* delete the function */ context_free_function(func); err = (int)count; goto clean; } if (!cmd->is_global) { func = context_find_function(file_cont, func_name); } if (NULL == func) { func = context_find_function(GLOBAL_CONTEXT, func_name); if (NULL == func) { return create_error(file_cont, -ERROR_UFUNC); } } goto execute_func; case KPLUGS_UNLOAD_ANONYMOUS: /* unload an anonymous function */ if (NULL != cmd->uptr2 || cmd->len1 || cmd->len2) { return create_error(file_cont, -ERROR_PARAM); } func = context_find_anonymous(cont, cmd->ptr1); if (NULL == func) { return create_error(file_cont, -ERROR_UFUNC); } /* delete the function */ context_free_function(func); err = (int)count; goto clean; break; case KPLUGS_EXECUTE_ANONYMOUS: /* execute (and unload) an anonymous function */ if (cmd->len1 || (cmd->len2 % sizeof(word)) != 0) { return create_error(file_cont, -ERROR_PARAM); } /* find the function */ if (!cmd->is_global) { func = context_find_anonymous(file_cont, cmd->ptr1); } if (NULL == func) { func = context_find_anonymous(GLOBAL_CONTEXT, cmd->ptr1); if (NULL == func) { return create_error(file_cont, -ERROR_UFUNC); } } execute_func: /* do the execution of a function: */ args = cmd->len2 / sizeof(word); if (args > func->num_maxargs || args < func->num_minargs) { ERROR_CLEAN(create_error(file_cont, -ERROR_ARGS)); } err = stack_alloc(&stack, sizeof(word), CALL_STACK_SIZE); if (err < 0) { err = create_error(file_cont, err); goto clean; } /* push the arguments to a stack */ for (iter = 0; iter < args; ++iter) { err = memory_copy_from_outside(&arg, cmd->ptr2 + (iter * sizeof(word)), sizeof(arg)); if (err < 0) { stack_free(&stack); err = create_error(file_cont, err); goto clean; } if (NULL == stack_push(&stack, &arg)) { stack_free(&stack); ERROR_CLEAN(create_error(file_cont, -ERROR_MEM)); } } /* execute the function and create an answer */ arg = vm_run_function(func, &stack, &excep); stack_free(&stack); if (excep.had_exception) { err = -EINVAL; /* it dosen't really matter which error. the value of the error will be taken from the answer */ arg = excep.value; } else { err = (int)count; } context_create_reply(file_cont, arg, &excep); goto clean; case KPLUGS_GET_LAST_EXCEPTION: if (NULL != cmd->uptr2 || cmd->len1 < sizeof(exception_t) || cmd->len2) { ERROR(create_error(file_cont, -ERROR_PARAM)); } err = context_get_last_exception(file_cont, (exception_t *)cmd->ptr1); if (err < 0) { return create_error(file_cont, err); } return count; default: return create_error(file_cont, -ERROR_PARAM); } clean: if (NULL != func) { function_put(func); } else if (NULL != code) { memory_free(code); } return err; }
/* copy memory from safely from any type of memory to any type of memory (optional - from different processes) */ int safe_memory_copy(void *dst, const void *src, word len, int dst_hint, int src_hint, word dst_pid, word src_pid) { word new_len = len; int dst_type, src_type; void *dst_map = NULL; void *src_map = NULL; struct task_struct *dst_task = current; struct task_struct *src_task = current; struct pid *pid_struct; int err = 0; if (!len) { return 0; } /* load the correct task structs: */ if (dst_pid || src_pid) { rcu_read_lock(); } if (dst_pid) { pid_struct = find_vpid(dst_pid); if (NULL == pid_struct) { ERROR_CLEAN(-ERROR_PARAM); } dst_task = pid_task(pid_struct, PIDTYPE_PID); } if (NULL == dst_task) { ERROR_CLEAN(-ERROR_PARAM); } if (src_pid) { pid_struct = find_vpid(src_pid); if (NULL == pid_struct) { ERROR_CLEAN(-ERROR_PARAM); } src_task = pid_task(pid_struct, PIDTYPE_PID); } if (NULL == src_task) { ERROR_CLEAN(-ERROR_PARAM); } /* if we don't know where this addresses came from, find out: */ if (dst_hint != ADDR_UNDEF) { dst_type = dst_hint; } else { dst_type = memory_check_addr_perm_task(dst, &new_len, 1, NULL, NULL, dst_task); if (dst_type == ADDR_UNDEF || new_len != len) { ERROR_CLEAN(-ERROR_POINT); } } if (src_hint != ADDR_UNDEF) { src_type = src_hint; } else { src_type = memory_check_addr_perm_task(src, &new_len, 0, NULL, NULL, src_task); if (src_type == ADDR_UNDEF || new_len != len) { ERROR_CLEAN(-ERROR_POINT); } } /* map user pages if we need to: */ /* IMPORTANT: * if you need to map user pages it cannot be atomic! * so if you are using a page from user space you should not * use this function in an atomic only context. */ if (dst_type == ADDR_OUTSIDE) { err = memory_map_task(dst, &new_len, &dst_map, (byte **)&dst, 1, dst_task); if (err < 0 || new_len != len) { goto clean; } } if (src_type == ADDR_OUTSIDE) { err = memory_map_task(src, &new_len, &src_map, (byte **)&src, 0, src_task); if (err < 0 || new_len != len) { goto clean; } } memory_copy(dst, src, len); err = 0; clean: if (dst_pid || src_pid) { rcu_read_unlock(); } if (dst_map) { memory_unmap(dst_map); } if (src_map) { memory_unmap(src_map); } return err; }
/* the module init function */ static int __init kplugs_init(void) { int err = 0; struct device *device = NULL; memory_start(); err = context_create(&GLOBAL_CONTEXT); if (err < 0) { output_string("Couldn't create the global context.\n"); ERROR_CLEAN(create_error(NULL, err)); } err = alloc_chrdev_region(&kplugs_devno , 0, 1, DEVICE_NAME); if (err < 0) { output_string("Couldn't allocate a region.\n"); ERROR_CLEAN(create_error(NULL, err)); } kplugs_class = class_create(THIS_MODULE, DEVICE_NAME); if (NULL == kplugs_class) { output_string("Couldn't create class.\n"); ERROR_CLEAN(-ENOMEM); } kplugs_cdev = cdev_alloc(); if (NULL == kplugs_cdev) { output_string("Couldn't allocate a cdev.\n"); ERROR_CLEAN(-ENOMEM); } cdev_init(kplugs_cdev, &kplugs_ops); err = cdev_add(kplugs_cdev, kplugs_devno, 1); if (err < 0) { output_string("Couldn't add the cdev.\n"); ERROR_CLEAN(create_error(NULL, err)); } device = device_create(kplugs_class, NULL, kplugs_devno, NULL, DEVICE_NAME); if (device == NULL) { output_string("Couldn't create the device.\n"); ERROR_CLEAN(-ENOMEM); } return 0; clean: if (NULL != kplugs_cdev) { cdev_del(kplugs_cdev); } if (NULL != kplugs_class) { class_destroy(kplugs_class); } if (kplugs_devno) { unregister_chrdev_region(kplugs_devno, 1); } if (NULL != GLOBAL_CONTEXT) { context_free(GLOBAL_CONTEXT); } memory_stop(); return err; }