OLD | NEW |
1 // Copyright (c) 2010 Google Inc. | 1 // Copyright (c) 2010 Google Inc. |
2 // All rights reserved. | 2 // All rights reserved. |
3 // | 3 // |
4 // Redistribution and use in source and binary forms, with or without | 4 // Redistribution and use in source and binary forms, with or without |
5 // modification, are permitted provided that the following conditions are | 5 // modification, are permitted provided that the following conditions are |
6 // met: | 6 // met: |
7 // | 7 // |
8 // * Redistributions of source code must retain the above copyright | 8 // * Redistributions of source code must retain the above copyright |
9 // notice, this list of conditions and the following disclaimer. | 9 // notice, this list of conditions and the following disclaimer. |
10 // * Redistributions in binary form must reproduce the above | 10 // * Redistributions in binary form must reproduce the above |
(...skipping 14 matching lines...) Expand all Loading... |
25 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | 25 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
26 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | 26 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
27 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | 27 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
28 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | 28 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
29 | 29 |
30 #include <string> | 30 #include <string> |
31 | 31 |
32 #include <stdint.h> | 32 #include <stdint.h> |
33 #include <unistd.h> | 33 #include <unistd.h> |
34 #include <signal.h> | 34 #include <signal.h> |
| 35 #include <sys/mman.h> |
35 #include <sys/poll.h> | 36 #include <sys/poll.h> |
36 #include <sys/socket.h> | 37 #include <sys/socket.h> |
37 #include <sys/uio.h> | 38 #include <sys/uio.h> |
38 | 39 |
39 #include "breakpad_googletest_includes.h" | 40 #include "breakpad_googletest_includes.h" |
40 #include "client/linux/handler/exception_handler.h" | 41 #include "client/linux/handler/exception_handler.h" |
41 #include "client/linux/minidump_writer/minidump_writer.h" | 42 #include "client/linux/minidump_writer/minidump_writer.h" |
42 #include "common/linux/eintr_wrapper.h" | 43 #include "common/linux/eintr_wrapper.h" |
43 #include "common/linux/linux_libc_support.h" | 44 #include "common/linux/linux_libc_support.h" |
44 #include "third_party/lss/linux_syscall_support.h" | 45 #include "third_party/lss/linux_syscall_support.h" |
| 46 #include "google_breakpad/processor/minidump.h" |
45 | 47 |
46 using namespace google_breakpad; | 48 using namespace google_breakpad; |
47 | 49 |
48 static void sigchld_handler(int signo) { } | 50 static void sigchld_handler(int signo) { } |
49 | 51 |
50 class ExceptionHandlerTest : public ::testing::Test { | 52 class ExceptionHandlerTest : public ::testing::Test { |
51 protected: | 53 protected: |
52 void SetUp() { | 54 void SetUp() { |
53 // We need to be able to wait for children, so SIGCHLD cannot be SIG_IGN. | 55 // We need to be able to wait for children, so SIGCHLD cannot be SIG_IGN. |
54 struct sigaction sa; | 56 struct sigaction sa; |
(...skipping 64 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
119 filename[len] = 0; | 121 filename[len] = 0; |
120 close(fds[0]); | 122 close(fds[0]); |
121 | 123 |
122 const std::string minidump_filename = std::string("/tmp/") + filename + | 124 const std::string minidump_filename = std::string("/tmp/") + filename + |
123 ".dmp"; | 125 ".dmp"; |
124 | 126 |
125 struct stat st; | 127 struct stat st; |
126 ASSERT_EQ(stat(minidump_filename.c_str(), &st), 0); | 128 ASSERT_EQ(stat(minidump_filename.c_str(), &st), 0); |
127 ASSERT_GT(st.st_size, 0u); | 129 ASSERT_GT(st.st_size, 0u); |
128 unlink(minidump_filename.c_str()); | 130 unlink(minidump_filename.c_str()); |
| 131 } |
| 132 |
| 133 TEST(ExceptionHandlerTest, InstructionPointerMemory) { |
| 134 int fds[2]; |
| 135 ASSERT_NE(pipe(fds), -1); |
| 136 |
| 137 const pid_t child = fork(); |
| 138 if (child == 0) { |
| 139 close(fds[0]); |
| 140 ExceptionHandler handler("/tmp", NULL, DoneCallback, (void*) fds[1], |
| 141 true); |
| 142 // Get some executable memory. |
| 143 const size_t memory_size = 256; // bytes |
| 144 char* memory = |
| 145 reinterpret_cast<char*>(mmap(NULL, |
| 146 memory_size, |
| 147 PROT_READ | PROT_WRITE | PROT_EXEC, |
| 148 MAP_PRIVATE | MAP_ANON, |
| 149 -1, |
| 150 0)); |
| 151 if (!memory) |
| 152 exit(0); |
| 153 |
| 154 // Write some instructions that will crash. Put them in the middle |
| 155 // of the block of memory, because the minidump should contain 128 |
| 156 // bytes on either side of the instruction pointer. |
| 157 const int offset = memory_size / 2; |
| 158 // This crashes with SIGILL on x86/x86-64/arm. |
| 159 const unsigned char instructions[] = { 0xff, 0xff, 0xff, 0xff }; |
| 160 memcpy(memory + offset, instructions, sizeof(instructions)); |
| 161 |
| 162 // Now execute the instructions, which should crash. |
| 163 typedef void (*void_function)(void); |
| 164 void_function memory_function = |
| 165 reinterpret_cast<void_function>(memory + offset); |
| 166 memory_function(); |
| 167 } |
| 168 close(fds[1]); |
| 169 |
| 170 int status; |
| 171 ASSERT_NE(HANDLE_EINTR(waitpid(child, &status, 0)), -1); |
| 172 ASSERT_TRUE(WIFSIGNALED(status)); |
| 173 ASSERT_EQ(WTERMSIG(status), SIGILL); |
| 174 |
| 175 struct pollfd pfd; |
| 176 memset(&pfd, 0, sizeof(pfd)); |
| 177 pfd.fd = fds[0]; |
| 178 pfd.events = POLLIN | POLLERR; |
| 179 |
| 180 const int r = HANDLE_EINTR(poll(&pfd, 1, 0)); |
| 181 ASSERT_EQ(r, 1); |
| 182 ASSERT_TRUE(pfd.revents & POLLIN); |
| 183 |
| 184 uint32_t len; |
| 185 ASSERT_EQ(read(fds[0], &len, sizeof(len)), (ssize_t)sizeof(len)); |
| 186 ASSERT_LT(len, (uint32_t)2048); |
| 187 char* filename = reinterpret_cast<char*>(malloc(len + 1)); |
| 188 ASSERT_EQ(read(fds[0], filename, len), len); |
| 189 filename[len] = 0; |
| 190 close(fds[0]); |
| 191 |
| 192 const std::string minidump_filename = std::string("/tmp/") + filename + |
| 193 ".dmp"; |
| 194 |
| 195 struct stat st; |
| 196 ASSERT_EQ(stat(minidump_filename.c_str(), &st), 0); |
| 197 ASSERT_GT(st.st_size, 0u); |
| 198 |
| 199 // Read the minidump. Locate the exception record and the |
| 200 // memory list, and then ensure that there is a memory region |
| 201 // in the memory list that covers the instruction pointer from |
| 202 // the exception record. |
| 203 Minidump minidump(minidump_filename); |
| 204 ASSERT_TRUE(minidump.Read()); |
| 205 |
| 206 MinidumpException* exception = minidump.GetException(); |
| 207 MinidumpMemoryList* memory_list = minidump.GetMemoryList(); |
| 208 ASSERT_TRUE(exception); |
| 209 ASSERT_TRUE(memory_list); |
| 210 ASSERT_LT(0, memory_list->region_count()); |
| 211 |
| 212 MinidumpContext* context = exception->GetContext(); |
| 213 ASSERT_TRUE(context); |
| 214 |
| 215 u_int64_t instruction_pointer; |
| 216 switch (context->GetContextCPU()) { |
| 217 case MD_CONTEXT_X86: |
| 218 instruction_pointer = context->GetContextX86()->eip; |
| 219 break; |
| 220 case MD_CONTEXT_AMD64: |
| 221 instruction_pointer = context->GetContextAMD64()->rip; |
| 222 break; |
| 223 case MD_CONTEXT_ARM: |
| 224 instruction_pointer = context->GetContextARM()->iregs[15]; |
| 225 break; |
| 226 default: |
| 227 FAIL() << "Unknown context CPU: " << context->GetContextCPU(); |
| 228 break; |
| 229 } |
| 230 |
| 231 MinidumpMemoryRegion* region = |
| 232 memory_list->GetMemoryRegionForAddress(instruction_pointer); |
| 233 EXPECT_TRUE(region); |
| 234 //TODO(ted): verify contents of memory region |
| 235 |
| 236 unlink(minidump_filename.c_str()); |
129 free(filename); | 237 free(filename); |
130 } | 238 } |
131 | 239 |
132 static const unsigned kControlMsgSize = | 240 static const unsigned kControlMsgSize = |
133 CMSG_SPACE(sizeof(int)) + CMSG_SPACE(sizeof(struct ucred)); | 241 CMSG_SPACE(sizeof(int)) + CMSG_SPACE(sizeof(struct ucred)); |
134 | 242 |
135 static bool | 243 static bool |
136 CrashHandler(const void* crash_context, size_t crash_context_size, | 244 CrashHandler(const void* crash_context, size_t crash_context_size, |
137 void* context) { | 245 void* context) { |
138 const int fd = (intptr_t) context; | 246 const int fd = (intptr_t) context; |
(...skipping 97 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
236 int status; | 344 int status; |
237 ASSERT_NE(HANDLE_EINTR(waitpid(child, &status, 0)), -1); | 345 ASSERT_NE(HANDLE_EINTR(waitpid(child, &status, 0)), -1); |
238 ASSERT_TRUE(WIFSIGNALED(status)); | 346 ASSERT_TRUE(WIFSIGNALED(status)); |
239 ASSERT_EQ(WTERMSIG(status), SIGSEGV); | 347 ASSERT_EQ(WTERMSIG(status), SIGSEGV); |
240 | 348 |
241 struct stat st; | 349 struct stat st; |
242 ASSERT_EQ(stat(templ, &st), 0); | 350 ASSERT_EQ(stat(templ, &st), 0); |
243 ASSERT_GT(st.st_size, 0u); | 351 ASSERT_GT(st.st_size, 0u); |
244 unlink(templ); | 352 unlink(templ); |
245 } | 353 } |
OLD | NEW |