Commit ff505da5 authored by Charlie Jacobsen's avatar Charlie Jacobsen Committed by Vikram Narayanan
Browse files

lcd-create-v2: Kernel module load/unload for kliblcd.

Adds two new functions to liblcd interface - lcd_module_load and
lcd_module_unload - for loading a kernel module from disk and into
the caller's address space. (Interface designed so that at some
point in the future, liblcd can implement these for isolated
code.)

The kliblcd implementation uses the host module loader to bring
in the kernel module from disk (as before), but now we duplicate
the entire module (rather than just the struct module pages).

Motivation: There are numerous bits embedded in the kernel module's
image (struct module, symbol tables, string tables, and possibly
more) that the host uses during kallsyms lookup, module linked-list
traversal, and so on, and it wouldn't be safe for the module loader
to share that with the LCD. (The module loader wasn't designed
to share bits with a potentially malicious or buggy peer.) It's
simpler to just duplicate the entire module image (init and core).

The other advantage is once we've made the duplicate, we can
unload the copy in the host module loader (all we needed were the
program bits).

IMPORTANT: This has some significant implications:

  1 - It means means we no longer have to look up the host's copy
      of the struct module in the receiving side of glue code for
      e.g. register_filesystem. (The struct module will be treated
      just like any other struct.)

      I think overall this will be a good thing, and we won't run into
      serious problems in the near future. Functions like
      register_filesystem bump the reference count on the struct
      module. When the user tried to unload the kernel module, the host
      checks that the reference count goest to zero. Now, we need to
      ensure instead that the LCD that contains the module won't go
      away (e.g. before all file systems it is responsible for have
      been unmounted). We'll have to cross that bridge when we get there.

  2 - Some of the host utility functions for e.g. symbol lookup will
      not work as is. But I see some workarounds.

The other big change is now we're using more coarse-grained
capability management for the module pages (only two capabilities -
one for init vmalloc mem, one for core vmalloc mem, rather than
a capability for each page). I need to now implement
lcd_vmalloc/lcd_vfree in order for this code to be fully ready.

