Sat, 03 Jan 2015 20:18:00 +0100
Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.
michael@0 | 1 | // Copyright (c) 2010 Google Inc. |
michael@0 | 2 | // All rights reserved. |
michael@0 | 3 | // |
michael@0 | 4 | // Redistribution and use in source and binary forms, with or without |
michael@0 | 5 | // modification, are permitted provided that the following conditions are |
michael@0 | 6 | // met: |
michael@0 | 7 | // |
michael@0 | 8 | // * Redistributions of source code must retain the above copyright |
michael@0 | 9 | // notice, this list of conditions and the following disclaimer. |
michael@0 | 10 | // * Redistributions in binary form must reproduce the above |
michael@0 | 11 | // copyright notice, this list of conditions and the following disclaimer |
michael@0 | 12 | // in the documentation and/or other materials provided with the |
michael@0 | 13 | // distribution. |
michael@0 | 14 | // * Neither the name of Google Inc. nor the names of its |
michael@0 | 15 | // contributors may be used to endorse or promote products derived from |
michael@0 | 16 | // this software without specific prior written permission. |
michael@0 | 17 | // |
michael@0 | 18 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
michael@0 | 19 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
michael@0 | 20 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
michael@0 | 21 | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
michael@0 | 22 | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
michael@0 | 23 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
michael@0 | 24 | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
michael@0 | 25 | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
michael@0 | 26 | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
michael@0 | 27 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
michael@0 | 28 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
michael@0 | 29 | |
michael@0 | 30 | // exploitability_win.cc: Windows specific exploitability engine. |
michael@0 | 31 | // |
michael@0 | 32 | // Provides a guess at the exploitability of the crash for the Windows |
michael@0 | 33 | // platform given a minidump and process_state. |
michael@0 | 34 | // |
michael@0 | 35 | // Author: Cris Neckar |
michael@0 | 36 | |
michael@0 | 37 | #include <vector> |
michael@0 | 38 | |
michael@0 | 39 | #include "processor/exploitability_win.h" |
michael@0 | 40 | |
michael@0 | 41 | #include "common/scoped_ptr.h" |
michael@0 | 42 | #include "google_breakpad/common/minidump_exception_win32.h" |
michael@0 | 43 | #include "google_breakpad/processor/minidump.h" |
michael@0 | 44 | #include "processor/disassembler_x86.h" |
michael@0 | 45 | #include "processor/logging.h" |
michael@0 | 46 | |
michael@0 | 47 | #include "third_party/libdisasm/libdis.h" |
michael@0 | 48 | |
michael@0 | 49 | namespace google_breakpad { |
michael@0 | 50 | |
michael@0 | 51 | // The cutoff that we use to judge if and address is likely an offset |
michael@0 | 52 | // from various interesting addresses. |
michael@0 | 53 | static const uint64_t kProbableNullOffset = 4096; |
michael@0 | 54 | static const uint64_t kProbableStackOffset = 8192; |
michael@0 | 55 | |
michael@0 | 56 | // The various cutoffs for the different ratings. |
michael@0 | 57 | static const size_t kHighCutoff = 100; |
michael@0 | 58 | static const size_t kMediumCutoff = 80; |
michael@0 | 59 | static const size_t kLowCutoff = 50; |
michael@0 | 60 | static const size_t kInterestingCutoff = 25; |
michael@0 | 61 | |
michael@0 | 62 | // Predefined incremental values for conditional weighting. |
michael@0 | 63 | static const size_t kTinyBump = 5; |
michael@0 | 64 | static const size_t kSmallBump = 20; |
michael@0 | 65 | static const size_t kMediumBump = 50; |
michael@0 | 66 | static const size_t kLargeBump = 70; |
michael@0 | 67 | static const size_t kHugeBump = 90; |
michael@0 | 68 | |
michael@0 | 69 | // The maximum number of bytes to disassemble past the program counter. |
michael@0 | 70 | static const size_t kDisassembleBytesBeyondPC = 2048; |
michael@0 | 71 | |
michael@0 | 72 | ExploitabilityWin::ExploitabilityWin(Minidump *dump, |
michael@0 | 73 | ProcessState *process_state) |
michael@0 | 74 | : Exploitability(dump, process_state) { } |
michael@0 | 75 | |
michael@0 | 76 | ExploitabilityRating ExploitabilityWin::CheckPlatformExploitability() { |
michael@0 | 77 | MinidumpException *exception = dump_->GetException(); |
michael@0 | 78 | if (!exception) { |
michael@0 | 79 | BPLOG(INFO) << "Minidump does not have exception record."; |
michael@0 | 80 | return EXPLOITABILITY_ERR_PROCESSING; |
michael@0 | 81 | } |
michael@0 | 82 | |
michael@0 | 83 | const MDRawExceptionStream *raw_exception = exception->exception(); |
michael@0 | 84 | if (!raw_exception) { |
michael@0 | 85 | BPLOG(INFO) << "Could not obtain raw exception info."; |
michael@0 | 86 | return EXPLOITABILITY_ERR_PROCESSING; |
michael@0 | 87 | } |
michael@0 | 88 | |
michael@0 | 89 | const MinidumpContext *context = exception->GetContext(); |
michael@0 | 90 | if (!context) { |
michael@0 | 91 | BPLOG(INFO) << "Could not obtain exception context."; |
michael@0 | 92 | return EXPLOITABILITY_ERR_PROCESSING; |
michael@0 | 93 | } |
michael@0 | 94 | |
michael@0 | 95 | MinidumpMemoryList *memory_list = dump_->GetMemoryList(); |
michael@0 | 96 | bool memory_available = true; |
michael@0 | 97 | if (!memory_list) { |
michael@0 | 98 | BPLOG(INFO) << "Minidump memory segments not available."; |
michael@0 | 99 | memory_available = false; |
michael@0 | 100 | } |
michael@0 | 101 | uint64_t address = process_state_->crash_address(); |
michael@0 | 102 | uint32_t exception_code = raw_exception->exception_record.exception_code; |
michael@0 | 103 | |
michael@0 | 104 | uint32_t exploitability_weight = 0; |
michael@0 | 105 | |
michael@0 | 106 | uint64_t stack_ptr = 0; |
michael@0 | 107 | uint64_t instruction_ptr = 0; |
michael@0 | 108 | uint64_t this_ptr = 0; |
michael@0 | 109 | |
michael@0 | 110 | switch (context->GetContextCPU()) { |
michael@0 | 111 | case MD_CONTEXT_X86: |
michael@0 | 112 | stack_ptr = context->GetContextX86()->esp; |
michael@0 | 113 | instruction_ptr = context->GetContextX86()->eip; |
michael@0 | 114 | this_ptr = context->GetContextX86()->ecx; |
michael@0 | 115 | break; |
michael@0 | 116 | case MD_CONTEXT_AMD64: |
michael@0 | 117 | stack_ptr = context->GetContextAMD64()->rsp; |
michael@0 | 118 | instruction_ptr = context->GetContextAMD64()->rip; |
michael@0 | 119 | this_ptr = context->GetContextAMD64()->rcx; |
michael@0 | 120 | break; |
michael@0 | 121 | default: |
michael@0 | 122 | BPLOG(INFO) << "Unsupported architecture."; |
michael@0 | 123 | return EXPLOITABILITY_ERR_PROCESSING; |
michael@0 | 124 | } |
michael@0 | 125 | |
michael@0 | 126 | // Check if we are executing on the stack. |
michael@0 | 127 | if (instruction_ptr <= (stack_ptr + kProbableStackOffset) && |
michael@0 | 128 | instruction_ptr >= (stack_ptr - kProbableStackOffset)) |
michael@0 | 129 | exploitability_weight += kHugeBump; |
michael@0 | 130 | |
michael@0 | 131 | switch (exception_code) { |
michael@0 | 132 | // This is almost certainly recursion. |
michael@0 | 133 | case MD_EXCEPTION_CODE_WIN_STACK_OVERFLOW: |
michael@0 | 134 | exploitability_weight += kTinyBump; |
michael@0 | 135 | break; |
michael@0 | 136 | |
michael@0 | 137 | // These exceptions tend to be benign and we can generally ignore them. |
michael@0 | 138 | case MD_EXCEPTION_CODE_WIN_INTEGER_DIVIDE_BY_ZERO: |
michael@0 | 139 | case MD_EXCEPTION_CODE_WIN_INTEGER_OVERFLOW: |
michael@0 | 140 | case MD_EXCEPTION_CODE_WIN_FLOAT_DIVIDE_BY_ZERO: |
michael@0 | 141 | case MD_EXCEPTION_CODE_WIN_FLOAT_INEXACT_RESULT: |
michael@0 | 142 | case MD_EXCEPTION_CODE_WIN_FLOAT_OVERFLOW: |
michael@0 | 143 | case MD_EXCEPTION_CODE_WIN_FLOAT_UNDERFLOW: |
michael@0 | 144 | case MD_EXCEPTION_CODE_WIN_IN_PAGE_ERROR: |
michael@0 | 145 | exploitability_weight += kTinyBump; |
michael@0 | 146 | break; |
michael@0 | 147 | |
michael@0 | 148 | // These exceptions will typically mean that we have jumped where we |
michael@0 | 149 | // shouldn't. |
michael@0 | 150 | case MD_EXCEPTION_CODE_WIN_ILLEGAL_INSTRUCTION: |
michael@0 | 151 | case MD_EXCEPTION_CODE_WIN_FLOAT_INVALID_OPERATION: |
michael@0 | 152 | case MD_EXCEPTION_CODE_WIN_PRIVILEGED_INSTRUCTION: |
michael@0 | 153 | exploitability_weight += kLargeBump; |
michael@0 | 154 | break; |
michael@0 | 155 | |
michael@0 | 156 | // These represent bugs in exception handlers. |
michael@0 | 157 | case MD_EXCEPTION_CODE_WIN_INVALID_DISPOSITION: |
michael@0 | 158 | case MD_EXCEPTION_CODE_WIN_NONCONTINUABLE_EXCEPTION: |
michael@0 | 159 | exploitability_weight += kSmallBump; |
michael@0 | 160 | break; |
michael@0 | 161 | |
michael@0 | 162 | case MD_EXCEPTION_CODE_WIN_HEAP_CORRUPTION: |
michael@0 | 163 | case MD_EXCEPTION_CODE_WIN_STACK_BUFFER_OVERRUN: |
michael@0 | 164 | exploitability_weight += kHugeBump; |
michael@0 | 165 | break; |
michael@0 | 166 | |
michael@0 | 167 | case MD_EXCEPTION_CODE_WIN_GUARD_PAGE_VIOLATION: |
michael@0 | 168 | exploitability_weight += kLargeBump; |
michael@0 | 169 | break; |
michael@0 | 170 | |
michael@0 | 171 | case MD_EXCEPTION_CODE_WIN_ACCESS_VIOLATION: |
michael@0 | 172 | bool near_null = (address <= kProbableNullOffset); |
michael@0 | 173 | bool bad_read = false; |
michael@0 | 174 | bool bad_write = false; |
michael@0 | 175 | if (raw_exception->exception_record.number_parameters >= 1) { |
michael@0 | 176 | MDAccessViolationTypeWin av_type = |
michael@0 | 177 | static_cast<MDAccessViolationTypeWin> |
michael@0 | 178 | (raw_exception->exception_record.exception_information[0]); |
michael@0 | 179 | switch (av_type) { |
michael@0 | 180 | case MD_ACCESS_VIOLATION_WIN_READ: |
michael@0 | 181 | bad_read = true; |
michael@0 | 182 | if (near_null) |
michael@0 | 183 | exploitability_weight += kSmallBump; |
michael@0 | 184 | else |
michael@0 | 185 | exploitability_weight += kMediumBump; |
michael@0 | 186 | break; |
michael@0 | 187 | case MD_ACCESS_VIOLATION_WIN_WRITE: |
michael@0 | 188 | bad_write = true; |
michael@0 | 189 | if (near_null) |
michael@0 | 190 | exploitability_weight += kSmallBump; |
michael@0 | 191 | else |
michael@0 | 192 | exploitability_weight += kHugeBump; |
michael@0 | 193 | break; |
michael@0 | 194 | case MD_ACCESS_VIOLATION_WIN_EXEC: |
michael@0 | 195 | if (near_null) |
michael@0 | 196 | exploitability_weight += kSmallBump; |
michael@0 | 197 | else |
michael@0 | 198 | exploitability_weight += kHugeBump; |
michael@0 | 199 | break; |
michael@0 | 200 | default: |
michael@0 | 201 | BPLOG(INFO) << "Unrecognized access violation type."; |
michael@0 | 202 | return EXPLOITABILITY_ERR_PROCESSING; |
michael@0 | 203 | break; |
michael@0 | 204 | } |
michael@0 | 205 | MinidumpMemoryRegion *instruction_region = 0; |
michael@0 | 206 | if (memory_available) { |
michael@0 | 207 | instruction_region = |
michael@0 | 208 | memory_list->GetMemoryRegionForAddress(instruction_ptr); |
michael@0 | 209 | } |
michael@0 | 210 | if (!near_null && instruction_region && |
michael@0 | 211 | context->GetContextCPU() == MD_CONTEXT_X86 && |
michael@0 | 212 | (bad_read || bad_write)) { |
michael@0 | 213 | // Perform checks related to memory around instruction pointer. |
michael@0 | 214 | uint32_t memory_offset = |
michael@0 | 215 | instruction_ptr - instruction_region->GetBase(); |
michael@0 | 216 | uint32_t available_memory = |
michael@0 | 217 | instruction_region->GetSize() - memory_offset; |
michael@0 | 218 | available_memory = available_memory > kDisassembleBytesBeyondPC ? |
michael@0 | 219 | kDisassembleBytesBeyondPC : available_memory; |
michael@0 | 220 | if (available_memory) { |
michael@0 | 221 | const uint8_t *raw_memory = |
michael@0 | 222 | instruction_region->GetMemory() + memory_offset; |
michael@0 | 223 | DisassemblerX86 disassembler(raw_memory, |
michael@0 | 224 | available_memory, |
michael@0 | 225 | instruction_ptr); |
michael@0 | 226 | disassembler.NextInstruction(); |
michael@0 | 227 | if (bad_read) |
michael@0 | 228 | disassembler.setBadRead(); |
michael@0 | 229 | else |
michael@0 | 230 | disassembler.setBadWrite(); |
michael@0 | 231 | if (disassembler.currentInstructionValid()) { |
michael@0 | 232 | // Check if the faulting instruction falls into one of |
michael@0 | 233 | // several interesting groups. |
michael@0 | 234 | switch (disassembler.currentInstructionGroup()) { |
michael@0 | 235 | case libdis::insn_controlflow: |
michael@0 | 236 | exploitability_weight += kLargeBump; |
michael@0 | 237 | break; |
michael@0 | 238 | case libdis::insn_string: |
michael@0 | 239 | exploitability_weight += kHugeBump; |
michael@0 | 240 | break; |
michael@0 | 241 | default: |
michael@0 | 242 | break; |
michael@0 | 243 | } |
michael@0 | 244 | // Loop the disassembler through the code and check if it |
michael@0 | 245 | // IDed any interesting conditions in the near future. |
michael@0 | 246 | // Multiple flags may be set so treat each equally. |
michael@0 | 247 | while (disassembler.NextInstruction() && |
michael@0 | 248 | disassembler.currentInstructionValid() && |
michael@0 | 249 | !disassembler.endOfBlock()) |
michael@0 | 250 | continue; |
michael@0 | 251 | if (disassembler.flags() & DISX86_BAD_BRANCH_TARGET) |
michael@0 | 252 | exploitability_weight += kLargeBump; |
michael@0 | 253 | if (disassembler.flags() & DISX86_BAD_ARGUMENT_PASSED) |
michael@0 | 254 | exploitability_weight += kTinyBump; |
michael@0 | 255 | if (disassembler.flags() & DISX86_BAD_WRITE) |
michael@0 | 256 | exploitability_weight += kMediumBump; |
michael@0 | 257 | if (disassembler.flags() & DISX86_BAD_BLOCK_WRITE) |
michael@0 | 258 | exploitability_weight += kMediumBump; |
michael@0 | 259 | if (disassembler.flags() & DISX86_BAD_READ) |
michael@0 | 260 | exploitability_weight += kTinyBump; |
michael@0 | 261 | if (disassembler.flags() & DISX86_BAD_BLOCK_READ) |
michael@0 | 262 | exploitability_weight += kTinyBump; |
michael@0 | 263 | if (disassembler.flags() & DISX86_BAD_COMPARISON) |
michael@0 | 264 | exploitability_weight += kTinyBump; |
michael@0 | 265 | } |
michael@0 | 266 | } |
michael@0 | 267 | } |
michael@0 | 268 | if (!near_null && AddressIsAscii(address)) |
michael@0 | 269 | exploitability_weight += kMediumBump; |
michael@0 | 270 | } else { |
michael@0 | 271 | BPLOG(INFO) << "Access violation type parameter missing."; |
michael@0 | 272 | return EXPLOITABILITY_ERR_PROCESSING; |
michael@0 | 273 | } |
michael@0 | 274 | } |
michael@0 | 275 | |
michael@0 | 276 | // Based on the calculated weight we return a simplified classification. |
michael@0 | 277 | BPLOG(INFO) << "Calculated exploitability weight: " << exploitability_weight; |
michael@0 | 278 | if (exploitability_weight >= kHighCutoff) |
michael@0 | 279 | return EXPLOITABILITY_HIGH; |
michael@0 | 280 | if (exploitability_weight >= kMediumCutoff) |
michael@0 | 281 | return EXPLOITABLITY_MEDIUM; |
michael@0 | 282 | if (exploitability_weight >= kLowCutoff) |
michael@0 | 283 | return EXPLOITABILITY_LOW; |
michael@0 | 284 | if (exploitability_weight >= kInterestingCutoff) |
michael@0 | 285 | return EXPLOITABILITY_INTERESTING; |
michael@0 | 286 | |
michael@0 | 287 | return EXPLOITABILITY_NONE; |
michael@0 | 288 | } |
michael@0 | 289 | |
michael@0 | 290 | } // namespace google_breakpad |