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 : namespace refinery {
27 : namespace {
28 :
29 : bool GetTib(StackRecordPtr stack,
30 : BytesLayerPtr bytes_layer,
31 E : NT_TIB* tib) {
32 : // Determine the TIB's address.
33 E : const Stack& stack_proto = stack->data();
34 : if (!stack_proto.has_thread_info() ||
35 E : !stack_proto.thread_info().has_teb_address()) {
36 i : return false;
37 : }
38 E : const Address tib_address = stack_proto.thread_info().teb_address();
39 :
40 : // Get the bytes backing the TIB.
41 E : const AddressRange tib_range(tib_address, sizeof(tib));
42 E : std::vector<BytesRecordPtr> matching_records;
43 E : bytes_layer->GetRecordsSpanning(tib_range, &matching_records);
44 E : if (matching_records.size() != 1)
45 E : return false;
46 E : BytesRecordPtr bytes_record = matching_records[0];
47 :
48 : // Get the TIB.
49 E : const Bytes& bytes = bytes_record->data();
50 E : AddressedData addressed_data(bytes_record->range(), bytes.data().c_str());
51 E : return addressed_data.GetAt(tib_address, tib);
52 E : }
53 :
54 : bool GetExceptionRegistrationRecord(
55 : AddressRange record_range,
56 : BytesLayerPtr bytes_layer,
57 E : EXCEPTION_REGISTRATION_RECORD* record) {
58 E : DCHECK(record_range.IsValid());
59 :
60 : // Get backing bytes.
61 E : std::vector<BytesRecordPtr> matching_records;
62 E : bytes_layer->GetRecordsSpanning(record_range, &matching_records);
63 E : if (matching_records.size() != 1)
64 i : return false;
65 E : BytesRecordPtr bytes_record = matching_records[0];
66 :
67 : // Get the record.
68 E : const Bytes& bytes = bytes_record->data();
69 E : AddressedData addressed_data(bytes_record->range(), bytes.data().c_str());
70 E : return addressed_data.GetAt(record_range.start(), record);
71 E : }
72 :
73 : void AddViolation(ValidationReport* report,
74 : ViolationType type,
75 E : const std::string& description) {
76 E : Violation* violation = report->add_error();
77 E : violation->set_type(type);
78 E : violation->set_description(description);
79 E : }
80 :
81 E : void AddNoChainViolation(StackRecordPtr stack, ValidationReport* report) {
82 E : const uint32 thread_id = stack->data().thread_info().thread_id();
83 : std::string description = base::StringPrintf(
84 E : "Thread %d has no exception registration record.", thread_id);
85 E : AddViolation(report, VIOLATION_NO_EXCEPTION_REGISTRATION_RECORD, description);
86 E : }
87 :
88 : void AddChainOutsideStackViolation(Address record_address,
89 : StackRecordPtr stack,
90 E : ValidationReport* report) {
91 E : const uint32 thread_id = stack->data().thread_info().thread_id();
92 : std::string description = base::StringPrintf(
93 : "Exception registration record not in stack (thread %d, record at %lld)",
94 E : thread_id, record_address);
95 : AddViolation(report, VIOLATION_EXCEPTION_REGISTRATION_RECORD_NOT_IN_STACK,
96 E : description);
97 E : }
98 :
99 : void AddChainAddressDecreaseViolation(Address record_addr,
100 : Address next_addr,
101 : StackRecordPtr stack,
102 E : ValidationReport* report) {
103 E : const uint32 thread_id = stack->data().thread_info().thread_id();
104 : std::string description = base::StringPrintf(
105 : "Exception chain address decrease (thread %d: record at %lld points to "
106 : "%lld).",
107 E : thread_id, record_addr, next_addr);
108 E : AddViolation(report, VIOLATION_EXCEPTION_CHAIN_ADDRESS_DECREASE, description);
109 E : }
110 :
111 : } // namespace
112 :
113 : Validator::ValidationResult ExceptionHandlerValidator::Validate(
114 : ProcessState* process_state,
115 E : ValidationReport* report) {
116 E : DCHECK(process_state); DCHECK(report);
117 :
118 E : BytesLayerPtr bytes_layer;
119 E : if (!process_state->FindLayer(&bytes_layer))
120 i : return VALIDATION_ERROR;
121 E : StackLayerPtr stack_layer;
122 E : if (!process_state->FindLayer(&stack_layer))
123 i : return VALIDATION_ERROR;
124 :
125 E : 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 E : NT_TIB tib = {};
131 E : if (!GetTib(stack, bytes_layer, &tib))
132 E : return VALIDATION_ERROR;
133 :
134 : // Validate there is at least one handler.
135 E : Address record_address = reinterpret_cast<Address>(tib.ExceptionList);
136 E : if (record_address == static_cast<Address>(-1))
137 E : AddNoChainViolation(stack, report);
138 :
139 : // Walk the exception registration record chain
140 : // TODO(manzagop): defend against an infinite loop.
141 E : while (record_address != static_cast<Address>(-1)) {
142 : // Ensure the exception registration record is in stack.
143 : AddressRange record_range(record_address,
144 E : sizeof(EXCEPTION_REGISTRATION_RECORD));
145 E : if (!stack->range().Contains(record_range)) {
146 E : AddChainOutsideStackViolation(record_address, stack, report);
147 E : break; // Stop processing the chain.
148 : }
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 E : EXCEPTION_REGISTRATION_RECORD record = {};
153 E : if (!GetExceptionRegistrationRecord(record_range, bytes_layer, &record))
154 i : return VALIDATION_ERROR;
155 :
156 : // Validate the address of the next exception registration record.
157 E : Address next_address = reinterpret_cast<Address>(record.Next);
158 E : if (next_address < record_address) {
159 : AddChainAddressDecreaseViolation(record_address, next_address, stack,
160 E : report);
161 E : break; // Stop processing the chain.
162 : }
163 :
164 E : record_address = next_address;
165 E : }
166 E : }
167 :
168 E : return VALIDATION_COMPLETE;
169 E : }
170 :
171 : } // namespace refinery
|