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