Palacios Public Git Repository

To checkout Palacios execute

  git clone http://v3vee.org/palacios/palacios.web/palacios.git
This will give you the master branch. You probably want the devel branch or one of the release branches. To switch to the devel branch, simply execute
  cd palacios
  git checkout --track -b devel origin/devel
The other branches are similar.


Add guarded module infrastructure
[palacios.git] / palacios / src / gears / ext_guard_mods.c
1 /* 
2  * This file is part of the Palacios Virtual Machine Monitor developed
3  * by the V3VEE Project with funding from the United States National 
4  * Science Foundation and the Department of Energy.  
5  *
6  * The V3VEE Project is a joint project between Northwestern University
7  * and the University of New Mexico.  You can find out more at 
8  * http://www.v3vee.org
9  *
10  * Copyright (c) 2012, Kyle C. Hale <kh@u.norhtwestern.edu>
11  * Copyright (c) 2012, The V3VEE Project <http://www.v3vee.org> 
12  * All rights reserved.
13  *
14  * Author: Kyle C. Hale <kh@u.northwestern.edu>
15  *
16  * This is free software.  You are permitted to use,
17  * redistribute, and modify it as specified in the file "V3VEE_LICENSE".
18  */
19
20 #include <palacios/vmm.h>
21 #include <palacios/vm_guest.h>
22 #include <palacios/vm_guest_mem.h>
23 #include <palacios/vmm_extensions.h>
24 #include <palacios/vmm_decoder.h>
25 #include <palacios/vmm_types.h>
26 #include <palacios/vmm_hypercall.h>
27 #include <palacios/vmm_hashtable.h>
28 #include <palacios/vmm_util.h>
29 #include <palacios/vmm_mem_hook.h>
30 #include <palacios/vmcb.h>
31 #include <gears/privilege.h>
32 #include <gears/guard_mods.h>
33
34 #ifndef V3_CONFIG_DEBUG_EXT_GUARD_MODS
35 #undef PrintDebug
36 #define PrintDebug(fmt, args...)
37 #endif
38
39
40 static struct v3_guarded_mods global_mods;
41
42 static struct v3_gm * current_gm;
43
44 /* generates an id for a module using
45  * the TSC and a hash */
46 static inline void 
47 gen_mod_id (ullong_t * id) 
48 {
49     ullong_t val, ret;
50     rdtscll(val);
51     ret = (ullong_t)v3_hash_long((ulong_t)val, sizeof(ullong_t)*8);
52     PrintDebug(VM_NONE, VCORE_NONE,"GM: Generating new module ID: 0x%llx\n", ret);
53     *id = ret;
54 }
55
56
57 static inline void 
58 free_mod (struct v3_gm * mod) 
59 {
60     struct v3_priv * p = NULL;
61     struct v3_priv * tmp = NULL;
62     struct v3_entry_point * er = NULL;
63     struct v3_entry_point * etmp = NULL;
64     int i;
65
66     /* free the privilege list */
67     list_for_each_entry_safe(p, tmp, &(mod->priv_list), priv_node) {
68         list_del(&(p->priv_node));
69         V3_Free(p);
70     }
71
72     V3_Free(mod->name);
73     V3_Free(mod->content_hash);
74
75     list_for_each_entry_safe(er, etmp, &(mod->er_list), er_node) {
76         list_del(&er->er_node);
77     }
78
79     for (i = 0; i < mod->num_entries; i++) {
80         V3_Free(mod->entry_points[i].name);
81     }
82
83     V3_Free(mod->entry_points);
84     V3_Free(mod);
85 }
86
87
88 static int 
89 add_privs_to_list (struct v3_vm_info * vm,
90                    struct list_head * list, 
91                    unsigned int nprivs,
92                    char ** priv_array)
93 {
94     struct v3_priv * priv = NULL;
95     int i;
96
97     for (i = 0 ; i < nprivs; i++) {
98         if ((priv = v3_lookup_priv(vm, priv_array[i])) == NULL) {
99             PrintError(VM_NONE, VCORE_NONE,"Guarded module requested non-existent privilege: %s\n", priv_array[i]);
100             return -1;
101         } else {
102             list_add(&(priv->priv_node), list);
103         }
104     }
105     
106     return 0;
107 }
108
109
110 static uint_t 
111 mod_name_hash_fn (addr_t key)
112 {
113     char * name = (char*)key;
114     return v3_hash_buffer((uint8_t*)name, strlen(name));
115 }
116
117
118 static int 
119 mod_name_eq_fn (addr_t key1, addr_t key2)
120 {
121     char * name1 = (char*)key1;
122     char * name2 = (char*)key2;
123     return (strcmp(name1, name2) == 0);
124 }
125
126
127 static uint_t 
128 mod_id_hash_fn (addr_t key) 
129 {
130     return v3_hash_long(key, sizeof(ullong_t) * 8);
131 }
132
133
134 static int 
135 mod_id_eq_fn (addr_t key1, addr_t key2) 
136 {
137     return (key1 == key2);
138 }
139
140
141 static uint_t 
142 entry_addr_hash_fn (addr_t key) 
143 {
144     return v3_hash_long(key, sizeof(addr_t) * 8);
145 }
146
147
148 static int 
149 entry_addr_eq_fn (addr_t key1, addr_t key2) 
150 {
151     return (key1 == key2);
152 }
153
154
155 /* GUARD INIT 
156  *
157  * This is invoked through a hypercall that has been instrumented in the
158  * guarded module's routine denoted by the module_init macro.
159  */
160 /* TODO: add text hash check here */
161 static int
162 guard_init (struct guest_info * core, unsigned int hcall_id, void * priv_data)
163 {
164     ullong_t mod_id;
165     struct v3_gm * gm;
166     struct v3_guarded_mods * mods = &global_mods;
167     struct v3_entry_point * er = NULL;
168     struct v3_entry_point * tmp = NULL;
169     struct v3_priv * priv = NULL;
170     struct v3_priv * ptmp = NULL;
171
172     /* the guarded module should provide its ID in RBX */
173     mod_id = core->vm_regs.rbx;
174
175     V3_Print(VM_NONE, VCORE_NONE, "Received init request from GM (id=0x%llx)\n", mod_id);
176
177     /* check if the corresponding module exists */
178     if ((gm = (struct v3_gm*)v3_htable_search(mods->mod_id_table, (addr_t)mod_id)) == 0) {
179         PrintError(VM_NONE, VCORE_NONE,"Module (id=0x%llx) not found\n", mod_id);
180         return -1;
181     }
182
183     PrintDebug(VM_NONE, VCORE_NONE,"GM: initializing guarded module %s\n", gm->name);
184
185     /* infer load address */
186     gm->load_addr = core->rip - HCALL_INSTR_LEN - gm->hcall_offset;
187     
188     PrintDebug(VM_NONE, VCORE_NONE,"\tNew GM load address: %p\n", (void*)gm->load_addr);
189
190     list_for_each_entry_safe(er, tmp, &(gm->er_list), er_node) {
191
192         /* fix up the dynamic address of the entry point */
193         er->addr += gm->load_addr;
194
195         /* check if this entry point already exists */
196         if (v3_htable_search(mods->er_hash, er->addr)) {
197             PrintError(VM_NONE, VCORE_NONE,"GM: Error! Entry point already exists!\n");
198             return -1;
199         }
200
201         /* add the new entry point to the global hash */
202         v3_htable_insert(mods->er_hash, er->addr, (addr_t)er);
203     }
204
205     PrintDebug(VM_NONE, VCORE_NONE,"GM: Raising privileges\n");
206
207     
208     /* module will come up in privileged mode */
209     gm->state = PRIV;
210
211     /* raise all of the module's privileges */
212     list_for_each_entry_safe(priv, ptmp, &(gm->priv_list), priv_node) {
213         if (priv->raise(core, priv->private_data) < 0) {
214             PrintError(VM_NONE, VCORE_NONE,"GM: Problem raising privilege\n");
215             return -1;
216         }
217     }
218
219     /* get the entry RSP for integrity checks */
220     addr_t hva;
221     if (v3_gva_to_hva(core, 
222         get_addr_linear(core, (addr_t)core->vm_regs.rsp, &(core->segments.ss)),
223         &hva) < 0) {
224             PrintError(VM_NONE, VCORE_NONE,"GM: Problem translating stack address\n");
225             return -1;
226     }
227     gm->entry_rsp = hva;
228
229     /* this is just to be consistent with other entry types, r11 isn't used in init */
230     gm->r11_stack_callback[gm->callback_nesting] = core->vm_regs.r11;
231     gm->callback_nesting++;
232
233     PrintDebug(VM_NONE, VCORE_NONE,"GM: Guarded module initialized, set to PRIV\n");
234
235     current_gm = gm;
236
237     return 0;
238 }
239
240
241 /* BORDER IN CALL
242  *
243  * This is invoked when the kernel calls the module (through a callback function)
244  *
245  * TODO: the stack checking model here is crappy, but will be updated to only
246  * look at frame pointers and return addresses. 
247  */
248 static int 
249 border_in_call (struct guest_info * core, unsigned int hcall_id, void * priv_data)
250 {
251     struct v3_guarded_mods * mods = &global_mods;
252     struct v3_entry_point * er;
253     struct v3_gm * gm = current_gm;
254     struct v3_priv * priv = NULL;
255     struct v3_priv * tmp = NULL;
256
257
258     addr_t entry = core->rip - HCALL_INSTR_LEN;
259
260     er = (struct v3_entry_point*)v3_htable_search(mods->er_hash, entry);
261     if (!er) {
262         PrintError(VM_NONE, VCORE_NONE,"Attempt to enter at an invalid entry point (%p)\n", (void*)entry);
263     } else {
264
265         gm = er->gm;
266
267         /* if this is first entry, record rsp */
268         if (gm->callback_nesting == 0) {
269             addr_t hva;
270             if (v3_gva_to_hva(core, 
271                get_addr_linear(core, (addr_t)core->vm_regs.rsp, &core->segments.ss),
272                &hva) < 0) {
273                     PrintError(VM_NONE, VCORE_NONE,"GM: Problem translating stack address\n");
274                     return -1;
275             }
276             gm->entry_rsp = hva;
277         }
278
279         if (gm->callback_nesting >= MAX_BORDER_NESTING) {
280             PrintError(VM_NONE, VCORE_NONE,"GM: Too much nesting of border crossings\n");
281             return -1;
282         } else {
283             gm->r11_stack_callback[gm->callback_nesting] = core->vm_regs.r11;
284             gm->callback_nesting++;
285         }
286
287         if (gm->state == INIT) {
288             PrintError(VM_NONE, VCORE_NONE,"ERROR: module reached guard entry without being initialized!\n");
289             return 0;
290         }
291
292         gm->state = PRIV;
293
294         PrintDebug(VM_NONE, VCORE_NONE,"Entry request at %p granted to GM (%s)\n", (void*)entry, gm->name);
295
296         list_for_each_entry_safe(priv, tmp, &(gm->priv_list), priv_node) {
297
298             if (priv->raise(core, priv->private_data) < 0) {
299                 PrintError(VM_NONE, VCORE_NONE,"GM: Problem raising privilege\n");
300                 return -1;
301             }
302
303         }
304
305     }
306     
307     return 0;
308 }
309
310
311 /*  BORDER-OUT RETURN
312  * 
313  * This is invoked when the module returns to the kernel, after being invoked through
314  * a callback function.
315  */
316 static int 
317 border_out_return (struct guest_info * core, unsigned int hcall_id, void * priv_data) 
318 {
319     struct v3_gm * gm = current_gm;
320     struct v3_priv * priv = NULL;
321     struct v3_priv * tmp = NULL;
322
323     gm->callback_nesting--;
324
325     /* restore the guest's return address */
326     core->vm_regs.r11 = gm->r11_stack_callback[gm->callback_nesting];
327     gm->r11_stack_callback[gm->callback_nesting] = 0;
328
329     /* Only lower privilege when we reach nesting 0 */
330     if (gm->callback_nesting <= 0)  {
331
332         PrintDebug(VM_NONE, VCORE_NONE,"GM: Granting exit request to GM (%s)\n", gm->name);
333         list_for_each_entry_safe(priv, tmp, &(gm->priv_list), priv_node) {
334
335             if (priv->lower(core, priv->private_data) < 0) {
336                 PrintError(VM_NONE, VCORE_NONE,"GM: Problem lowering privilege\n");
337                 return -1;
338             }
339         
340         }
341
342         gm->state = NO_PRIV;
343
344     } else {
345         PrintDebug(VM_NONE, VCORE_NONE,"Priv Lower requested, but still at nesting level (%d)\n", gm->callback_nesting);
346     }
347
348     return 0;
349 }
350
351
352 /* BORDER-OUT CALL
353  *
354  * This is invoked when the module calls out to the kernel, results in lowering of privilege
355  *
356  */
357 static int
358 border_out_call (struct guest_info * core, unsigned int hcall_id, void * priv_data) {
359
360     struct v3_gm * gm = current_gm;
361     struct v3_priv * priv = NULL;
362     struct v3_priv * tmp = NULL;
363
364     /* TODO: um...we seem to be getting a border out call in NO_PRIV state */
365     if (1 || gm->state == PRIV || gm->state == INIT) {
366
367         gm->r11_stack_kernel[gm->kernel_nesting] = core->vm_regs.r11;
368         gm->kernel_nesting++;
369
370         PrintDebug(VM_NONE, VCORE_NONE,"GM: Granting border out call request to GM (%s)\n", gm->name);
371         list_for_each_entry_safe(priv, tmp, &(gm->priv_list), priv_node) {
372             if (priv->lower(core, priv->private_data) < 0) {
373                 PrintError(VM_NONE, VCORE_NONE,"GM: Problem lowering privilege\n");
374                 return -1;
375             }
376
377         }
378
379         if (v3_gva_to_hva(core, 
380             get_addr_linear(core, (addr_t)core->vm_regs.rsp, &(core->segments.ss)),
381             &gm->exit_rsp) < 0) {
382             PrintError(VM_NONE, VCORE_NONE,"GM: border out, error translating rsp addr\n");
383             return -1;
384         }
385
386         if (gm->entry_rsp && gm->exit_rsp && gm->state == PRIV && (gm->entry_rsp >= gm->exit_rsp)) {
387             gm->stack_hash = v3_hash_buffer((uchar_t*)gm->exit_rsp, gm->entry_rsp - gm->exit_rsp);
388         }
389
390         gm->state = NO_PRIV;
391
392     } else {
393         PrintError(VM_NONE, VCORE_NONE,"GM: Trying to run border-out without privilege\n"); 
394     }
395
396     /* else, do nothing */
397     return 0;
398 }
399
400
401 /* BORDER-IN RETURN
402  * 
403  * This is invoked when the kernel returns control to the module after a border-out call
404  * Results in privilege raise
405  *
406  */
407 static int
408 border_in_return (struct guest_info * core, unsigned int hcall_id, void * priv_data) {
409     struct v3_guarded_mods * mods = &global_mods;
410     struct v3_gm * gm = NULL;
411     struct v3_priv * priv = NULL;
412     struct v3_priv * tmp = NULL;
413     struct v3_entry_point * er = NULL;
414
415     addr_t entry = core->rip - HCALL_INSTR_LEN;
416     if ((er = (struct v3_entry_point*)v3_htable_search(mods->er_hash, entry)) == 0) {
417         PrintError(VM_NONE, VCORE_NONE,"Attempt to enter at an invalid entry point (%p)\n", (void*)entry);
418     } else {
419         gm = er->gm;
420
421         /* restore the guest's return address */
422         gm->kernel_nesting--;
423         core->vm_regs.r11 = gm->r11_stack_kernel[gm->kernel_nesting];
424         gm->r11_stack_kernel[gm->kernel_nesting] = 0;
425
426         
427         if (gm->state == NO_PRIV) {
428             PrintDebug(VM_NONE, VCORE_NONE,"GM: Granting border in return request to GM (%s)\n", gm->name);
429             list_for_each_entry_safe(priv, tmp, &(gm->priv_list), priv_node) {
430                 if (priv->raise(core, priv->private_data) < 0) {
431                     PrintError(VM_NONE, VCORE_NONE,"GM: Problem raising privilege\n");
432                     return -1;
433                 }
434             }
435
436             if (gm->entry_rsp && gm->exit_rsp && (gm->entry_rsp > gm->exit_rsp)) {
437                 if (v3_hash_buffer((uchar_t*)gm->exit_rsp, gm->entry_rsp - gm->exit_rsp) != gm->stack_hash) {
438                 }
439             }
440
441             gm->state = PRIV;
442         } 
443     }
444
445     return 0;
446 }
447
448
449 static int 
450 init_gm (struct v3_vm_info * vm, v3_cfg_tree_t * cfg, void ** priv_data) 
451 {
452     struct v3_guarded_mods * mods = &global_mods;
453     
454     *priv_data = mods;
455
456     INIT_LIST_HEAD(&(mods->mod_list));
457
458     if (!(mods->mod_name_table = v3_create_htable(0, mod_name_hash_fn, mod_name_eq_fn))) {
459         PrintError(VM_NONE, VCORE_NONE,"GM: Could not create GM module name hashtable\n");
460         return -1;
461     }
462
463     if (!(mods->mod_id_table = v3_create_htable(0, mod_id_hash_fn, mod_id_eq_fn))) {
464         PrintError(VM_NONE, VCORE_NONE,"GM: Could not create GM module ID hashtable\n");
465         return -1;
466     }
467
468     if (!(mods->er_hash = v3_create_htable(0, entry_addr_hash_fn, entry_addr_eq_fn))) {
469         PrintError(VM_NONE, VCORE_NONE,"GM: Could not create GM entry point hashtable\n");
470         return -1;
471     }
472
473     /* these hypercalls will be invoked with a module id (hash) in rax. */
474     if (v3_register_hypercall(vm, V3_BIN_CALL_HCALL, border_in_call, mods) < 0    ||
475         v3_register_hypercall(vm, V3_BOUT_RET_HCALL, border_out_return, mods) < 0 ||
476         v3_register_hypercall(vm, V3_BIN_RET_HCALL, border_in_return, mods) < 0   ||
477         v3_register_hypercall(vm, V3_BOUT_CALL_HCALL, border_out_call, mods) < 0  ||
478         v3_register_hypercall(vm, V3_GUARD_INIT_HCALL, guard_init, mods) < 0) {
479         PrintError(VM_NONE, VCORE_NONE,"GM: Problem registering hypercalls\n");
480         return -1;
481     }
482
483     return 0;
484 }
485
486
487 static int 
488 deinit_gm (struct v3_vm_info * vm, void * priv_data) 
489 {
490     struct v3_guarded_mods * mods = (struct v3_guarded_mods *)priv_data;
491     struct v3_gm * mod = NULL;
492     struct v3_gm * tmp = NULL;
493
494     list_for_each_entry_safe(mod, tmp, &(mods->mod_list), mod_node) {
495         free_mod(mod);
496     }
497
498     v3_free_htable(mods->mod_id_table, 0, 0);
499     v3_free_htable(mods->mod_name_table, 0, 0);
500     v3_free_htable(mods->er_hash, 0, 0);
501
502     if (v3_remove_hypercall(vm, V3_BIN_CALL_HCALL) < 0 ||
503         v3_remove_hypercall(vm, V3_BOUT_RET_HCALL) < 0  ||
504         v3_remove_hypercall(vm, V3_BIN_RET_HCALL) < 0  ||
505         v3_remove_hypercall(vm, V3_BOUT_CALL_HCALL) < 0  ||
506         v3_remove_hypercall(vm, V3_GUARD_INIT_HCALL) < 0) {
507
508         PrintError(VM_NONE, VCORE_NONE,"GM: Problem removing GM hypercalls\n");
509         return -1;
510     }
511
512     return 0;
513 }
514
515
516 /* returns 0 on error, module id on success */
517 unsigned long long
518 v3_register_gm  (void *  vm, 
519                  char *  name,
520                  char *  hash,
521                  unsigned int hc_off,
522                  unsigned int size,
523                  unsigned int nentries,
524                  unsigned int nprivs,
525                  char ** priv_array,
526                  void * private_data, 
527                  void * entry_points)
528 {
529     struct v3_guarded_mods * mods = &global_mods;
530     struct v3_gm * mod;
531     ullong_t mod_id;
532     int i;
533     char * tmp;
534
535     if (!name) {
536         PrintError(VM_NONE, VCORE_NONE,"Invalid module name\n");
537         return 0;
538     }
539
540     if (v3_htable_search(mods->mod_name_table, (addr_t)name)) {
541         PrintError(VM_NONE, VCORE_NONE,"Multiple instances of guarded module (%s)\n", name);
542         return 0;
543     }
544
545     /* generate a unique identifier for this module */
546     gen_mod_id(&mod_id);  
547
548     mod = (struct v3_gm *)V3_Malloc(sizeof(struct v3_gm));
549     if (!mod) {
550         PrintError(VM_NONE, VCORE_NONE,"Problem allocating guarded module\n");
551         return 0;
552     }
553
554     memset(mod, 0, sizeof(struct v3_gm));
555
556     V3_Print(VM_NONE, VCORE_NONE, "V3 GM Registration: received name (%s)\n", name);
557     mod->name = V3_Malloc(strlen(name)+1);
558     if (!mod->name) {
559         PrintError(VM_NONE, VCORE_NONE,"Problem allocating space for mod name\n");
560         return -1;
561     }
562
563     V3_Print(VM_NONE, VCORE_NONE, "V3 GM Registration: received hash (%s)\n", hash);
564     mod->content_hash = V3_Malloc(strlen(hash)+1);
565     if (!mod->content_hash) {
566         PrintError(VM_NONE, VCORE_NONE,"Problem allocating space for content hash\n");
567         return -1;
568     }
569     strcpy(mod->name, name);
570     strcpy(mod->content_hash, hash);
571
572     mod->hcall_offset     = hc_off;
573     mod->text_size        = size;
574     mod->num_entries      = nentries;
575     mod->private_data     = private_data;
576     mod->id               = mod_id;
577     mod->callback_nesting = 0;
578     mod->kernel_nesting   = 0;
579
580     INIT_LIST_HEAD(&(mod->priv_list));
581     if (add_privs_to_list(vm, &(mod->priv_list), nprivs, priv_array) == -1) {
582         PrintError(VM_NONE, VCORE_NONE,"Could not add privileges to guarded module\n");
583         return -1;
584     }
585
586     mod->state      = INIT;
587     mod->stack_hash = 0;
588
589     /* setup the valid entry points */
590     INIT_LIST_HEAD(&(mod->er_list));
591
592     mod->entry_points = V3_Malloc(sizeof(struct v3_entry_point)*mod->num_entries);
593
594     if (!mod->entry_points) {
595         PrintError(VM_NONE, VCORE_NONE,"Problem allocating entry point array\n");
596         return -1;
597     }
598
599     memcpy(mod->entry_points, entry_points, sizeof(struct v3_entry_point)*mod->num_entries);
600     
601     /* entries are added to the list, but can't be hashed yet since we're using
602      * the load address as a key, but we haven't fixed these addresses up yet */
603     for (i = 0; i < mod->num_entries; i++) {
604         tmp = V3_Malloc(strlen(mod->entry_points[i].name)+1);
605         if (!tmp) {
606             PrintError(VM_NONE, VCORE_NONE,"Problem allocating space for name locally\n");
607             return -1;
608         }
609         strcpy(tmp, mod->entry_points[i].name);
610         mod->entry_points[i].name = tmp;
611         mod->entry_points[i].gm   = mod;
612         list_add(&(mod->entry_points[i].er_node), &(mod->er_list));
613     }
614
615     //v3_htable_insert(mods->mod_id_table, mod_id, (addr_t)mod);
616     // TODO: this will change the content hash of the module code segment, fix!
617     v3_htable_insert(mods->mod_id_table, (addr_t)0xa3aeea3aeebadbad, (addr_t)mod);
618     v3_htable_insert(mods->mod_name_table, (addr_t)name, (addr_t)mod);
619     list_add(&(mod->mod_node), &(mods->mod_list));
620     return mod_id;
621 }
622
623
624 static struct v3_extension_impl guard_mods_impl = {
625         .name = "guard_mods",
626         .vm_init = init_gm,
627         .vm_deinit = deinit_gm,
628         .core_init = NULL,
629         .core_deinit = NULL,
630         .on_entry = NULL,
631         .on_exit = NULL
632 };
633
634 register_extension(&guard_mods_impl);