From 543dae7cd1beea7de89893f8d57f2f1aa1883cf8 Mon Sep 17 00:00:00 2001 From: Christopher Ferris Date: Mon, 18 Nov 2013 15:48:51 -0800 Subject: [PATCH] Check memory accesses before making them. --- include/map_info.h | 43 +++++++++++++++++++++++++++ include/tdep-x86_64/libunwind_i.h | 2 ++ src/dwarf/Gfind_proc_info-lsb.c | 2 +- src/os-linux.c | 61 ++++++++++++++++++++++++++++++++++++++- src/os-linux.h | 26 ++++++++++++++++- src/x86_64/Ginit.c | 36 ++++++++++++++++++++--- 6 files changed, 163 insertions(+), 7 deletions(-) create mode 100644 include/map_info.h diff --git a/include/map_info.h b/include/map_info.h new file mode 100644 index 0000000..1af10d4 --- /dev/null +++ b/include/map_info.h @@ -0,0 +1,43 @@ +/* libunwind - a platform-independent unwind library + Copyright (C) 2013 The Android Open Source Project + +This file is part of libunwind. + +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 map_info_h +#define map_info_h + +struct map_info + { + uintptr_t start; + uintptr_t end; + uintptr_t offset; + int flags; + struct map_info *next; + }; + +int maps_is_readable(struct map_info *map_list, unw_word_t addr); + +int maps_is_writable(struct map_info *map_list, unw_word_t addr); + +struct map_info *maps_create_list(pid_t pid); + +#endif /* map_info_h */ diff --git a/include/tdep-x86_64/libunwind_i.h b/include/tdep-x86_64/libunwind_i.h index 8c9cd05..d8d8d20 100644 --- a/include/tdep-x86_64/libunwind_i.h +++ b/include/tdep-x86_64/libunwind_i.h @@ -35,6 +35,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include #include "elf64.h" +#include "map_info.h" #include "mempool.h" #include "dwarf.h" @@ -72,6 +73,7 @@ struct unw_addr_space unw_word_t dyn_info_list_addr; /* (cached) dyn_info_list_addr */ struct dwarf_rs_cache global_cache; struct unw_debug_frame_list *debug_frames; + struct map_info *map_list; }; struct cursor diff --git a/src/dwarf/Gfind_proc_info-lsb.c b/src/dwarf/Gfind_proc_info-lsb.c index f75bda2..f2a130a 100644 --- a/src/dwarf/Gfind_proc_info-lsb.c +++ b/src/dwarf/Gfind_proc_info-lsb.c @@ -245,7 +245,7 @@ find_binary_for_address (unw_word_t ip, char *name, size_t name_size) unsigned long segbase, mapoff, hi; maps_init (&mi, pid); - while (maps_next (&mi, &segbase, &hi, &mapoff)) + while (maps_next (&mi, &segbase, &hi, &mapoff, NULL)) if (ip >= segbase && ip < hi) { size_t len = strlen (mi.path); diff --git a/src/os-linux.c b/src/os-linux.c index bcc4036..44f7f18 100644 --- a/src/os-linux.c +++ b/src/os-linux.c @@ -27,8 +27,67 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include #include "libunwind_i.h" +#include "map_info.h" #include "os-linux.h" +struct map_info * +maps_create_list(pid_t pid) +{ + struct map_iterator mi; + unsigned long start, end, offset, flags; + struct map_info *map_list = NULL; + struct map_info *cur_map; + + if (maps_init (&mi, pid) < 0) + return NULL; + + while (maps_next (&mi, &start, &end, &offset, &flags)) + { + cur_map = (struct map_info *)malloc(sizeof(struct map_info)); + if (cur_map == NULL) + break; + cur_map->next = map_list; + cur_map->start = start; + cur_map->end = end; + cur_map->offset = offset; + cur_map->flags = flags; + + map_list = cur_map; + } + + maps_close (&mi); + + return map_list; +} + +static struct map_info * +get_map(struct map_info *map_list, unw_word_t addr) +{ + while (map_list) + { + if (addr >= map_list->start && addr < map_list->end) + return map_list; + map_list = map_list->next; + } + return NULL; +} + +int maps_is_readable(struct map_info *map_list, unw_word_t addr) +{ + struct map_info *map = get_map(map_list, addr); + if (map != NULL) + return map->flags & MAP_FLAGS_READABLE; + return 0; +} + +int maps_is_writable(struct map_info *map_list, unw_word_t addr) +{ + struct map_info *map = get_map(map_list, addr); + if (map != NULL) + return map->flags & MAP_FLAGS_WRITABLE; + return 0; +} + PROTECTED int tdep_get_elf_image (struct elf_image *ei, pid_t pid, unw_word_t ip, unsigned long *segbase, unsigned long *mapoff, @@ -41,7 +100,7 @@ tdep_get_elf_image (struct elf_image *ei, pid_t pid, unw_word_t ip, if (maps_init (&mi, pid) < 0) return -1; - while (maps_next (&mi, segbase, &hi, mapoff)) + while (maps_next (&mi, segbase, &hi, mapoff, NULL)) if (ip >= *segbase && ip < hi) { found = 1; diff --git a/src/os-linux.h b/src/os-linux.h index ad9d675..9f7d665 100644 --- a/src/os-linux.h +++ b/src/os-linux.h @@ -27,6 +27,13 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef os_linux_h #define os_linux_h +enum map_flags + { + MAP_FLAGS_READABLE = 0x1, + MAP_FLAGS_WRITABLE = 0x2, + MAP_FLAGS_EXECUTABLE = 0x4, + }; + struct map_iterator { off_t offset; @@ -201,7 +208,8 @@ scan_string (char *cp, char *valp, size_t buf_size) static inline int maps_next (struct map_iterator *mi, - unsigned long *low, unsigned long *high, unsigned long *offset) + unsigned long *low, unsigned long *high, unsigned long *offset, + unsigned long *flags) { char perm[16], dash = 0, colon = 0, *cp; unsigned long major, minor, inum; @@ -275,6 +283,22 @@ maps_next (struct map_iterator *mi, cp = scan_string (cp, NULL, 0); if (dash != '-' || colon != ':') continue; /* skip line with unknown or bad format */ + if (flags) + { + *flags = 0; + if (perm[0] == 'r') + { + *flags |= MAP_FLAGS_READABLE; + } + if (perm[1] == 'w') + { + *flags |= MAP_FLAGS_WRITABLE; + } + if (perm[2] == 'x') + { + *flags |= MAP_FLAGS_EXECUTABLE; + } + } return 1; } return 0; diff --git a/src/x86_64/Ginit.c b/src/x86_64/Ginit.c index daea078..d0a3178 100644 --- a/src/x86_64/Ginit.c +++ b/src/x86_64/Ginit.c @@ -160,8 +160,16 @@ access_mem (unw_addr_space_t as, unw_word_t addr, unw_word_t *val, int write, { if (unlikely (write)) { - Debug (16, "mem[%016lx] <- %lx\n", addr, *val); - *(unw_word_t *) addr = *val; + if (maps_is_writable(as->map_list, addr)) + { + Debug (16, "mem[%016lx] <- %lx\n", addr, *val); + *(unw_word_t *) addr = *val; + } + else + { + Debug (16, "Unwritable memory mem[%016lx] <- %lx\n", addr, *val); + return -1; + } } else { @@ -170,8 +178,17 @@ access_mem (unw_addr_space_t as, unw_word_t addr, unw_word_t *val, int write, if (likely (c != NULL) && unlikely (c->validate) && unlikely (validate_mem (addr))) return -1; - *val = *(unw_word_t *) addr; - Debug (16, "mem[%016lx] -> %lx\n", addr, *val); + + if (maps_is_readable(as->map_list, addr)) + { + *val = *(unw_word_t *) addr; + Debug (16, "mem[%016lx] -> %lx\n", addr, *val); + } + else + { + Debug (16, "Unreadable memory mem[%016lx] -> XXX\n", addr); + return -1; + } } return 0; } @@ -247,6 +264,9 @@ get_static_proc_name (unw_addr_space_t as, unw_word_t ip, return _Uelf64_get_proc_name (as, getpid (), ip, buf, buf_len, offp); } +static define_lock (_U_map_init_lock); +static struct map_info *_U_map_list = NULL; + HIDDEN void x86_64_local_addr_space_init (void) { @@ -264,6 +284,14 @@ x86_64_local_addr_space_init (void) memset (last_good_addr, 0, sizeof (unw_word_t) * NLGA); lga_victim = 0; + + mutex_lock (&_U_map_init_lock); + if (_U_map_list == NULL) + { + _U_map_list = maps_create_list(getpid()); + } + mutex_unlock (&_U_map_init_lock); + local_addr_space.map_list = _U_map_list; } #endif /* !UNW_REMOTE_ONLY */ -- 1.8.4.1