From: Kyle Hale Date: Wed, 10 Apr 2013 21:03:22 +0000 (-0500) Subject: Add guarded module infrastructure X-Git-Url: http://v3vee.org/palacios/gitweb/gitweb.cgi?a=commitdiff_plain;h=324f9e9a6e83b8079926902a26e3f6bb5e624a06;p=palacios.releases.git Add guarded module infrastructure This commit includes all of the changes for guarded modules that do not touch the VMM core at all. This includes a host interface, userspace utility, and Palacios extensions. --- diff --git a/linux_module/Makefile b/linux_module/Makefile index a7eeb53..d45c494 100644 --- a/linux_module/Makefile +++ b/linux_module/Makefile @@ -44,6 +44,7 @@ v3vee-$(V3_CONFIG_HOST_HYPERCALL) += iface-host-hypercall.o v3vee-$(V3_CONFIG_EXT_CODE_INJECT) += iface-code-inject.o v3vee-$(V3_CONFIG_EXT_ENV_INJECT) += iface-env-inject.o v3vee-$(V3_CONFIG_EXT_SELECTIVE_SYSCALL_EXIT) += iface-syscall.o +v3vee-$(V3_CONFIG_EXT_GUARD_MODULES) += iface-guard-mods.o v3vee-$(V3_CONFIG_HOST_PCI) += iface-host-pci.o diff --git a/linux_module/iface-guard-mods.c b/linux_module/iface-guard-mods.c new file mode 100644 index 0000000..67917d7 --- /dev/null +++ b/linux_module/iface-guard-mods.c @@ -0,0 +1,211 @@ +/* + * Linux interface for guarded module registration + * + * (c) Kyle C. Hale 2012 + * + */ + +#include +#include +#include +#include +#include +#include +#include "palacios.h" +#include "vm.h" +#include "linux-exts.h" +#include "iface-guard-mods.h" + + +static int +vm_register_mod (struct v3_guest * guest, + unsigned int cmd, + unsigned long arg, + void * priv_data) +{ + uint64_t ret; + struct v3_guard_mod arg_mod; + struct v3_guard_mod * gm; + int i; + + gm = palacios_alloc(sizeof(struct v3_guard_mod)); + if (!gm) { + ERROR("palacios: error allocating guarded module\n"); + return -EFAULT; + } + + if (copy_from_user(&arg_mod, (void __user *)arg, sizeof(struct v3_guard_mod))) { + ERROR("palacios: error copying guarded module data from userspace\n"); + return -EFAULT; + } + + memcpy(gm, &arg_mod, sizeof(struct v3_guard_mod)); + + /* get module name */ + gm->name = palacios_alloc(strnlen_user((char __user *)arg_mod.name, MAX_MOD_NAME_LEN)+1); + if (!gm->name) { + ERROR("Problem allocating space for mod name\n"); + return -1; + } + + if (strncpy_from_user(gm->name, (char __user *)arg_mod.name, MAX_MOD_NAME_LEN) == -EFAULT) { + ERROR("problem copying from userspace\n"); + return -1; + } + + /* get module content hash */ + gm->content_hash = palacios_alloc(strnlen_user((char __user *)arg_mod.content_hash, MAX_HASH_LEN)+1); + if (!gm->content_hash) { + ERROR("Problem allocating space for content hash\n"); + return -1; + } + + if (strncpy_from_user(gm->content_hash, (char __user *)arg_mod.content_hash, MAX_HASH_LEN) == -EFAULT) { + ERROR("problem copying from userspace\n"); + return -1; + } + + /* get valid entry points */ + gm->entry_points = palacios_alloc(sizeof(struct v3_guard_mod)*arg_mod.num_entries); + if (!gm->entry_points) { + ERROR("Problem allocating space for entry point array\n"); + return -1; + } + + if (copy_from_user(gm->entry_points, (void __user *)arg_mod.entry_points, sizeof(struct v3_guard_mod)*arg_mod.num_entries) + == -EFAULT) { + ERROR("problem copying from userspace\n"); + return -1; + } + + for (i = 0; i < gm->num_entries; i++) { + int len; + char * tmp; + + if ((len = strnlen_user((char __user *)gm->entry_points[i].name, MAX_MOD_NAME_LEN)+1) == -EFAULT) { + ERROR("problem getting strlen from userspace\n"); + return -1; + } + + tmp = palacios_alloc(len); + if (!tmp) { + ERROR("Problem allocating space for string\n"); + return -1; + } + + if (strncpy_from_user(tmp, (char __user *)gm->entry_points[i].name, MAX_MOD_NAME_LEN) == -EFAULT) { + ERROR("problem copying from userspace\n"); + return -1; + } + + gm->entry_points[i].name = tmp; + } + + /* get list of privileges */ + gm->priv_array = palacios_alloc(sizeof(char*)*arg_mod.num_privs); + if (!gm->priv_array) { + ERROR("Problem allocating space for privilege array\n"); + return -1; + } + + if (copy_from_user(gm->priv_array, (void __user *)arg_mod.priv_array, sizeof(char*)*arg_mod.num_privs) + == -EFAULT) { + ERROR("problem copying privilege array from userspace\n"); + return -1; + } + + for (i = 0; i < gm->num_privs; i++) { + int len; + char * tmp; + if ((len = strlen_user((char __user*)gm->priv_array[i]) + 1) == -EFAULT) { + ERROR("problem getting strlen from userspace\n"); + return -1; + } + + tmp = palacios_alloc(len); + if (!tmp) { + ERROR("Problem allocating space for privilege name\n"); + return -1; + } + + if (strncpy_from_user(tmp, (char __user *)gm->priv_array[i], MAX_MOD_NAME_LEN) == -EFAULT) { + ERROR("problem copying privilege from userspace\n"); + return -1; + } + + gm->priv_array[i] = tmp; + } + + INFO("Registering Guarded Module with Palacios\n"); + ret = v3_register_gm(guest->v3_ctx, + gm->name, + gm->content_hash, + gm->hcall_offset, + gm->text_size, + gm->num_entries, + gm->num_privs, + gm->priv_array, + NULL, + (void*)gm->entry_points); + + if (!ret) { + ERROR("palacios: could not register guarded module: %s\n", arg_mod.name); + return -1; + } + + arg_mod.id = ret; + + if (copy_to_user((void __user *)arg, &arg_mod, sizeof(struct v3_guard_mod))) { + ERROR("palacios: error copying guarded module back to userspace\n"); + return -1; + } + + kfree(gm->name); + kfree(gm->content_hash); + for (i = 0; i < gm->num_entries; i++) { + kfree(gm->entry_points[i].name); + } + kfree(gm->entry_points); + kfree(gm); + return 0; +} + + +static int +init_guard_mods (void) +{ + return 0; +} + + +static int +deinit_guard_mods (void) +{ + return 0; +} + + +static int +guest_init_guard_mods (struct v3_guest * guest, void ** vm_data) +{ + add_guest_ctrl(guest, V3_VM_REGISTER_MOD, vm_register_mod, NULL); + return 0; +} + + +static int +guest_deinit_guard_mods (struct v3_guest * guest, void * vm_data) +{ + return 0; +} + + +static struct linux_ext guard_mods_ext = { + .name = "GUARDED_MODULES", + .init = init_guard_mods, + .deinit = deinit_guard_mods, + .guest_init = guest_init_guard_mods, + .guest_deinit = guest_deinit_guard_mods +}; + +register_extension(&guard_mods_ext); diff --git a/linux_module/iface-guard-mods.h b/linux_module/iface-guard-mods.h new file mode 100644 index 0000000..416e4b9 --- /dev/null +++ b/linux_module/iface-guard-mods.h @@ -0,0 +1,31 @@ +#ifndef __IFACE_GUARD_MODS_H__ +#define __IFACE_GUARD_MODS_H__ + +#define V3_VM_REGISTER_MOD 14124 + +#define MAX_MOD_NAME_LEN 256 +#define MAX_HASH_LEN 128 + +struct v3_entry_point { + char * name; + unsigned long offset; + char is_ret; + + /* TODO: Fix this HACKERY! These last two are placeholders */ + void * hack[3]; +}; + + +struct v3_guard_mod { + char * name; /* GM name */ + char * content_hash; /* hash of the module .text segment */ + unsigned int hcall_offset; /* offset from .text to hypercall */ + unsigned int text_size; /* size of module .text segment */ + unsigned int num_entries; /* number of entry points */ + unsigned int num_privs; + char ** priv_array; /* each bit represent a requested privilege */ + unsigned long long id; /* GM ID */ + struct v3_entry_point * entry_points; /* entry point array (offsets) */ +}; + +#endif diff --git a/linux_usr/Makefile b/linux_usr/Makefile index 4f03c2a..b53732f 100644 --- a/linux_usr/Makefile +++ b/linux_usr/Makefile @@ -49,7 +49,8 @@ EXPERIMENTAL_EXECS = v3_simulate \ v3_inject_ecc_scrubber_mce \ v3_top_inject \ v3_env_inject \ - v3_syscall + v3_syscall \ + v3_register_gm @@ -89,6 +90,10 @@ libv3_ctrl.a : v3_ctrl.c v3_ctrl.h $(AR) ruv libv3_ctrl.a v3_ctrl.o rm -rf v3_ctrl.o +libjson.a : cJSON.c cJSON.h + $(CC) $(CFLAGS) -c cJSON.c + $(AR) ruv libjson.a cJSON.o + rm -rf cJSON.o # # Most tools compile in a straightforward way # @@ -132,6 +137,8 @@ v3_user_host_dev_example : v3_user_host_dev_example.c libv3_user_host_dev.a v3_os_debug : v3_os_debug.c libv3_user_host_dev.a $(CC) $(CFLAGS) $< -I../linux_module -L. -lv3_user_host_dev -o $@ +v3_register_gm: v3_register_gm.c libjson.a libv3_ctrl.a v3_ctrl.h + $(CC) $(CFLAGS) $< -L. -lm -lv3_ctrl -ljson -o $@ # # User keyed stream support includes a library and examples diff --git a/linux_usr/cJSON.c b/linux_usr/cJSON.c new file mode 100644 index 0000000..eb7c2f9 --- /dev/null +++ b/linux_usr/cJSON.c @@ -0,0 +1,514 @@ +/* + Copyright (c) 2009 Dave Gamble + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ + +/* cJSON */ +/* JSON parser in C. */ + +#include +#include +#include +#include +#include +#include +#include +#include "cJSON.h" + +static const char *ep; + +const char *cJSON_GetErrorPtr() {return ep;} + +static int cJSON_strcasecmp(const char *s1,const char *s2) +{ + if (!s1) return (s1==s2)?0:1;if (!s2) return 1; + for(; tolower(*s1) == tolower(*s2); ++s1, ++s2) if(*s1 == 0) return 0; + return tolower(*(const unsigned char *)s1) - tolower(*(const unsigned char *)s2); +} + +static void *(*cJSON_malloc)(size_t sz) = malloc; +static void (*cJSON_free)(void *ptr) = free; + +static char* cJSON_strdup(const char* str) +{ + size_t len; + char* copy; + + len = strlen(str) + 1; + if (!(copy = (char*)cJSON_malloc(len))) return 0; + memcpy(copy,str,len); + return copy; +} + +void cJSON_InitHooks(cJSON_Hooks* hooks) +{ + if (!hooks) { /* Reset hooks */ + cJSON_malloc = malloc; + cJSON_free = free; + return; + } + + cJSON_malloc = (hooks->malloc_fn)?hooks->malloc_fn:malloc; + cJSON_free = (hooks->free_fn)?hooks->free_fn:free; +} + +/* Internal constructor. */ +static cJSON *cJSON_New_Item() +{ + cJSON* node = (cJSON*)cJSON_malloc(sizeof(cJSON)); + if (node) memset(node,0,sizeof(cJSON)); + return node; +} + +/* Delete a cJSON structure. */ +void cJSON_Delete(cJSON *c) +{ + cJSON *next; + while (c) + { + next=c->next; + if (!(c->type&cJSON_IsReference) && c->child) cJSON_Delete(c->child); + if (!(c->type&cJSON_IsReference) && c->valuestring) cJSON_free(c->valuestring); + if (c->string) cJSON_free(c->string); + cJSON_free(c); + c=next; + } +} + +/* Parse the input text to generate a number, and populate the result into item. */ +static const char *parse_number(cJSON *item,const char *num) +{ + double n=0,sign=1,scale=0;int subscale=0,signsubscale=1; + + /* Could use sscanf for this? */ + if (*num=='-') sign=-1,num++; /* Has sign? */ + if (*num=='0') num++; /* is zero */ + if (*num>='1' && *num<='9') do n=(n*10.0)+(*num++ -'0'); while (*num>='0' && *num<='9'); /* Number? */ + if (*num=='.' && num[1]>='0' && num[1]<='9') {num++; do n=(n*10.0)+(*num++ -'0'),scale--; while (*num>='0' && *num<='9');} /* Fractional part? */ + if (*num=='e' || *num=='E') /* Exponent? */ + { num++;if (*num=='+') num++; else if (*num=='-') signsubscale=-1,num++; /* With sign? */ + while (*num>='0' && *num<='9') subscale=(subscale*10)+(*num++ - '0'); /* Number? */ + } + + n=sign*n*pow(10.0,(scale+subscale*signsubscale)); /* number = +/- number.fraction * 10^+/- exponent */ + + item->valuedouble=n; + item->valueint=(int)n; + item->type=cJSON_Number; + return num; +} + +/* Render the number nicely from the given item into a string. */ +static char *print_number(cJSON *item) +{ + char *str; + double d=item->valuedouble; + if (fabs(((double)item->valueint)-d)<=DBL_EPSILON && d<=INT_MAX && d>=INT_MIN) + { + str=(char*)cJSON_malloc(21); /* 2^64+1 can be represented in 21 chars. */ + if (str) sprintf(str,"%d",item->valueint); + } + else + { + str=(char*)cJSON_malloc(64); /* This is a nice tradeoff. */ + if (str) + { + if (fabs(floor(d)-d)<=DBL_EPSILON) sprintf(str,"%.0f",d); + else if (fabs(d)<1.0e-6 || fabs(d)>1.0e9) sprintf(str,"%e",d); + else sprintf(str,"%f",d); + } + } + return str; +} + +/* Parse the input text into an unescaped cstring, and populate item. */ +static const unsigned char firstByteMark[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC }; +static const char *parse_string(cJSON *item,const char *str) +{ + const char *ptr=str+1;char *ptr2;char *out;int len=0;unsigned uc,uc2; + if (*str!='\"') {ep=str;return 0;} /* not a string! */ + + while (*ptr!='\"' && *ptr && ++len) if (*ptr++ == '\\') ptr++; /* Skip escaped quotes. */ + + out=(char*)cJSON_malloc(len+1); /* This is how long we need for the string, roughly. */ + if (!out) return 0; + + ptr=str+1;ptr2=out; + while (*ptr!='\"' && *ptr) + { + if (*ptr!='\\') *ptr2++=*ptr++; + else + { + ptr++; + switch (*ptr) + { + case 'b': *ptr2++='\b'; break; + case 'f': *ptr2++='\f'; break; + case 'n': *ptr2++='\n'; break; + case 'r': *ptr2++='\r'; break; + case 't': *ptr2++='\t'; break; + case 'u': /* transcode utf16 to utf8. */ + sscanf(ptr+1,"%4x",&uc);ptr+=4; /* get the unicode char. */ + + if ((uc>=0xDC00 && uc<=0xDFFF) || uc==0) break; // check for invalid. + + if (uc>=0xD800 && uc<=0xDBFF) // UTF16 surrogate pairs. + { + if (ptr[1]!='\\' || ptr[2]!='u') break; // missing second-half of surrogate. + sscanf(ptr+3,"%4x",&uc2);ptr+=6; + if (uc2<0xDC00 || uc2>0xDFFF) break; // invalid second-half of surrogate. + uc=0x10000 | ((uc&0x3FF)<<10) | (uc2&0x3FF); + } + + len=4;if (uc<0x80) len=1;else if (uc<0x800) len=2;else if (uc<0x10000) len=3; ptr2+=len; + + switch (len) { + case 4: *--ptr2 =((uc | 0x80) & 0xBF); uc >>= 6; + case 3: *--ptr2 =((uc | 0x80) & 0xBF); uc >>= 6; + case 2: *--ptr2 =((uc | 0x80) & 0xBF); uc >>= 6; + case 1: *--ptr2 =(uc | firstByteMark[len]); + } + ptr2+=len; + break; + default: *ptr2++=*ptr; break; + } + ptr++; + } + } + *ptr2=0; + if (*ptr=='\"') ptr++; + item->valuestring=out; + item->type=cJSON_String; + return ptr; +} + +/* Render the cstring provided to an escaped version that can be printed. */ +static char *print_string_ptr(const char *str) +{ + const char *ptr;char *ptr2,*out;int len=0;unsigned char token; + + if (!str) return cJSON_strdup(""); + ptr=str;while ((token=*ptr) && ++len) {if (strchr("\"\\\b\f\n\r\t",token)) len++; else if (token<32) len+=5;ptr++;} + + out=(char*)cJSON_malloc(len+3); + if (!out) return 0; + + ptr2=out;ptr=str; + *ptr2++='\"'; + while (*ptr) + { + if ((unsigned char)*ptr>31 && *ptr!='\"' && *ptr!='\\') *ptr2++=*ptr++; + else + { + *ptr2++='\\'; + switch (token=*ptr++) + { + case '\\': *ptr2++='\\'; break; + case '\"': *ptr2++='\"'; break; + case '\b': *ptr2++='b'; break; + case '\f': *ptr2++='f'; break; + case '\n': *ptr2++='n'; break; + case '\r': *ptr2++='r'; break; + case '\t': *ptr2++='t'; break; + default: sprintf(ptr2,"u%04x",token);ptr2+=5; break; /* escape and print */ + } + } + } + *ptr2++='\"';*ptr2++=0; + return out; +} +/* Invote print_string_ptr (which is useful) on an item. */ +static char *print_string(cJSON *item) {return print_string_ptr(item->valuestring);} + +/* Predeclare these prototypes. */ +static const char *parse_value(cJSON *item,const char *value); +static char *print_value(cJSON *item,int depth,int fmt); +static const char *parse_array(cJSON *item,const char *value); +static char *print_array(cJSON *item,int depth,int fmt); +static const char *parse_object(cJSON *item,const char *value); +static char *print_object(cJSON *item,int depth,int fmt); + +/* Utility to jump whitespace and cr/lf */ +static const char *skip(const char *in) {while (in && *in && (unsigned char)*in<=32) in++; return in;} + +/* Parse an object - create a new root, and populate. */ +cJSON *cJSON_Parse(const char *value) +{ + cJSON *c=cJSON_New_Item(); + ep=0; + if (!c) return 0; /* memory fail */ + + if (!parse_value(c,skip(value))) {cJSON_Delete(c);return 0;} + return c; +} + +/* Render a cJSON item/entity/structure to text. */ +char *cJSON_Print(cJSON *item) {return print_value(item,0,1);} +char *cJSON_PrintUnformatted(cJSON *item) {return print_value(item,0,0);} + +/* Parser core - when encountering text, process appropriately. */ +static const char *parse_value(cJSON *item,const char *value) +{ + if (!value) return 0; /* Fail on null. */ + if (!strncmp(value,"null",4)) { item->type=cJSON_NULL; return value+4; } + if (!strncmp(value,"false",5)) { item->type=cJSON_False; return value+5; } + if (!strncmp(value,"true",4)) { item->type=cJSON_True; item->valueint=1; return value+4; } + if (*value=='\"') { return parse_string(item,value); } + if (*value=='-' || (*value>='0' && *value<='9')) { return parse_number(item,value); } + if (*value=='[') { return parse_array(item,value); } + if (*value=='{') { return parse_object(item,value); } + + ep=value;return 0; /* failure. */ +} + +/* Render a value to text. */ +static char *print_value(cJSON *item,int depth,int fmt) +{ + char *out=0; + if (!item) return 0; + switch ((item->type)&255) + { + case cJSON_NULL: out=cJSON_strdup("null"); break; + case cJSON_False: out=cJSON_strdup("false");break; + case cJSON_True: out=cJSON_strdup("true"); break; + case cJSON_Number: out=print_number(item);break; + case cJSON_String: out=print_string(item);break; + case cJSON_Array: out=print_array(item,depth,fmt);break; + case cJSON_Object: out=print_object(item,depth,fmt);break; + } + return out; +} + +/* Build an array from input text. */ +static const char *parse_array(cJSON *item,const char *value) +{ + cJSON *child; + if (*value!='[') {ep=value;return 0;} /* not an array! */ + + item->type=cJSON_Array; + value=skip(value+1); + if (*value==']') return value+1; /* empty array. */ + + item->child=child=cJSON_New_Item(); + if (!item->child) return 0; /* memory fail */ + value=skip(parse_value(child,skip(value))); /* skip any spacing, get the value. */ + if (!value) return 0; + + while (*value==',') + { + cJSON *new_item; + if (!(new_item=cJSON_New_Item())) return 0; /* memory fail */ + child->next=new_item;new_item->prev=child;child=new_item; + value=skip(parse_value(child,skip(value+1))); + if (!value) return 0; /* memory fail */ + } + + if (*value==']') return value+1; /* end of array */ + ep=value;return 0; /* malformed. */ +} + +/* Render an array to text */ +static char *print_array(cJSON *item,int depth,int fmt) +{ + char **entries; + char *out=0,*ptr,*ret;int len=5; + cJSON *child=item->child; + int numentries=0,i=0,fail=0; + + /* How many entries in the array? */ + while (child) numentries++,child=child->next; + /* Allocate an array to hold the values for each */ + entries=(char**)cJSON_malloc(numentries*sizeof(char*)); + if (!entries) return 0; + memset(entries,0,numentries*sizeof(char*)); + /* Retrieve all the results: */ + child=item->child; + while (child && !fail) + { + ret=print_value(child,depth+1,fmt); + entries[i++]=ret; + if (ret) len+=strlen(ret)+2+(fmt?1:0); else fail=1; + child=child->next; + } + + /* If we didn't fail, try to malloc the output string */ + if (!fail) out=(char*)cJSON_malloc(len); + /* If that fails, we fail. */ + if (!out) fail=1; + + /* Handle failure. */ + if (fail) + { + for (i=0;itype=cJSON_Object; + value=skip(value+1); + if (*value=='}') return value+1; /* empty array. */ + + item->child=child=cJSON_New_Item(); + if (!item->child) return 0; + value=skip(parse_string(child,skip(value))); + if (!value) return 0; + child->string=child->valuestring;child->valuestring=0; + if (*value!=':') {ep=value;return 0;} /* fail! */ + value=skip(parse_value(child,skip(value+1))); /* skip any spacing, get the value. */ + if (!value) return 0; + + while (*value==',') + { + cJSON *new_item; + if (!(new_item=cJSON_New_Item())) return 0; /* memory fail */ + child->next=new_item;new_item->prev=child;child=new_item; + value=skip(parse_string(child,skip(value+1))); + if (!value) return 0; + child->string=child->valuestring;child->valuestring=0; + if (*value!=':') {ep=value;return 0;} /* fail! */ + value=skip(parse_value(child,skip(value+1))); /* skip any spacing, get the value. */ + if (!value) return 0; + } + + if (*value=='}') return value+1; /* end of array */ + ep=value;return 0; /* malformed. */ +} + +/* Render an object to text. */ +static char *print_object(cJSON *item,int depth,int fmt) +{ + char **entries=0,**names=0; + char *out=0,*ptr,*ret,*str;int len=7,i=0,j; + cJSON *child=item->child; + int numentries=0,fail=0; + /* Count the number of entries. */ + while (child) numentries++,child=child->next; + /* Allocate space for the names and the objects */ + entries=(char**)cJSON_malloc(numentries*sizeof(char*)); + if (!entries) return 0; + names=(char**)cJSON_malloc(numentries*sizeof(char*)); + if (!names) {cJSON_free(entries);return 0;} + memset(entries,0,sizeof(char*)*numentries); + memset(names,0,sizeof(char*)*numentries); + + /* Collect all the results into our arrays: */ + child=item->child;depth++;if (fmt) len+=depth; + while (child) + { + names[i]=str=print_string_ptr(child->string); + entries[i++]=ret=print_value(child,depth,fmt); + if (str && ret) len+=strlen(ret)+strlen(str)+2+(fmt?2+depth:0); else fail=1; + child=child->next; + } + + /* Try to allocate the output string */ + if (!fail) out=(char*)cJSON_malloc(len); + if (!out) fail=1; + + /* Handle failure */ + if (fail) + { + for (i=0;ichild;int i=0;while(c)i++,c=c->next;return i;} +cJSON *cJSON_GetArrayItem(cJSON *array,int item) {cJSON *c=array->child; while (c && item>0) item--,c=c->next; return c;} +cJSON *cJSON_GetObjectItem(cJSON *object,const char *string) {cJSON *c=object->child; while (c && cJSON_strcasecmp(c->string,string)) c=c->next; return c;} + +/* Utility for array list handling. */ +static void suffix_object(cJSON *prev,cJSON *item) {prev->next=item;item->prev=prev;} +/* Utility for handling references. */ +static cJSON *create_reference(cJSON *item) {cJSON *ref=cJSON_New_Item();if (!ref) return 0;memcpy(ref,item,sizeof(cJSON));ref->string=0;ref->type|=cJSON_IsReference;ref->next=ref->prev=0;return ref;} + +/* Add item to array/object. */ +void cJSON_AddItemToArray(cJSON *array, cJSON *item) {cJSON *c=array->child;if (!item) return; if (!c) {array->child=item;} else {while (c && c->next) c=c->next; suffix_object(c,item);}} +void cJSON_AddItemToObject(cJSON *object,const char *string,cJSON *item) {if (!item) return; if (item->string) cJSON_free(item->string);item->string=cJSON_strdup(string);cJSON_AddItemToArray(object,item);} +void cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item) {cJSON_AddItemToArray(array,create_reference(item));} +void cJSON_AddItemReferenceToObject(cJSON *object,const char *string,cJSON *item) {cJSON_AddItemToObject(object,string,create_reference(item));} + +cJSON *cJSON_DetachItemFromArray(cJSON *array,int which) {cJSON *c=array->child;while (c && which>0) c=c->next,which--;if (!c) return 0; + if (c->prev) c->prev->next=c->next;if (c->next) c->next->prev=c->prev;if (c==array->child) array->child=c->next;c->prev=c->next=0;return c;} +void cJSON_DeleteItemFromArray(cJSON *array,int which) {cJSON_Delete(cJSON_DetachItemFromArray(array,which));} +cJSON *cJSON_DetachItemFromObject(cJSON *object,const char *string) {int i=0;cJSON *c=object->child;while (c && cJSON_strcasecmp(c->string,string)) i++,c=c->next;if (c) return cJSON_DetachItemFromArray(object,i);return 0;} +void cJSON_DeleteItemFromObject(cJSON *object,const char *string) {cJSON_Delete(cJSON_DetachItemFromObject(object,string));} + +/* Replace array/object items with new ones. */ +void cJSON_ReplaceItemInArray(cJSON *array,int which,cJSON *newitem) {cJSON *c=array->child;while (c && which>0) c=c->next,which--;if (!c) return; + newitem->next=c->next;newitem->prev=c->prev;if (newitem->next) newitem->next->prev=newitem; + if (c==array->child) array->child=newitem; else newitem->prev->next=newitem;c->next=c->prev=0;cJSON_Delete(c);} +void cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem){int i=0;cJSON *c=object->child;while(c && cJSON_strcasecmp(c->string,string))i++,c=c->next;if(c){newitem->string=cJSON_strdup(string);cJSON_ReplaceItemInArray(object,i,newitem);}} + +/* Create basic types: */ +cJSON *cJSON_CreateNull() {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_NULL;return item;} +cJSON *cJSON_CreateTrue() {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_True;return item;} +cJSON *cJSON_CreateFalse() {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_False;return item;} +cJSON *cJSON_CreateBool(int b) {cJSON *item=cJSON_New_Item();if(item)item->type=b?cJSON_True:cJSON_False;return item;} +cJSON *cJSON_CreateNumber(double num) {cJSON *item=cJSON_New_Item();if(item){item->type=cJSON_Number;item->valuedouble=num;item->valueint=(int)num;}return item;} +cJSON *cJSON_CreateString(const char *string) {cJSON *item=cJSON_New_Item();if(item){item->type=cJSON_String;item->valuestring=cJSON_strdup(string);}return item;} +cJSON *cJSON_CreateArray() {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_Array;return item;} +cJSON *cJSON_CreateObject() {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_Object;return item;} + +/* Create Arrays: */ +cJSON *cJSON_CreateIntArray(int *numbers,int count) {int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;a && ichild=n;else suffix_object(p,n);p=n;}return a;} +cJSON *cJSON_CreateFloatArray(float *numbers,int count) {int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;a && ichild=n;else suffix_object(p,n);p=n;}return a;} +cJSON *cJSON_CreateDoubleArray(double *numbers,int count) {int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;a && ichild=n;else suffix_object(p,n);p=n;}return a;} +cJSON *cJSON_CreateStringArray(const char **strings,int count) {int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;a && ichild=n;else suffix_object(p,n);p=n;}return a;} diff --git a/linux_usr/cJSON.h b/linux_usr/cJSON.h new file mode 100644 index 0000000..b7a6cd3 --- /dev/null +++ b/linux_usr/cJSON.h @@ -0,0 +1,127 @@ +/* + Copyright (c) 2009 Dave Gamble + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ + +#ifndef cJSON__h +#define cJSON__h + +#ifdef __cplusplus +extern "C" +{ +#endif + +/* cJSON Types: */ +#define cJSON_False 0 +#define cJSON_True 1 +#define cJSON_NULL 2 +#define cJSON_Number 3 +#define cJSON_String 4 +#define cJSON_Array 5 +#define cJSON_Object 6 + +#define cJSON_IsReference 256 + +/* The cJSON structure: */ +typedef struct cJSON { + struct cJSON *next,*prev; /* next/prev allow you to walk array/object chains. Alternatively, use GetArraySize/GetArrayItem/GetObjectItem */ + struct cJSON *child; /* An array or object item will have a child pointer pointing to a chain of the items in the array/object. */ + + int type; /* The type of the item, as above. */ + + char *valuestring; /* The item's string, if type==cJSON_String */ + int valueint; /* The item's number, if type==cJSON_Number */ + double valuedouble; /* The item's number, if type==cJSON_Number */ + + char *string; /* The item's name string, if this item is the child of, or is in the list of subitems of an object. */ +} cJSON; + +typedef struct cJSON_Hooks { + void *(*malloc_fn)(size_t sz); + void (*free_fn)(void *ptr); +} cJSON_Hooks; + +/* Supply malloc, realloc and free functions to cJSON */ +extern void cJSON_InitHooks(cJSON_Hooks* hooks); + + +/* Supply a block of JSON, and this returns a cJSON object you can interrogate. Call cJSON_Delete when finished. */ +extern cJSON *cJSON_Parse(const char *value); +/* Render a cJSON entity to text for transfer/storage. Free the char* when finished. */ +extern char *cJSON_Print(cJSON *item); +/* Render a cJSON entity to text for transfer/storage without any formatting. Free the char* when finished. */ +extern char *cJSON_PrintUnformatted(cJSON *item); +/* Delete a cJSON entity and all subentities. */ +extern void cJSON_Delete(cJSON *c); + +/* Returns the number of items in an array (or object). */ +extern int cJSON_GetArraySize(cJSON *array); +/* Retrieve item number "item" from array "array". Returns NULL if unsuccessful. */ +extern cJSON *cJSON_GetArrayItem(cJSON *array,int item); +/* Get item "string" from object. Case insensitive. */ +extern cJSON *cJSON_GetObjectItem(cJSON *object,const char *string); + +/* For analysing failed parses. This returns a pointer to the parse error. You'll probably need to look a few chars back to make sense of it. Defined when cJSON_Parse() returns 0. 0 when cJSON_Parse() succeeds. */ +extern const char *cJSON_GetErrorPtr(); + +/* These calls create a cJSON item of the appropriate type. */ +extern cJSON *cJSON_CreateNull(); +extern cJSON *cJSON_CreateTrue(); +extern cJSON *cJSON_CreateFalse(); +extern cJSON *cJSON_CreateBool(int b); +extern cJSON *cJSON_CreateNumber(double num); +extern cJSON *cJSON_CreateString(const char *string); +extern cJSON *cJSON_CreateArray(); +extern cJSON *cJSON_CreateObject(); + +/* These utilities create an Array of count items. */ +extern cJSON *cJSON_CreateIntArray(int *numbers,int count); +extern cJSON *cJSON_CreateFloatArray(float *numbers,int count); +extern cJSON *cJSON_CreateDoubleArray(double *numbers,int count); +extern cJSON *cJSON_CreateStringArray(const char **strings,int count); + +/* Append item to the specified array/object. */ +extern void cJSON_AddItemToArray(cJSON *array, cJSON *item); +extern void cJSON_AddItemToObject(cJSON *object,const char *string,cJSON *item); +/* Append reference to item to the specified array/object. Use this when you want to add an existing cJSON to a new cJSON, but don't want to corrupt your existing cJSON. */ +extern void cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item); +extern void cJSON_AddItemReferenceToObject(cJSON *object,const char *string,cJSON *item); + +/* Remove/Detatch items from Arrays/Objects. */ +extern cJSON *cJSON_DetachItemFromArray(cJSON *array,int which); +extern void cJSON_DeleteItemFromArray(cJSON *array,int which); +extern cJSON *cJSON_DetachItemFromObject(cJSON *object,const char *string); +extern void cJSON_DeleteItemFromObject(cJSON *object,const char *string); + +/* Update array items. */ +extern void cJSON_ReplaceItemInArray(cJSON *array,int which,cJSON *newitem); +extern void cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem); + +#define cJSON_AddNullToObject(object,name) cJSON_AddItemToObject(object, name, cJSON_CreateNull()) +#define cJSON_AddTrueToObject(object,name) cJSON_AddItemToObject(object, name, cJSON_CreateTrue()) +#define cJSON_AddFalseToObject(object,name) cJSON_AddItemToObject(object, name, cJSON_CreateFalse()) +#define cJSON_AddNumberToObject(object,name,n) cJSON_AddItemToObject(object, name, cJSON_CreateNumber(n)) +#define cJSON_AddStringToObject(object,name,s) cJSON_AddItemToObject(object, name, cJSON_CreateString(s)) + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/linux_usr/v3_register_gm.c b/linux_usr/v3_register_gm.c new file mode 100644 index 0000000..50411b2 --- /dev/null +++ b/linux_usr/v3_register_gm.c @@ -0,0 +1,154 @@ +/* + * V3 Guarded Module registration utility + * + * This code allows a user to register a + * guest driver module to be guarded upon + * injection into a guest. * + * (c) Kyle C. Hale, 2012 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "v3_ctrl.h" +#include "iface-guard-mods.h" +#include "cJSON.h" + +#define SET_PRIV(x, i) ((x) |= 1U << (i)) + + +/* Parse text to JSON, then render back to text, and print! */ +static void +populate_gm (char * filename, struct v3_guard_mod * m) +{ + int fd, i, nents, nrets; + char * data; + struct stat stats; + cJSON *json, *tmp, *tmp2, *ep, *sub_ep; + + fd = open(filename, O_RDONLY); + if (fd < 0) { + fprintf(stderr, "Error opening file: %s\n", filename); + exit(0); + } + + if (fstat(fd, &stats) == -1) { + fprintf(stderr, "Error stating file: %s\n", filename); + exit(0); + } + + data = malloc(stats.st_size); + v3_read_file(fd, stats.st_size, data); + close(fd); + + json = cJSON_Parse(data); + + if (!json) { + fprintf(stderr, "Error before: [%s]\n",cJSON_GetErrorPtr()); + goto out; + } + + m->name = cJSON_Print(cJSON_GetObjectItem(json, "module_name")); + m->content_hash = cJSON_Print(cJSON_GetObjectItem(json, "content_hash")); + + tmp = cJSON_GetObjectItem(json, "size"); + m->text_size = tmp->valueint; + + tmp = cJSON_GetObjectItem(json, "hcall_offset"); + m->hcall_offset = tmp->valueint; + + /* extract all the valid entry points */ + tmp = cJSON_GetObjectItem(json, "entry_points"); + nents = cJSON_GetArraySize(tmp); + + tmp2 = cJSON_GetObjectItem(json, "ret_points"); + nrets = cJSON_GetArraySize(tmp2); + + + m->num_entries = nents + nrets; + printf("num entries: %d, nents: %d, nrets: %d\n", m->num_entries, nents, nrets); + m->entry_points = malloc(sizeof(struct v3_entry_point)*m->num_entries); + + for (i = 0; i < nents; i++) { + ep = cJSON_GetArrayItem(tmp, i); + sub_ep = cJSON_GetArrayItem(ep, 0); + + m->entry_points[i].name = cJSON_Print(sub_ep); + m->entry_points[i].is_ret = 0; + + sub_ep = cJSON_GetArrayItem(ep, 1); + m->entry_points[i].offset = sub_ep->valueint; + } + + + for (i = nents; i < m->num_entries; i++) { + ep = cJSON_GetArrayItem(tmp2, i - nents); + sub_ep = cJSON_GetArrayItem(ep, 0); + + m->entry_points[i].name = cJSON_Print(sub_ep); + m->entry_points[i].is_ret = 1; + + sub_ep = cJSON_GetArrayItem(ep, 1); + m->entry_points[i].offset = sub_ep->valueint; + } + + tmp = cJSON_GetObjectItem(json, "privileges"); + m->num_privs = cJSON_GetArraySize(tmp); + m->priv_array = malloc(sizeof(char*)*m->num_privs); + if (!m->priv_array) { + fprintf(stderr, "Problem allocating privilege array in userspace\n"); + goto out; + } + + for (i = 0; i < m->num_privs; i++) { + ep = cJSON_GetArrayItem(tmp, i); + m->priv_array[i] = cJSON_Print(ep); + } + +out: + cJSON_Delete(json); + free(data); +} + + +int main (int argc, char **argv) { + struct v3_guard_mod mod; + char *dev_file, *json_file; + uint64_t ret; + + if (argc < 2) { + v3_usage(" \n"); + return -1; + } + + dev_file = argv[1]; + json_file = argv[2]; + + populate_gm(json_file, &mod); + + printf("Registering guarded module: %s, size: %d, offset: %d\n", mod.name, mod.text_size, mod.hcall_offset); + + mod.id = 0; + + ret = v3_vm_ioctl(dev_file, V3_VM_REGISTER_MOD, &mod); + + if (ret < 0) { + fprintf(stderr, "Problem registering module\n"); + return -1; + } + + if (!mod.id) { + fprintf(stderr, "Could not register guarded module\n"); + } else { + printf("Module successfully registered [0x%llx]\n", mod.id); + } + + return 0; +} diff --git a/palacios/include/gears/guard_mods.h b/palacios/include/gears/guard_mods.h new file mode 100644 index 0000000..806e550 --- /dev/null +++ b/palacios/include/gears/guard_mods.h @@ -0,0 +1,101 @@ +/* + * This file is part of the Palacios Virtual Machine Monitor developed + * by the V3VEE Project with funding from the United States National + * Science Foundation and the Department of Energy. + * + * The V3VEE Project is a joint project between Northwestern University + * and the University of New Mexico. You can find out more at + * http://www.v3vee.org + * + * Copyright (c) 2012, Kyle C. Hale + * Copyright (c) 2012, The V3VEE Project + * All rights reserved. + * + * Author: Kyle C. Hale + * + * This is free software. You are permitted to use, + * redistribute, and modify it as specified in the file "V3VEE_LICENSE". + */ + +#ifndef __GUARD_MODS_H__ +#define __GUARD_MODS_H__ + + +#ifdef __V3VEE__ + +#define HCALL_INSTR_LEN 3 +#define MAX_BORDER_NESTING 128 + +enum v3_mod_state { + INIT, + PRIV, + NO_PRIV, +}; + +/* these need to be updated when + * load address is received */ +struct v3_entry_point { + char * name; + addr_t addr; + uint8_t is_ret; /* indicates that this is a return point */ + + struct v3_gm * gm; + struct list_head er_node; +}; + + +struct v3_gm { + char * name; + char * content_hash; + ulong_t stack_hash; + uint_t num_entries; + uint_t hcall_offset; + uint_t text_size; + addr_t load_addr; /* this is a GVA */ + ullong_t id; + struct v3_entry_point * entry_points; + enum v3_mod_state state; + struct list_head priv_list; /* list of privileges associated with this GM */ + struct list_head mod_node; + struct list_head er_list; + void * private_data; + int callback_nesting; + int kernel_nesting; + addr_t r11_stack_kernel[MAX_BORDER_NESTING]; + addr_t r11_stack_callback[MAX_BORDER_NESTING]; + addr_t entry_rsp; + addr_t exit_rsp; + +}; + +#define V3_GUARD_INIT_HCALL 0x6000 +#define V3_BIN_CALL_HCALL 0x6001 +#define V3_BOUT_RET_HCALL 0x6002 +#define V3_BOUT_CALL_HCALL 0x6003 +#define V3_BIN_RET_HCALL 0x6004 + +struct v3_guarded_mods { + struct list_head mod_list; + struct hashtable * mod_id_table; + struct hashtable * mod_name_table; + struct hashtable * er_hash; /* hash for quick lookups on valid entries/returns */ +}; + + +#endif /* __V3VEE__ */ + +unsigned long long +v3_register_gm (void * vm, + char * name, + char * hash, + unsigned int hc_off, + unsigned int size, + unsigned int nentries, + unsigned int nprivs, + char ** priv_array, + void * private_data, + void * entry_points); + + +#endif /* __GUARD_MODS_H__ */ + diff --git a/palacios/include/gears/privilege.h b/palacios/include/gears/privilege.h new file mode 100644 index 0000000..4f3ca53 --- /dev/null +++ b/palacios/include/gears/privilege.h @@ -0,0 +1,52 @@ +#ifndef __PRIVILEGE_H__ +#define __PRIVILEGE_H__ + + +struct v3_privileges { + + struct list_head priv_list; + struct hashtable * priv_table; + +}; + +struct v3_priv { + const char * name; + + + int (*init) (struct guest_info * core, void ** private_data); + int (*lower) (struct guest_info * core, void * private_data); + int (*raise) (struct guest_info * core, void * private_data); + int (*deinit) (struct guest_info * core, void * private_data); + + void * private_data; + + struct list_head priv_node; + +}; + +struct v3_priv * v3_lookup_priv (struct v3_vm_info * vm, const char * name); + +int v3_core_raise_all_privs (struct guest_info * core); +int v3_core_lower_all_privs (struct guest_info * core); + +int v3_bind_privilege (struct guest_info * core, + const char * priv_name, + int (*init) (struct guest_info * core, void ** private_data), + int (*lower) (struct guest_info * core, void * private_data), + int (*raise) (struct guest_info * core, void * private_data), + int (*deinit) (struct guest_info * core, void * private_data), + void * priv_data); + + +/* + +#define register_privilege(priv) \ + static struct v3_priv * _v3_priv \ + __attribute__((used)) \ + __attribute__((unused, __section__("_v3_privileges"),\ + aligned(sizeof(addr_t)))) \ + = priv; + +*/ + +#endif diff --git a/palacios/src/gears/Kconfig b/palacios/src/gears/Kconfig index 46db427..1961d69 100644 --- a/palacios/src/gears/Kconfig +++ b/palacios/src/gears/Kconfig @@ -7,6 +7,7 @@ config GEARS This is the Guest Examination and Revision Services, an extension used to modify guest code and implement guest-context VMM services in Palacios + config EXT_SW_INTERRUPTS bool "Enable interception and hooking of software interrupts" depends on GEARS @@ -101,4 +102,35 @@ config EXT_CODE_INJECT This option will allow code to be injected and run in the guest context +config EXT_PRIV + bool "Allow privileges to be given to code modules" + depends on GEARS + default n + help + This option allows different code modules in the guest to be + given privileges such as uninhibited access to devices etc. + +config DEBUG_EXT_PRIV + bool "Allow privileged module debugging" + depends on EXT_PRIV + default n + help + Turns on debugging output for privilege extension + +config EXT_GUARD_MODULES + bool "Allow guarded execution of guest modules" + depends on GEARS + depends on EXT_PRIV + default n + help + This option allows priveledge hardware access to kernel drivers/modules + in the guest. Requires instrumentation of guest driver/module. + +config DEBUG_EXT_GUARD_MODS + bool "Allow guarded module debugging" + depends on EXT_GUARD_MODULES + default n + help + Turns on debugging output for guarded modules extension + endmenu diff --git a/palacios/src/gears/Makefile b/palacios/src/gears/Makefile index 001b043..8de6c26 100644 --- a/palacios/src/gears/Makefile +++ b/palacios/src/gears/Makefile @@ -4,6 +4,8 @@ obj-$(V3_CONFIG_EXT_EXECVE_HOOK) += ext_execve_hook.o obj-$(V3_CONFIG_EXT_EXECVE_HOOK) += ext_process_environment.o obj-$(V3_CONFIG_EXT_CODE_INJECT) += ext_code_inject.o obj-$(V3_CONFIG_EXT_ENV_INJECT) += ext_env_inject.o +obj-$(V3_CONFIG_EXT_GUARD_MODULES) += ext_guard_mods.o +obj-$(V3_CONFIG_EXT_PRIV) += ext_priv.o obj-y += null.o diff --git a/palacios/src/gears/ext_guard_mods.c b/palacios/src/gears/ext_guard_mods.c new file mode 100644 index 0000000..0923255 --- /dev/null +++ b/palacios/src/gears/ext_guard_mods.c @@ -0,0 +1,634 @@ +/* + * This file is part of the Palacios Virtual Machine Monitor developed + * by the V3VEE Project with funding from the United States National + * Science Foundation and the Department of Energy. + * + * The V3VEE Project is a joint project between Northwestern University + * and the University of New Mexico. You can find out more at + * http://www.v3vee.org + * + * Copyright (c) 2012, Kyle C. Hale + * Copyright (c) 2012, The V3VEE Project + * All rights reserved. + * + * Author: Kyle C. Hale + * + * This is free software. You are permitted to use, + * redistribute, and modify it as specified in the file "V3VEE_LICENSE". + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef V3_CONFIG_DEBUG_EXT_GUARD_MODS +#undef PrintDebug +#define PrintDebug(fmt, args...) +#endif + + +static struct v3_guarded_mods global_mods; + +static struct v3_gm * current_gm; + +/* generates an id for a module using + * the TSC and a hash */ +static inline void +gen_mod_id (ullong_t * id) +{ + ullong_t val, ret; + rdtscll(val); + ret = (ullong_t)v3_hash_long((ulong_t)val, sizeof(ullong_t)*8); + PrintDebug(VM_NONE, VCORE_NONE,"GM: Generating new module ID: 0x%llx\n", ret); + *id = ret; +} + + +static inline void +free_mod (struct v3_gm * mod) +{ + struct v3_priv * p = NULL; + struct v3_priv * tmp = NULL; + struct v3_entry_point * er = NULL; + struct v3_entry_point * etmp = NULL; + int i; + + /* free the privilege list */ + list_for_each_entry_safe(p, tmp, &(mod->priv_list), priv_node) { + list_del(&(p->priv_node)); + V3_Free(p); + } + + V3_Free(mod->name); + V3_Free(mod->content_hash); + + list_for_each_entry_safe(er, etmp, &(mod->er_list), er_node) { + list_del(&er->er_node); + } + + for (i = 0; i < mod->num_entries; i++) { + V3_Free(mod->entry_points[i].name); + } + + V3_Free(mod->entry_points); + V3_Free(mod); +} + + +static int +add_privs_to_list (struct v3_vm_info * vm, + struct list_head * list, + unsigned int nprivs, + char ** priv_array) +{ + struct v3_priv * priv = NULL; + int i; + + for (i = 0 ; i < nprivs; i++) { + if ((priv = v3_lookup_priv(vm, priv_array[i])) == NULL) { + PrintError(VM_NONE, VCORE_NONE,"Guarded module requested non-existent privilege: %s\n", priv_array[i]); + return -1; + } else { + list_add(&(priv->priv_node), list); + } + } + + return 0; +} + + +static uint_t +mod_name_hash_fn (addr_t key) +{ + char * name = (char*)key; + return v3_hash_buffer((uint8_t*)name, strlen(name)); +} + + +static int +mod_name_eq_fn (addr_t key1, addr_t key2) +{ + char * name1 = (char*)key1; + char * name2 = (char*)key2; + return (strcmp(name1, name2) == 0); +} + + +static uint_t +mod_id_hash_fn (addr_t key) +{ + return v3_hash_long(key, sizeof(ullong_t) * 8); +} + + +static int +mod_id_eq_fn (addr_t key1, addr_t key2) +{ + return (key1 == key2); +} + + +static uint_t +entry_addr_hash_fn (addr_t key) +{ + return v3_hash_long(key, sizeof(addr_t) * 8); +} + + +static int +entry_addr_eq_fn (addr_t key1, addr_t key2) +{ + return (key1 == key2); +} + + +/* GUARD INIT + * + * This is invoked through a hypercall that has been instrumented in the + * guarded module's routine denoted by the module_init macro. + */ +/* TODO: add text hash check here */ +static int +guard_init (struct guest_info * core, unsigned int hcall_id, void * priv_data) +{ + ullong_t mod_id; + struct v3_gm * gm; + struct v3_guarded_mods * mods = &global_mods; + struct v3_entry_point * er = NULL; + struct v3_entry_point * tmp = NULL; + struct v3_priv * priv = NULL; + struct v3_priv * ptmp = NULL; + + /* the guarded module should provide its ID in RBX */ + mod_id = core->vm_regs.rbx; + + V3_Print(VM_NONE, VCORE_NONE, "Received init request from GM (id=0x%llx)\n", mod_id); + + /* check if the corresponding module exists */ + if ((gm = (struct v3_gm*)v3_htable_search(mods->mod_id_table, (addr_t)mod_id)) == 0) { + PrintError(VM_NONE, VCORE_NONE,"Module (id=0x%llx) not found\n", mod_id); + return -1; + } + + PrintDebug(VM_NONE, VCORE_NONE,"GM: initializing guarded module %s\n", gm->name); + + /* infer load address */ + gm->load_addr = core->rip - HCALL_INSTR_LEN - gm->hcall_offset; + + PrintDebug(VM_NONE, VCORE_NONE,"\tNew GM load address: %p\n", (void*)gm->load_addr); + + list_for_each_entry_safe(er, tmp, &(gm->er_list), er_node) { + + /* fix up the dynamic address of the entry point */ + er->addr += gm->load_addr; + + /* check if this entry point already exists */ + if (v3_htable_search(mods->er_hash, er->addr)) { + PrintError(VM_NONE, VCORE_NONE,"GM: Error! Entry point already exists!\n"); + return -1; + } + + /* add the new entry point to the global hash */ + v3_htable_insert(mods->er_hash, er->addr, (addr_t)er); + } + + PrintDebug(VM_NONE, VCORE_NONE,"GM: Raising privileges\n"); + + + /* module will come up in privileged mode */ + gm->state = PRIV; + + /* raise all of the module's privileges */ + list_for_each_entry_safe(priv, ptmp, &(gm->priv_list), priv_node) { + if (priv->raise(core, priv->private_data) < 0) { + PrintError(VM_NONE, VCORE_NONE,"GM: Problem raising privilege\n"); + return -1; + } + } + + /* get the entry RSP for integrity checks */ + addr_t hva; + if (v3_gva_to_hva(core, + get_addr_linear(core, (addr_t)core->vm_regs.rsp, &(core->segments.ss)), + &hva) < 0) { + PrintError(VM_NONE, VCORE_NONE,"GM: Problem translating stack address\n"); + return -1; + } + gm->entry_rsp = hva; + + /* this is just to be consistent with other entry types, r11 isn't used in init */ + gm->r11_stack_callback[gm->callback_nesting] = core->vm_regs.r11; + gm->callback_nesting++; + + PrintDebug(VM_NONE, VCORE_NONE,"GM: Guarded module initialized, set to PRIV\n"); + + current_gm = gm; + + return 0; +} + + +/* BORDER IN CALL + * + * This is invoked when the kernel calls the module (through a callback function) + * + * TODO: the stack checking model here is crappy, but will be updated to only + * look at frame pointers and return addresses. + */ +static int +border_in_call (struct guest_info * core, unsigned int hcall_id, void * priv_data) +{ + struct v3_guarded_mods * mods = &global_mods; + struct v3_entry_point * er; + struct v3_gm * gm = current_gm; + struct v3_priv * priv = NULL; + struct v3_priv * tmp = NULL; + + + addr_t entry = core->rip - HCALL_INSTR_LEN; + + er = (struct v3_entry_point*)v3_htable_search(mods->er_hash, entry); + if (!er) { + PrintError(VM_NONE, VCORE_NONE,"Attempt to enter at an invalid entry point (%p)\n", (void*)entry); + } else { + + gm = er->gm; + + /* if this is first entry, record rsp */ + if (gm->callback_nesting == 0) { + addr_t hva; + if (v3_gva_to_hva(core, + get_addr_linear(core, (addr_t)core->vm_regs.rsp, &core->segments.ss), + &hva) < 0) { + PrintError(VM_NONE, VCORE_NONE,"GM: Problem translating stack address\n"); + return -1; + } + gm->entry_rsp = hva; + } + + if (gm->callback_nesting >= MAX_BORDER_NESTING) { + PrintError(VM_NONE, VCORE_NONE,"GM: Too much nesting of border crossings\n"); + return -1; + } else { + gm->r11_stack_callback[gm->callback_nesting] = core->vm_regs.r11; + gm->callback_nesting++; + } + + if (gm->state == INIT) { + PrintError(VM_NONE, VCORE_NONE,"ERROR: module reached guard entry without being initialized!\n"); + return 0; + } + + gm->state = PRIV; + + PrintDebug(VM_NONE, VCORE_NONE,"Entry request at %p granted to GM (%s)\n", (void*)entry, gm->name); + + list_for_each_entry_safe(priv, tmp, &(gm->priv_list), priv_node) { + + if (priv->raise(core, priv->private_data) < 0) { + PrintError(VM_NONE, VCORE_NONE,"GM: Problem raising privilege\n"); + return -1; + } + + } + + } + + return 0; +} + + +/* BORDER-OUT RETURN + * + * This is invoked when the module returns to the kernel, after being invoked through + * a callback function. + */ +static int +border_out_return (struct guest_info * core, unsigned int hcall_id, void * priv_data) +{ + struct v3_gm * gm = current_gm; + struct v3_priv * priv = NULL; + struct v3_priv * tmp = NULL; + + gm->callback_nesting--; + + /* restore the guest's return address */ + core->vm_regs.r11 = gm->r11_stack_callback[gm->callback_nesting]; + gm->r11_stack_callback[gm->callback_nesting] = 0; + + /* Only lower privilege when we reach nesting 0 */ + if (gm->callback_nesting <= 0) { + + PrintDebug(VM_NONE, VCORE_NONE,"GM: Granting exit request to GM (%s)\n", gm->name); + list_for_each_entry_safe(priv, tmp, &(gm->priv_list), priv_node) { + + if (priv->lower(core, priv->private_data) < 0) { + PrintError(VM_NONE, VCORE_NONE,"GM: Problem lowering privilege\n"); + return -1; + } + + } + + gm->state = NO_PRIV; + + } else { + PrintDebug(VM_NONE, VCORE_NONE,"Priv Lower requested, but still at nesting level (%d)\n", gm->callback_nesting); + } + + return 0; +} + + +/* BORDER-OUT CALL + * + * This is invoked when the module calls out to the kernel, results in lowering of privilege + * + */ +static int +border_out_call (struct guest_info * core, unsigned int hcall_id, void * priv_data) { + + struct v3_gm * gm = current_gm; + struct v3_priv * priv = NULL; + struct v3_priv * tmp = NULL; + + /* TODO: um...we seem to be getting a border out call in NO_PRIV state */ + if (1 || gm->state == PRIV || gm->state == INIT) { + + gm->r11_stack_kernel[gm->kernel_nesting] = core->vm_regs.r11; + gm->kernel_nesting++; + + PrintDebug(VM_NONE, VCORE_NONE,"GM: Granting border out call request to GM (%s)\n", gm->name); + list_for_each_entry_safe(priv, tmp, &(gm->priv_list), priv_node) { + if (priv->lower(core, priv->private_data) < 0) { + PrintError(VM_NONE, VCORE_NONE,"GM: Problem lowering privilege\n"); + return -1; + } + + } + + if (v3_gva_to_hva(core, + get_addr_linear(core, (addr_t)core->vm_regs.rsp, &(core->segments.ss)), + &gm->exit_rsp) < 0) { + PrintError(VM_NONE, VCORE_NONE,"GM: border out, error translating rsp addr\n"); + return -1; + } + + if (gm->entry_rsp && gm->exit_rsp && gm->state == PRIV && (gm->entry_rsp >= gm->exit_rsp)) { + gm->stack_hash = v3_hash_buffer((uchar_t*)gm->exit_rsp, gm->entry_rsp - gm->exit_rsp); + } + + gm->state = NO_PRIV; + + } else { + PrintError(VM_NONE, VCORE_NONE,"GM: Trying to run border-out without privilege\n"); + } + + /* else, do nothing */ + return 0; +} + + +/* BORDER-IN RETURN + * + * This is invoked when the kernel returns control to the module after a border-out call + * Results in privilege raise + * + */ +static int +border_in_return (struct guest_info * core, unsigned int hcall_id, void * priv_data) { + struct v3_guarded_mods * mods = &global_mods; + struct v3_gm * gm = NULL; + struct v3_priv * priv = NULL; + struct v3_priv * tmp = NULL; + struct v3_entry_point * er = NULL; + + addr_t entry = core->rip - HCALL_INSTR_LEN; + if ((er = (struct v3_entry_point*)v3_htable_search(mods->er_hash, entry)) == 0) { + PrintError(VM_NONE, VCORE_NONE,"Attempt to enter at an invalid entry point (%p)\n", (void*)entry); + } else { + gm = er->gm; + + /* restore the guest's return address */ + gm->kernel_nesting--; + core->vm_regs.r11 = gm->r11_stack_kernel[gm->kernel_nesting]; + gm->r11_stack_kernel[gm->kernel_nesting] = 0; + + + if (gm->state == NO_PRIV) { + PrintDebug(VM_NONE, VCORE_NONE,"GM: Granting border in return request to GM (%s)\n", gm->name); + list_for_each_entry_safe(priv, tmp, &(gm->priv_list), priv_node) { + if (priv->raise(core, priv->private_data) < 0) { + PrintError(VM_NONE, VCORE_NONE,"GM: Problem raising privilege\n"); + return -1; + } + } + + if (gm->entry_rsp && gm->exit_rsp && (gm->entry_rsp > gm->exit_rsp)) { + if (v3_hash_buffer((uchar_t*)gm->exit_rsp, gm->entry_rsp - gm->exit_rsp) != gm->stack_hash) { + } + } + + gm->state = PRIV; + } + } + + return 0; +} + + +static int +init_gm (struct v3_vm_info * vm, v3_cfg_tree_t * cfg, void ** priv_data) +{ + struct v3_guarded_mods * mods = &global_mods; + + *priv_data = mods; + + INIT_LIST_HEAD(&(mods->mod_list)); + + if (!(mods->mod_name_table = v3_create_htable(0, mod_name_hash_fn, mod_name_eq_fn))) { + PrintError(VM_NONE, VCORE_NONE,"GM: Could not create GM module name hashtable\n"); + return -1; + } + + if (!(mods->mod_id_table = v3_create_htable(0, mod_id_hash_fn, mod_id_eq_fn))) { + PrintError(VM_NONE, VCORE_NONE,"GM: Could not create GM module ID hashtable\n"); + return -1; + } + + if (!(mods->er_hash = v3_create_htable(0, entry_addr_hash_fn, entry_addr_eq_fn))) { + PrintError(VM_NONE, VCORE_NONE,"GM: Could not create GM entry point hashtable\n"); + return -1; + } + + /* these hypercalls will be invoked with a module id (hash) in rax. */ + if (v3_register_hypercall(vm, V3_BIN_CALL_HCALL, border_in_call, mods) < 0 || + v3_register_hypercall(vm, V3_BOUT_RET_HCALL, border_out_return, mods) < 0 || + v3_register_hypercall(vm, V3_BIN_RET_HCALL, border_in_return, mods) < 0 || + v3_register_hypercall(vm, V3_BOUT_CALL_HCALL, border_out_call, mods) < 0 || + v3_register_hypercall(vm, V3_GUARD_INIT_HCALL, guard_init, mods) < 0) { + PrintError(VM_NONE, VCORE_NONE,"GM: Problem registering hypercalls\n"); + return -1; + } + + return 0; +} + + +static int +deinit_gm (struct v3_vm_info * vm, void * priv_data) +{ + struct v3_guarded_mods * mods = (struct v3_guarded_mods *)priv_data; + struct v3_gm * mod = NULL; + struct v3_gm * tmp = NULL; + + list_for_each_entry_safe(mod, tmp, &(mods->mod_list), mod_node) { + free_mod(mod); + } + + v3_free_htable(mods->mod_id_table, 0, 0); + v3_free_htable(mods->mod_name_table, 0, 0); + v3_free_htable(mods->er_hash, 0, 0); + + if (v3_remove_hypercall(vm, V3_BIN_CALL_HCALL) < 0 || + v3_remove_hypercall(vm, V3_BOUT_RET_HCALL) < 0 || + v3_remove_hypercall(vm, V3_BIN_RET_HCALL) < 0 || + v3_remove_hypercall(vm, V3_BOUT_CALL_HCALL) < 0 || + v3_remove_hypercall(vm, V3_GUARD_INIT_HCALL) < 0) { + + PrintError(VM_NONE, VCORE_NONE,"GM: Problem removing GM hypercalls\n"); + return -1; + } + + return 0; +} + + +/* returns 0 on error, module id on success */ +unsigned long long +v3_register_gm (void * vm, + char * name, + char * hash, + unsigned int hc_off, + unsigned int size, + unsigned int nentries, + unsigned int nprivs, + char ** priv_array, + void * private_data, + void * entry_points) +{ + struct v3_guarded_mods * mods = &global_mods; + struct v3_gm * mod; + ullong_t mod_id; + int i; + char * tmp; + + if (!name) { + PrintError(VM_NONE, VCORE_NONE,"Invalid module name\n"); + return 0; + } + + if (v3_htable_search(mods->mod_name_table, (addr_t)name)) { + PrintError(VM_NONE, VCORE_NONE,"Multiple instances of guarded module (%s)\n", name); + return 0; + } + + /* generate a unique identifier for this module */ + gen_mod_id(&mod_id); + + mod = (struct v3_gm *)V3_Malloc(sizeof(struct v3_gm)); + if (!mod) { + PrintError(VM_NONE, VCORE_NONE,"Problem allocating guarded module\n"); + return 0; + } + + memset(mod, 0, sizeof(struct v3_gm)); + + V3_Print(VM_NONE, VCORE_NONE, "V3 GM Registration: received name (%s)\n", name); + mod->name = V3_Malloc(strlen(name)+1); + if (!mod->name) { + PrintError(VM_NONE, VCORE_NONE,"Problem allocating space for mod name\n"); + return -1; + } + + V3_Print(VM_NONE, VCORE_NONE, "V3 GM Registration: received hash (%s)\n", hash); + mod->content_hash = V3_Malloc(strlen(hash)+1); + if (!mod->content_hash) { + PrintError(VM_NONE, VCORE_NONE,"Problem allocating space for content hash\n"); + return -1; + } + strcpy(mod->name, name); + strcpy(mod->content_hash, hash); + + mod->hcall_offset = hc_off; + mod->text_size = size; + mod->num_entries = nentries; + mod->private_data = private_data; + mod->id = mod_id; + mod->callback_nesting = 0; + mod->kernel_nesting = 0; + + INIT_LIST_HEAD(&(mod->priv_list)); + if (add_privs_to_list(vm, &(mod->priv_list), nprivs, priv_array) == -1) { + PrintError(VM_NONE, VCORE_NONE,"Could not add privileges to guarded module\n"); + return -1; + } + + mod->state = INIT; + mod->stack_hash = 0; + + /* setup the valid entry points */ + INIT_LIST_HEAD(&(mod->er_list)); + + mod->entry_points = V3_Malloc(sizeof(struct v3_entry_point)*mod->num_entries); + + if (!mod->entry_points) { + PrintError(VM_NONE, VCORE_NONE,"Problem allocating entry point array\n"); + return -1; + } + + memcpy(mod->entry_points, entry_points, sizeof(struct v3_entry_point)*mod->num_entries); + + /* entries are added to the list, but can't be hashed yet since we're using + * the load address as a key, but we haven't fixed these addresses up yet */ + for (i = 0; i < mod->num_entries; i++) { + tmp = V3_Malloc(strlen(mod->entry_points[i].name)+1); + if (!tmp) { + PrintError(VM_NONE, VCORE_NONE,"Problem allocating space for name locally\n"); + return -1; + } + strcpy(tmp, mod->entry_points[i].name); + mod->entry_points[i].name = tmp; + mod->entry_points[i].gm = mod; + list_add(&(mod->entry_points[i].er_node), &(mod->er_list)); + } + + //v3_htable_insert(mods->mod_id_table, mod_id, (addr_t)mod); + // TODO: this will change the content hash of the module code segment, fix! + v3_htable_insert(mods->mod_id_table, (addr_t)0xa3aeea3aeebadbad, (addr_t)mod); + v3_htable_insert(mods->mod_name_table, (addr_t)name, (addr_t)mod); + list_add(&(mod->mod_node), &(mods->mod_list)); + return mod_id; +} + + +static struct v3_extension_impl guard_mods_impl = { + .name = "guard_mods", + .vm_init = init_gm, + .vm_deinit = deinit_gm, + .core_init = NULL, + .core_deinit = NULL, + .on_entry = NULL, + .on_exit = NULL +}; + +register_extension(&guard_mods_impl); diff --git a/palacios/src/gears/ext_priv.c b/palacios/src/gears/ext_priv.c new file mode 100644 index 0000000..06eb7d9 --- /dev/null +++ b/palacios/src/gears/ext_priv.c @@ -0,0 +1,219 @@ +/* + * This file is part of the Palacios Virtual Machine Monitor developed + * by the V3VEE Project with funding from the United States National + * Science Foundation and the Department of Energy. + * + * The V3VEE Project is a joint project between Northwestern University + * and the University of New Mexico. You can find out more at + * http://www.v3vee.org + * + * Copyright (c) 2012, Kyle C. Hale + * Copyright (c) 2012, The V3VEE Project + * All rights reserved. + * + * Author: Kyle C. Hale + * + * This is free software. You are permitted to use, + * redistribute, and modify it as specified in the file "V3VEE_LICENSE". + */ + +#include +#include +#include +#include +#include +#include +#include + +#include + +#ifndef V3_CONFIG_DEBUG_EXT_PRIV +#undef PrintDebug +#define PrintDebug(fmt, args...) +#endif + +static struct v3_privileges * global_privs; + +static uint_t +priv_hash_fn (addr_t key) +{ + return v3_hash_buffer((uint8_t*)key, strlen((char*)key)); +} + + +static int +priv_eq_fn (addr_t key1, addr_t key2) +{ + return (strcmp((char*)key1, (char*)key2) == 0); +} + + +static int +init_priv (struct v3_vm_info * vm, v3_cfg_tree_t * cfg, void ** priv_data) +{ + struct v3_privileges * p = V3_Malloc(sizeof(struct v3_privileges)); + + global_privs = p; + + if (!p) { + PrintError(VM_NONE, VCORE_NONE,"Priv: Problem allocating privileges\n"); + return -1; + } + + INIT_LIST_HEAD(&(p->priv_list)); + + *priv_data = p; + + if (!(p->priv_table = v3_create_htable(0, priv_hash_fn, priv_eq_fn))) { + PrintError(VM_NONE, VCORE_NONE,"Priv: Problem creating privilege hash table\n"); + return -1; + } + + return 0; +} + + +static int +deinit_priv (struct v3_vm_info * vm, void * priv_data) +{ + struct v3_privileges * privs = (struct v3_privileges *)priv_data; + struct v3_priv * p = NULL; + struct v3_priv * tmp = NULL; + + list_for_each_entry_safe(p, tmp, &(privs->priv_list), priv_node) { + list_del(&(p->priv_node)); + V3_Free(p); + } + + v3_free_htable(privs->priv_table, 0, 0); + + return 0; +} + + +struct v3_priv * +v3_lookup_priv (struct v3_vm_info * vm, const char * name) +{ + struct v3_privileges * p = global_privs; + return ((struct v3_priv*)v3_htable_search(p->priv_table, (addr_t)name)); +} + + +static int +deinit_all_privs (struct guest_info * core, + void * priv_data, + void * core_data) +{ + struct v3_privileges * p = global_privs; + struct v3_priv * priv = NULL; + struct v3_priv * tmp = NULL; + + list_for_each_entry_safe(priv, tmp, &p->priv_list, priv_node) { + if (priv->deinit(core, priv->private_data) < 0) { + PrintError(VM_NONE, VCORE_NONE,"Problem deiniting privilege on core (%s)\n", priv->name); + } + } + + return 0; +} + + +int +v3_core_raise_all_privs (struct guest_info * core) +{ + struct v3_privileges * p = global_privs; + struct v3_priv * priv = NULL; + struct v3_priv * tmp = NULL; + + list_for_each_entry_safe(priv, tmp, &p->priv_list, priv_node) { + + if (priv->raise(core, priv->private_data) < 0) { + PrintError(VM_NONE, VCORE_NONE,"Priv: Problem lowering privilege on core (%s)\n", priv->name); + return -1; + } + + } + + return 0; +} + + +int +v3_core_lower_all_privs (struct guest_info * core) +{ + struct v3_privileges * p = global_privs; + struct v3_priv * priv = NULL; + struct v3_priv * tmp = NULL; + + list_for_each_entry_safe(priv, tmp, &p->priv_list, priv_node) { + + if (priv->lower(core, priv->private_data) < 0) { + PrintError(VM_NONE, VCORE_NONE,"Priv: Problem lowering privilege on core (%s)\n", priv->name); + return -1; + } + + } + + return 0; +} + + +int +v3_bind_privilege (struct guest_info * core, + const char * priv_name, + int (*init) (struct guest_info * core, void ** private_data), + int (*lower) (struct guest_info * core, void * private_data), + int (*raise) (struct guest_info * core, void * private_data), + int (*deinit) (struct guest_info * core, void * private_data), + void * priv_data) +{ + struct v3_privileges * p = global_privs; + struct v3_priv * priv = v3_lookup_priv(core->vm_info, priv_name); + if (priv) { + PrintError(VM_NONE, VCORE_NONE,"Priv: Privilege (%s) already exists\n", priv_name); + return -1; + } + + priv = (struct v3_priv *)V3_Malloc(sizeof(struct v3_priv)); + + if (!priv) { + PrintError(VM_NONE, VCORE_NONE,"Priv: Problem allocating privilege\n"); + return -1; + } + + PrintDebug(VM_NONE, VCORE_NONE,"Priv: Binding privilege (%s)\n", priv_name); + + priv->init = init; + priv->lower = lower; + priv->raise = raise; + priv->deinit = deinit; + priv->private_data = priv_data; + + if (v3_htable_insert(p->priv_table, (addr_t)priv_name, (addr_t)priv) == 0) { + PrintError(VM_NONE, VCORE_NONE,"Priv: Could not register privilege (%s)\n", priv_name); + return -1; + } + + if (priv->init(core, &priv->private_data) < 0) { + PrintError(VM_NONE, VCORE_NONE,"Priv: Could not initialize privilege (%s)\n", priv_name); + return -1; + } + + V3_Print(VM_NONE, VCORE_NONE, "Privilege (%s) bound and initialized successfully\n", priv_name); + + return 0; +} + + + +static struct v3_extension_impl priv_impl = { + .name = "privilege", + .vm_init = init_priv, + .vm_deinit = deinit_priv, + .core_init = NULL, + .core_deinit = deinit_all_privs, + .on_entry = NULL, + .on_exit = NULL +}; + +register_extension(&priv_impl);