diff options
author | Tim Moore <moore@blackbox.bricoworks.com> | 2008-09-08 16:14:03 +0200 |
---|---|---|
committer | Tim Moore <moore@blackbox.bricoworks.com> | 2008-09-08 16:14:03 +0200 |
commit | e6342ff8c11bbed1d2058507b918cf3c140e1322 (patch) | |
tree | 4c7db07136368155ea272f848d5ac46819a6ae84 | |
parent | e36b5fc35d4033d5718eb8e101968b79d0360202 (diff) | |
download | systemtap-steved-e6342ff8c11bbed1d2058507b918cf3c140e1322.tar.gz systemtap-steved-e6342ff8c11bbed1d2058507b918cf3c140e1322.tar.xz systemtap-steved-e6342ff8c11bbed1d2058507b918cf3c140e1322.zip |
PR1288: runtime functions for avoiding certain addresses
-rw-r--r-- | runtime/ChangeLog | 8 | ||||
-rw-r--r-- | runtime/addr-map.c | 181 | ||||
-rw-r--r-- | runtime/loc2c-runtime.h | 230 | ||||
-rw-r--r-- | runtime/runtime.h | 1 |
4 files changed, 326 insertions, 94 deletions
diff --git a/runtime/ChangeLog b/runtime/ChangeLog index 8f20ed11..eb091d01 100644 --- a/runtime/ChangeLog +++ b/runtime/ChangeLog @@ -1,3 +1,11 @@ +2008-09-08 Tim Moore <timoore@redhat.com> + + PR 1288 + * addr-map.c: New file with functions for looking up addresses + * loc2c-runtime.h (deref, store_deref): Use lookup_bad_addr to + avoid dereferencing known dangerous addresses. + * runtime.h: Include addr-map.c. + 2008-09-06 Frank Ch. Eigler <fche@elastic.org> PR 6445 diff --git a/runtime/addr-map.c b/runtime/addr-map.c new file mode 100644 index 00000000..8231b57f --- /dev/null +++ b/runtime/addr-map.c @@ -0,0 +1,181 @@ +/* -*- linux-c -*- + * Map of addresses to disallow. + * Copyright (C) 2005-2008 Red Hat Inc. + * + * This file is part of systemtap, and is free software. You can + * redistribute it and/or modify it under the terms of the GNU General + * Public License (GPL); either version 2, or (at your option) any + * later version. + */ + +#ifndef _ADDR_MAP_C_ +#define _ADDR_MAP_C_ 1 + +/** @file addr-map + * @brief Implements functions used by the deref macro to blacklist + * certain addresses. + */ + +struct addr_map_entry +{ + unsigned long min; + unsigned long max; +}; + +struct addr_map +{ + size_t size; + struct addr_map_entry entries[0]; +}; + +static DEFINE_SPINLOCK(addr_map_lock); + +struct addr_map* blackmap; + +/* Find address of entry where we can insert a new one. */ +static size_t +upper_bound(unsigned long addr, struct addr_map* map) +{ + size_t start = 0; + size_t end = map->size; + struct addr_map_entry *entry = 0; + + if (end == 0) + return 0; + do + { + size_t new_idx; + if (addr < map->entries[start].min) + return start; + if (addr >= map->entries[end-1].max) + return end; + new_idx = (end + start) / 2; + entry = &map->entries[new_idx]; + if (addr < entry->min) + end = new_idx; + else + start = new_idx + 1; + } while (end != start); + return entry - &map->entries[0]; +} + +static struct addr_map_entry* +lookup_addr_aux(unsigned long addr, struct addr_map* map) +{ + size_t start = 0; + size_t end; + if (!map) + return 0; + end = map->size; + if (map->size == 0) + return 0; + + do + { + int entry_idx; + struct addr_map_entry *entry = 0; + if (addr < map->entries[start].min || addr >= map->entries[end - 1].max) + return 0; + entry_idx = (end + start) / 2; + entry = &map->entries[entry_idx]; + if (entry->min <= addr && entry->max > addr) + return entry; + if (addr < entry->min) + end = entry_idx; + else + start = entry_idx + 1; + } while (start < end); + return 0; +} + +int +lookup_bad_addr(unsigned long addr) +{ + struct addr_map_entry* result = 0; + spin_lock(&addr_map_lock); + result = lookup_addr_aux(addr, blackmap); + spin_unlock(&addr_map_lock); + if (result) + return 1; + else + return 0; +} + + +int +add_bad_addr_entry(unsigned long min_addr, unsigned long max_addr, + struct addr_map_entry** existing_min, + struct addr_map_entry** existing_max) +{ + struct addr_map* new_map = 0; + struct addr_map* old_map = 0; + struct addr_map_entry* min_entry = 0; + struct addr_map_entry* max_entry = 0; + struct addr_map_entry* new_entry = 0; + size_t existing = 0; + + while (1) + { + size_t old_size; + spin_lock(&addr_map_lock); + old_map = blackmap; + if (!blackmap) + { + existing = 0; + old_size = 0; + } + else + { + min_entry = lookup_addr_aux(min_addr, blackmap); + max_entry = lookup_addr_aux(max_addr, blackmap); + if (min_entry || max_entry) + { + if (existing_min) + *existing_min = min_entry; + if (existing_max) + *existing_max = max_entry; + spin_unlock(&addr_map_lock); + return 1; + } + existing = upper_bound(min_addr, old_map); + old_size = old_map->size; + } + spin_unlock(&addr_map_lock); + new_map = kmalloc(sizeof(*new_map) + + sizeof(*new_entry) * (old_size + 1), + GFP_KERNEL); + if (!new_map) + return -ENOMEM; + spin_lock(&addr_map_lock); + if (blackmap != old_map) + { + kfree(new_map); + spin_unlock(&addr_map_lock); + continue; + } + new_entry = &new_map->entries[existing]; + new_entry->min = min_addr; + new_entry->max = max_addr; + if (old_map) + { + memcpy(&new_map->entries, old_map->entries, + existing * sizeof(*new_entry)); + if (old_map->size > existing) + memcpy(new_entry + 1, &old_map->entries[existing + 1], + (old_map->size - existing) * sizeof(*new_entry)); + } + new_map->size = blackmap->size + 1; + blackmap = new_map; + spin_unlock(&addr_map_lock); + if (old_map) + kfree(old_map); + return 0; + } +} + +void +delete_bad_addr_entry(struct addr_map_entry* entry) +{ +} + +#endif diff --git a/runtime/loc2c-runtime.h b/runtime/loc2c-runtime.h index 1247da51..0af19edc 100644 --- a/runtime/loc2c-runtime.h +++ b/runtime/loc2c-runtime.h @@ -201,6 +201,8 @@ #define deref(size, addr) ({ \ intptr_t _i; \ + if (lookup_bad_addr((unsigned long)addr)) \ + __deref_bad(); \ switch (size) { \ case 1: _i = kread((u8 *)(addr)); break; \ case 2: _i = kread((u16 *)(addr)); break; \ @@ -213,6 +215,8 @@ }) #define store_deref(size, addr, value) ({ \ + if (lookup_bad_addr((unsigned long)addr)) \ + __store_deref_bad(); \ switch (size) { \ case 1: kwrite((u8 *)(addr), (value)); break; \ case 2: kwrite((u16 *)(addr), (value)); break; \ @@ -234,30 +238,36 @@ extern void __store_deref_bad(void); int _bad = 0; \ u8 _b; u16 _w; u32 _l; \ intptr_t _v; \ - switch (size) \ - { \ - case 1: __get_user_asm(_b,addr,_bad,"b","b","=q",1); _v = _b; break; \ - case 2: __get_user_asm(_w,addr,_bad,"w","w","=r",1); _v = _w; break; \ - case 4: __get_user_asm(_l,addr,_bad,"l","","=r",1); _v = _l; break; \ - default: _v = __get_user_bad(); \ - } \ - if (_bad) \ - DEREF_FAULT(addr); \ + if (lookup_bad_addr((unsigned long)addr)) \ + _bad = 1; \ + else \ + switch (size) \ + { \ + case 1: __get_user_asm(_b,addr,_bad,"b","b","=q",1); _v = _b; break; \ + case 2: __get_user_asm(_w,addr,_bad,"w","w","=r",1); _v = _w; break; \ + case 4: __get_user_asm(_l,addr,_bad,"l","","=r",1); _v = _l; break; \ + default: _v = __get_user_bad(); \ + } \ + if (_bad) \ + DEREF_FAULT(addr); \ _v; \ }) #define store_deref(size, addr, value) \ ({ \ int _bad = 0; \ - switch (size) \ - { \ - case 1: __put_user_asm(((u8)(value)),addr,_bad,"b","b","iq",1); break; \ - case 2: __put_user_asm(((u16)(value)),addr,_bad,"w","w","ir",1); break; \ - case 4: __put_user_asm(((u32)(value)),addr,_bad,"l","k","ir",1); break; \ - default: __put_user_bad(); \ - } \ - if (_bad) \ - STORE_DEREF_FAULT(addr); \ + if (lookup_bad_addr((unsigned long)addr)) \ + _bad = 1; \ + else \ + switch (size) \ + { \ + case 1: __put_user_asm(((u8)(value)),addr,_bad,"b","b","iq",1); break;\ + case 2: __put_user_asm(((u16)(value)),addr,_bad,"w","w","ir",1); break;\ + case 4: __put_user_asm(((u32)(value)),addr,_bad,"l","k","ir",1); break;\ + default: __put_user_bad(); \ + } \ + if (_bad) \ + STORE_DEREF_FAULT(addr); \ }) @@ -268,14 +278,17 @@ extern void __store_deref_bad(void); int _bad = 0; \ u8 _b; u16 _w; u32 _l; u64 _q; \ intptr_t _v; \ - switch (size) \ - { \ - case 1: __get_user_asm(_b,addr,_bad,"b","b","=q",1); _v = _b; break; \ - case 2: __get_user_asm(_w,addr,_bad,"w","w","=r",1); _v = _w; break; \ - case 4: __get_user_asm(_l,addr,_bad,"l","","=r",1); _v = _l; break; \ - case 8: __get_user_asm(_q,addr,_bad,"q","","=r",1); _v = _q; break; \ - default: _v = __get_user_bad(); \ - } \ + if (lookup_bad_addr((unsigned long)addr)) \ + _bad = 1; \ + else \ + switch (size) \ + { \ + case 1: __get_user_asm(_b,addr,_bad,"b","b","=q",1); _v = _b; break; \ + case 2: __get_user_asm(_w,addr,_bad,"w","w","=r",1); _v = _w; break; \ + case 4: __get_user_asm(_l,addr,_bad,"l","","=r",1); _v = _l; break; \ + case 8: __get_user_asm(_q,addr,_bad,"q","","=r",1); _v = _q; break; \ + default: _v = __get_user_bad(); \ + } \ if (_bad) \ DEREF_FAULT(addr); \ _v; \ @@ -284,14 +297,17 @@ extern void __store_deref_bad(void); #define store_deref(size, addr, value) \ ({ \ int _bad = 0; \ - switch (size) \ - { \ - case 1: __put_user_asm(((u8)(value)),addr,_bad,"b","b","iq",1); break; \ - case 2: __put_user_asm(((u16)(value)),addr,_bad,"w","w","ir",1); break; \ - case 4: __put_user_asm(((u32)(value)),addr,_bad,"l","k","ir",1); break; \ - case 8: __put_user_asm(((u64)(value)),addr,_bad,"q","","Zr",1); break; \ - default: __put_user_bad(); \ - } \ + if (lookup_bad_addr((unsigned long)addr)) \ + _bad = 1; \ + else \ + switch (size) \ + { \ + case 1: __put_user_asm(((u8)(value)),addr,_bad,"b","b","iq",1); break; \ + case 2: __put_user_asm(((u16)(value)),addr,_bad,"w","w","ir",1); break;\ + case 4: __put_user_asm(((u32)(value)),addr,_bad,"l","k","ir",1); break;\ + case 8: __put_user_asm(((u64)(value)),addr,_bad,"q","","Zr",1); break; \ + default: __put_user_bad(); \ + } \ if (_bad) \ STORE_DEREF_FAULT(addr); \ }) @@ -301,13 +317,16 @@ extern void __store_deref_bad(void); ({ \ int _bad = 0; \ intptr_t _v=0; \ - switch (size){ \ - case 1: __get_user_size(_v, addr, 1, _bad); break; \ - case 2: __get_user_size(_v, addr, 2, _bad); break; \ - case 4: __get_user_size(_v, addr, 4, _bad); break; \ - case 8: __get_user_size(_v, addr, 8, _bad); break; \ - default: __get_user_unknown(); break; \ - } \ + if (lookup_bad_addr((unsigned long)addr)) \ + _bad = 1; \ + else \ + switch (size){ \ + case 1: __get_user_size(_v, addr, 1, _bad); break; \ + case 2: __get_user_size(_v, addr, 2, _bad); break; \ + case 4: __get_user_size(_v, addr, 4, _bad); break; \ + case 8: __get_user_size(_v, addr, 8, _bad); break; \ + default: __get_user_unknown(); break; \ + } \ if (_bad) \ DEREF_FAULT(addr); \ _v; \ @@ -316,13 +335,16 @@ extern void __store_deref_bad(void); #define store_deref(size, addr, value) \ ({ \ int _bad=0; \ - switch (size){ \ - case 1: __put_user_size(value, addr, 1, _bad); break; \ - case 2: __put_user_size(value, addr, 2, _bad); break; \ - case 4: __put_user_size(value, addr, 4, _bad); break; \ - case 8: __put_user_size(value, addr, 8, _bad); break; \ - default: __put_user_unknown(); break; \ - } \ + if (lookup_bad_addr((unsigned long)addr)) \ + _bad = 1; \ + else \ + switch (size){ \ + case 1: __put_user_size(value, addr, 1, _bad); break; \ + case 2: __put_user_size(value, addr, 2, _bad); break; \ + case 4: __put_user_size(value, addr, 4, _bad); break; \ + case 8: __put_user_size(value, addr, 8, _bad); break; \ + default: __put_user_unknown(); break; \ + } \ if (_bad) \ STORE_DEREF_FAULT(addr); \ }) @@ -373,30 +395,36 @@ extern void __store_deref_bad(void); ({ \ int _bad = 0; \ intptr_t _v; \ - switch (size) \ - { \ - case 1: __stp_get_user_asm(_v,addr,_bad,"lbz"); break; \ - case 2: __stp_get_user_asm(_v,addr,_bad,"lhz"); break; \ - case 4: __stp_get_user_asm(_v,addr,_bad,"lwz"); break; \ - case 8: __stp_get_user_asm(_v,addr,_bad,"ld"); break; \ - default: _v = __get_user_bad(); \ - } \ + if (lookup_bad_addr((unsigned long)addr)) \ + _bad = 1; \ + else \ + switch (size) \ + { \ + case 1: __stp_get_user_asm(_v,addr,_bad,"lbz"); break; \ + case 2: __stp_get_user_asm(_v,addr,_bad,"lhz"); break; \ + case 4: __stp_get_user_asm(_v,addr,_bad,"lwz"); break; \ + case 8: __stp_get_user_asm(_v,addr,_bad,"ld"); break; \ + default: _v = __get_user_bad(); \ + } \ if (_bad) \ - DEREF_FAULT(addr); \ + DEREF_FAULT(addr); \ _v; \ }) #define store_deref(size, addr, value) \ ({ \ int _bad = 0; \ - switch (size) \ - { \ - case 1: __stp_put_user_asm(((u8)(value)),addr,_bad,"stb"); break; \ - case 2: __stp_put_user_asm(((u16)(value)),addr,_bad,"sth"); break; \ - case 4: __stp_put_user_asm(((u32)(value)),addr,_bad,"stw"); break; \ - case 8: __stp_put_user_asm(((u64)(value)),addr,_bad, "std"); break; \ - default: __put_user_bad(); \ - } \ + if (lookup_bad_addr((unsigned long)addr)) \ + _bad = 1; \ + else \ + switch (size) \ + { \ + case 1: __stp_put_user_asm(((u8)(value)),addr,_bad,"stb"); break; \ + case 2: __stp_put_user_asm(((u16)(value)),addr,_bad,"sth"); break; \ + case 4: __stp_put_user_asm(((u32)(value)),addr,_bad,"stw"); break; \ + case 8: __stp_put_user_asm(((u64)(value)),addr,_bad, "std"); break; \ + default: __put_user_bad(); \ + } \ if (_bad) \ STORE_DEREF_FAULT(addr); \ }) @@ -541,12 +569,15 @@ extern void __store_deref_bad(void); ({ \ int _bad = 0; \ intptr_t _v=0; \ - switch (size){ \ - case 1: __stp_get_user_asm_byte(_v, addr, _bad); break; \ - case 2: __stp_get_user_asm_half(_v, addr, _bad); break; \ - case 4: __stp_get_user_asm_word(_v, addr, _bad); break; \ - default: __get_user_bad(); break; \ - } \ + if (lookup_bad_addr((unsigned long)addr)) \ + _bad = 1; \ + else \ + switch (size){ \ + case 1: __stp_get_user_asm_byte(_v, addr, _bad); break; \ + case 2: __stp_get_user_asm_half(_v, addr, _bad); break; \ + case 4: __stp_get_user_asm_word(_v, addr, _bad); break; \ + default: __get_user_bad(); break; \ + } \ if (_bad) \ DEREF_FAULT(addr); \ _v; \ @@ -555,13 +586,16 @@ extern void __store_deref_bad(void); #define store_deref(size, addr, value) \ ({ \ int _bad=0; \ - switch (size){ \ - case 1: __stp_put_user_asm_byte(value, addr, _bad); break; \ - case 2: __stp_put_user_asm_half(value, addr, _bad); break; \ - case 4: __stp_put_user_asm_word(value, addr, _bad); break; \ - case 8: __stp_put_user_asm_dword(value, addr, _bad); break; \ - default: __put_user_bad(); break; \ - } \ + if (lookup_bad_addr((unsigned long)addr)) \ + _bad = 1; \ + else \ + switch (size){ \ + case 1: __stp_put_user_asm_byte(value, addr, _bad); break; \ + case 2: __stp_put_user_asm_half(value, addr, _bad); break; \ + case 4: __stp_put_user_asm_word(value, addr, _bad); break; \ + case 8: __stp_put_user_asm_dword(value, addr, _bad); break; \ + default: __put_user_bad(); break; \ + } \ if (_bad) \ STORE_DEREF_FAULT(addr); \ }) @@ -624,28 +658,31 @@ extern void __store_deref_bad(void); u8 _b; u16 _w; u32 _l; u64 _q; \ int _bad = 0; \ intptr_t _v = 0; \ - switch (size) { \ - case 1: { \ + if (lookup_bad_addr((unsigned long)addr)) \ + _bad = 1; \ + else \ + switch (size) { \ + case 1: { \ __stp_get_asm(_b, addr, _bad, 1); \ _v = _b; \ break; \ - }; \ - case 2: { \ + }; \ + case 2: { \ __stp_get_asm(_w, addr, _bad, 2); \ _v = _w; \ break; \ - }; \ - case 4: { \ + }; \ + case 4: { \ __stp_get_asm(_l, addr, _bad, 4); \ _v = _l; \ break; \ - }; \ - case 8: { \ + }; \ + case 8: { \ __stp_get_asm(_q, addr, _bad, 8); \ _v = _q; \ break; \ - }; \ - default: \ + }; \ + default: \ _bad = -EFAULT; \ } \ if (_bad) \ @@ -657,12 +694,17 @@ extern void __store_deref_bad(void); ({ \ int _bad = 0; \ int i; \ - for(i=0;i<size;i++){ \ - __stp_put_asm((u8)(value>>((size-i-1)*8)&0xff), \ - (u64)addr+i,_bad); \ - if (_bad) \ - STORE_DEREF_FAULT(addr); \ - } \ + if (lookup_bad_addr((unsigned long)addr)) \ + _bad = 1; \ + else \ + for(i=0;i<size;i++){ \ + __stp_put_asm((u8)(value>>((size-i-1)*8)&0xff), \ + (u64)addr+i,_bad); \ + if (_bad) \ + break; \ + } \ + if (_bad) \ + STORE_DEREF_FAULT(addr); \ }) diff --git a/runtime/runtime.h b/runtime/runtime.h index 2711f531..fd0cac9e 100644 --- a/runtime/runtime.h +++ b/runtime/runtime.h @@ -88,6 +88,7 @@ static struct #ifdef STP_PERFMON #include "perf.c" #endif +#include "addr-map.c" /* Support functions for int64_t module parameters. */ int param_set_int64_t(const char *val, struct kernel_param *kp) |