Note: I considered linking the kernel module at a lower address
so that we can use a direct map in the LCD's virtual address
space. And this may be possible now that we're unloading the
module right after we load it. But it's a bit risky since the
host uses some of the section addresses for symbol and exception
table lookup, and maybe others (if I linked it for a lower
address, the host would crash or use random memory). The benefits
didn't seem to outweigh the risks.
parent ac556704
/*
* module_load.c
*
* For loading a kernel module from disk into memory.
*
* Copyright: University of Utah
*/
#include <lcd-domains/liblcd.h>
/* HOST LOAD/UNLOAD -------------------------------------------------- */
static inline size_t mpath_size(char *mdir, char *module_name)
{
size_t psize = 0;
/* mdir/ */
psize += strlen(mdir) + 1;
/* module_name.ko */
psize += strlen(module_name) + 3;
/* Allow for trailing \0 */
return psize + 1;
}
int __kliblcd_module_host_load(char *mdir, char *module_name,
struct module **m_out)
{
int ret;
struct module *m;
char *mpath;
size_t psize;
/*
* Form module path
*/
psize = mpath_size(mdir, module_name);
if (psize >= LCD_MPATH_SIZE) {
LIBLCD_ERR("path to lcd's module .ko is too big");
LIBLCD_ERR(" mdir = %s, name = %s, size = %llu",
mdir, module_name, psize);
ret = -EINVAL;
goto fail;
}
mpath = kmalloc(psize, GFP_KERNEL);
if (!mpath) {
LIBLCD_ERR("malloc module path");
ret = -ENOMEM;
goto fail0;
}
snprintf(mpath, psize, "%s/%s.ko", mdir, module_name);
/*
* Load the requested module
*/
ret = request_lcd_module(mpath);
if (ret < 0) {
LIBLCD_ERR("load module failed for %s (ret = %d)", mpath, ret);
goto fail1;
}
/*
* Find loaded module, and inc its ref counter; must hold module mutex
* while finding module.
*/
mutex_lock(&module_mutex);
m = find_module(module_name);
mutex_unlock(&module_mutex);
if (!m) {
LIBLCD_ERR("couldn't find module");
ret = -EIO;
goto fail2;
}
if(!try_module_get(m)) {
LIBLCD_ERR("incrementing module ref count");
ret = -EIO;
goto fail3;
}
*m_out = m;
kfree(mpath);
return ret;
fail3:
ret = do_sys_delete_module(module_name, 0, 1);
if (ret)
LIBLCD_ERR("double fault: deleting module");
fail2:
fail1:
*m_out = NULL;
kfree(mpath);
fail0:
fail:
return ret;
}
void __kliblcd_module_host_unload(char *module_name)
{
int ret;
struct module *m;
/*
* Delete module
*
* We need to look it up so we can do a put
*/
mutex_lock(&module_mutex);
m = find_module(module_name);
mutex_unlock(&module_mutex);
if (!m) {
LIBLCD_ERR("couldn't find module");
goto fail;
}
module_put(m);
ret = do_sys_delete_module(module_name, 0, 1);
if (ret) {
LIBLCD_ERR("deleting module");
goto fail;
}
goto out;
out:
fail:
return;
}
/* HIGHER-LEVEL LOAD/UNLOAD ---------------------------------------- */
static int dup_module_pages(hva_t pages_base, unsigned long size,
void **dup_pages_bits,
cptr_t *dup_pages_cap_out)
{
int ret;
void *dup_pages;
unsigned int unused;
/*
* Alloc dup pages, and memcpy the bits over
*/
dup_pages = lcd_vmalloc(size);
if (!dup_pages) {
LIBLCD_ERR("failed to vmalloc dup pages");
ret = -ENOMEM;
goto fail1;
}
memcpy(dup_pages, hva2va(pages_base), size);
/*
* Look up the cptr to the vmalloc memory object capability
*/
ret = lcd_virt_to_cptr(__gva((unsigned long)dup_pages),
dup_pages_cap_out,
&unused);
if (ret) {
LIBLCD_ERR("vmalloc mem object lookup failed");
goto fail2;
}
*dup_pages_bits = dup_pages;
return 0;
fail2:
lcd_vfree(dup_pages);
fail1:
return ret;
}
static void dedup_module_pages(void *pages_bits)
{
lcd_vfree(pages_bits);
}
int lcd_load_module(char *mdir, char *mname,
void **m_init_bits,
void **m_core_bits,
cptr_t *m_init_pages,
cptr_t *m_core_pages,
gva_t *m_init_link_addr,
gva_t *m_core_link_addr)
{
int ret;
struct module *m;
/*
* Load module in host
*/
ret = __kliblcd_module_host_load(mdir, mname, &m);
if (ret)
goto fail1;
/*
* Dup init and core pages so that LCD will use a copy
* separate from the host (this will protect things
* like the struct module, the module's symbol table,
* etc. that the host uses for certain operations).
*/
ret = dup_module_pages(va2hva(m->module_init), m->init_size,
m_init_bits, m_init_pages);
if (ret) {
LIBLCD_ERR("failed to load module's init");
goto fail2;
}
ret = dup_module_pages(va2hva(m->module_core), m->core_size,
m_core_bits, m_core_pages);
if (ret) {
LIBLCD_ERR("failed to load module's core");
goto fail3;
}
/*
* Extract addresses where init and core should be mapped (the
* link base addresses)
*/
*m_init_link_addr = __gva((unsigned long)m->module_init);
*m_core_link_addr = __gva((unsigned long)m->module_core);
/*
* Unload module from host -- we don't need the host module
* loader to hang onto it now that we've got the program
* bits.
*/
__kliblcd_module_host_unload(mname);
return 0;
fail3:
dedup_module_pages(*m_init_bits);
fail2:
__kliblcd_module_host_unload(mname);
fail1:
return ret;
}
void lcd_release_module(void *m_init_bits, void *m_core_bits)
{
/*
* Delete duplicates
*/
dedup_module_pages(m_init_bits);
dedup_module_pages(m_core_bits);
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment