1 : // Copyright 2015 Google Inc. All Rights Reserved.
2 : //
3 : // Licensed under the Apache License, Version 2.0 (the "License");
4 : // you may not use this file except in compliance with the License.
5 : // You may obtain a copy of the License at
6 : //
7 : // http://www.apache.org/licenses/LICENSE-2.0
8 : //
9 : // Unless required by applicable law or agreed to in writing, software
10 : // distributed under the License is distributed on an "AS IS" BASIS,
11 : // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 : // See the License for the specific language governing permissions and
13 : // limitations under the License.
14 :
15 : #include "syzygy/refinery/validators/exception_handler_validator.h"
16 :
17 : #include <string>
18 : #include <vector>
19 :
20 : #include "base/strings/stringprintf.h"
21 : #include "syzygy/refinery/core/address.h"
22 : #include "syzygy/refinery/core/addressed_data.h"
23 : #include "syzygy/refinery/process_state/process_state_util.h"
24 : #include "syzygy/refinery/process_state/refinery.pb.h"
25 :
26 m : namespace refinery {
27 m : namespace {
28 :
29 m : bool GetTib(StackRecordPtr stack,
30 m : BytesLayerPtr bytes_layer,
31 m : NT_TIB* tib) {
32 : // Determine the TIB's address.
33 m : const Stack& stack_proto = stack->data();
34 m : if (!stack_proto.has_thread_info() ||
35 m : !stack_proto.thread_info().has_teb_address()) {
36 m : return false;
37 m : }
38 m : const Address tib_address = stack_proto.thread_info().teb_address();
39 :
40 : // Get the bytes backing the TIB.
41 m : const AddressRange tib_range(tib_address, sizeof(tib));
42 m : std::vector<BytesRecordPtr> matching_records;
43 m : bytes_layer->GetRecordsSpanning(tib_range, &matching_records);
44 m : if (matching_records.size() != 1)
45 m : return false;
46 m : BytesRecordPtr bytes_record = matching_records[0];
47 :
48 : // Get the TIB.
49 m : const Bytes& bytes = bytes_record->data();
50 m : AddressedData addressed_data(bytes_record->range(), bytes.data().c_str());
51 m : return addressed_data.GetAt(tib_address, tib);
52 m : }
53 :
54 m : bool GetExceptionRegistrationRecord(
55 m : AddressRange record_range,
56 m : BytesLayerPtr bytes_layer,
57 m : EXCEPTION_REGISTRATION_RECORD* record) {
58 m : DCHECK(record_range.IsValid());
59 :
60 : // Get backing bytes.
61 m : std::vector<BytesRecordPtr> matching_records;
62 m : bytes_layer->GetRecordsSpanning(record_range, &matching_records);
63 m : if (matching_records.size() != 1)
64 m : return false;
65 m : BytesRecordPtr bytes_record = matching_records[0];
66 :
67 : // Get the record.
68 m : const Bytes& bytes = bytes_record->data();
69 m : AddressedData addressed_data(bytes_record->range(), bytes.data().c_str());
70 m : return addressed_data.GetAt(record_range.start(), record);
71 m : }
72 :
73 m : void AddViolation(ValidationReport* report,
74 m : ViolationType type,
75 m : const std::string& description) {
76 m : Violation* violation = report->add_error();
77 m : violation->set_type(type);
78 m : violation->set_description(description);
79 m : }
80 :
81 m : void AddNoChainViolation(StackRecordPtr stack, ValidationReport* report) {
82 m : const uint32_t thread_id = stack->data().thread_info().thread_id();
83 m : std::string description = base::StringPrintf(
84 m : "Thread %d has no exception registration record.", thread_id);
85 m : AddViolation(report, VIOLATION_NO_EXCEPTION_REGISTRATION_RECORD, description);
86 m : }
87 :
88 m : void AddChainOutsideStackViolation(Address record_address,
89 m : StackRecordPtr stack,
90 m : ValidationReport* report) {
91 m : const uint32_t thread_id = stack->data().thread_info().thread_id();
92 m : std::string description = base::StringPrintf(
93 m : "Exception registration record not in stack (thread %d, record at %lld)",
94 m : thread_id, record_address);
95 m : AddViolation(report, VIOLATION_EXCEPTION_REGISTRATION_RECORD_NOT_IN_STACK,
96 m : description);
97 m : }
98 :
99 m : void AddChainAddressDecreaseViolation(Address record_addr,
100 m : Address next_addr,
101 m : StackRecordPtr stack,
102 m : ValidationReport* report) {
103 m : const uint32_t thread_id = stack->data().thread_info().thread_id();
104 m : std::string description = base::StringPrintf(
105 m : "Exception chain address decrease (thread %d: record at %lld points to "
106 m : "%lld).",
107 m : thread_id, record_addr, next_addr);
108 m : AddViolation(report, VIOLATION_EXCEPTION_CHAIN_ADDRESS_DECREASE, description);
109 m : }
110 :
111 m : } // namespace
112 :
113 m : Validator::ValidationResult ExceptionHandlerValidator::Validate(
114 m : ProcessState* process_state,
115 m : ValidationReport* report) {
116 m : DCHECK(process_state); DCHECK(report);
117 :
118 m : BytesLayerPtr bytes_layer;
119 m : if (!process_state->FindLayer(&bytes_layer))
120 m : return VALIDATION_ERROR;
121 m : StackLayerPtr stack_layer;
122 m : if (!process_state->FindLayer(&stack_layer))
123 m : return VALIDATION_ERROR;
124 :
125 m : for (StackRecordPtr stack : *stack_layer) {
126 : // Get the TIB.
127 : // TODO(manzagop): stop assuming 32bit-ness for the minidump. Instead
128 : // implement type detection, eg by looking at the ntll module, and
129 : // requesting its NT_TIB.
130 m : NT_TIB tib = {};
131 m : if (!GetTib(stack, bytes_layer, &tib))
132 m : return VALIDATION_ERROR;
133 :
134 : // Validate there is at least one handler.
135 m : Address record_address = reinterpret_cast<Address>(tib.ExceptionList);
136 m : if (record_address == static_cast<Address>(-1))
137 m : AddNoChainViolation(stack, report);
138 :
139 : // Walk the exception registration record chain
140 : // TODO(manzagop): defend against an infinite loop.
141 m : while (record_address != static_cast<Address>(-1)) {
142 : // Ensure the exception registration record is in stack.
143 m : AddressRange record_range(record_address,
144 m : sizeof(EXCEPTION_REGISTRATION_RECORD));
145 m : if (!stack->range().Contains(record_range)) {
146 m : AddChainOutsideStackViolation(record_address, stack, report);
147 m : break; // Stop processing the chain.
148 m : }
149 :
150 : // Get the record. Failing to obtain it is an error, as the stack is
151 : // assumed present, and we've validated the record is in stack.
152 m : EXCEPTION_REGISTRATION_RECORD record = {};
153 m : if (!GetExceptionRegistrationRecord(record_range, bytes_layer, &record))
154 m : return VALIDATION_ERROR;
155 :
156 : // Validate the address of the next exception registration record.
157 m : Address next_address = reinterpret_cast<Address>(record.Next);
158 m : if (next_address < record_address) {
159 m : AddChainAddressDecreaseViolation(record_address, next_address, stack,
160 m : report);
161 m : break; // Stop processing the chain.
162 m : }
163 :
164 m : record_address = next_address;
165 m : }
166 m : }
167 :
168 m : return VALIDATION_COMPLETE;
169 m : }
170 :
171 m : } // namespace refinery
|