summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTim Moore <moore@blackbox.bricoworks.com>2008-09-08 16:14:03 +0200
committerTim Moore <moore@blackbox.bricoworks.com>2008-09-08 16:14:03 +0200
commite6342ff8c11bbed1d2058507b918cf3c140e1322 (patch)
tree4c7db07136368155ea272f848d5ac46819a6ae84
parente36b5fc35d4033d5718eb8e101968b79d0360202 (diff)
downloadsystemtap-steved-e6342ff8c11bbed1d2058507b918cf3c140e1322.tar.gz
systemtap-steved-e6342ff8c11bbed1d2058507b918cf3c140e1322.tar.xz
systemtap-steved-e6342ff8c11bbed1d2058507b918cf3c140e1322.zip
PR1288: runtime functions for avoiding certain addresses
-rw-r--r--runtime/ChangeLog8
-rw-r--r--runtime/addr-map.c181
-rw-r--r--runtime/loc2c-runtime.h230
-rw-r--r--runtime/runtime.h1
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)