1 : // Copyright 2014 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/kasko/service_bridge.h"
16 :
17 : #include <windows.h> // NOLINT
18 : #include <rpc.h>
19 : #include <stdint.h>
20 :
21 : #include "base/logging.h"
22 : #include "base/process/process_handle.h"
23 : #include "syzygy/common/com_utils.h"
24 : #include "syzygy/common/rpc/helpers.h"
25 : #include "syzygy/kasko/minidump_request.h"
26 : #include "syzygy/kasko/service.h"
27 :
28 m : namespace kasko {
29 m : namespace {
30 m : ServiceBridge* g_service_bridge = NULL;
31 m : } // namespace
32 m : } // namespace kasko
33 :
34 : // RPC calls all come through this single free function. We use the singleton
35 : // g_service_bridge to forward the call to the running Service.
36 m : boolean KaskoService_SendDiagnosticReport(handle_t IDL_handle,
37 m : MinidumpRequest request) {
38 m : DCHECK(kasko::g_service_bridge);
39 :
40 m : base::ProcessId client_process_id =
41 m : ::common::rpc::GetClientProcessID(IDL_handle);
42 m : if (!client_process_id)
43 m : return false;
44 :
45 m : kasko::MinidumpRequest internal_request;
46 :
47 m : internal_request.client_exception_pointers = true;
48 m : internal_request.exception_info_address = request.exception_info_address;
49 :
50 m : switch (request.type) {
51 m : case SMALL_DUMP:
52 m : internal_request.type = kasko::MinidumpRequest::SMALL_DUMP_TYPE;
53 m : break;
54 m : case LARGER_DUMP:
55 m : internal_request.type = kasko::MinidumpRequest::LARGER_DUMP_TYPE;
56 m : break;
57 m : case FULL_DUMP:
58 m : internal_request.type = kasko::MinidumpRequest::FULL_DUMP_TYPE;
59 m : break;
60 m : default:
61 m : NOTREACHED();
62 m : break;
63 m : }
64 :
65 m : for (unsigned long i = 0; i < request.user_selected_memory_ranges_size; ++i) {
66 m : kasko::MinidumpRequest::MemoryRange internal_memory_range = {
67 m : static_cast<uint32_t>(
68 m : request.user_selected_memory_ranges[i].base_address),
69 m : static_cast<uint32_t>(request.user_selected_memory_ranges[i].length)};
70 m : internal_request.user_selected_memory_ranges.push_back(
71 m : internal_memory_range);
72 m : }
73 :
74 m : for (unsigned long i = 0; i < request.crash_keys_size; ++i) {
75 m : if (!request.crash_keys[i].name || !request.crash_keys[i].value)
76 m : continue;
77 :
78 m : internal_request.crash_keys.push_back(kasko::MinidumpRequest::CrashKey(
79 m : request.crash_keys[i].name, request.crash_keys[i].value));
80 m : }
81 :
82 m : for (unsigned long i = 0; i < request.custom_streams_size; ++i) {
83 m : if (!request.custom_streams[i].size)
84 m : continue;
85 m : kasko::MinidumpRequest::CustomStream internal_custom_stream = {
86 m : request.custom_streams[i].type,
87 m : reinterpret_cast<const void*>(request.custom_streams[i].data),
88 m : request.custom_streams[i].size};
89 m : internal_request.custom_streams.push_back(internal_custom_stream);
90 m : }
91 :
92 m : kasko::g_service_bridge->service_->SendDiagnosticReport(
93 m : client_process_id, request.thread_id, internal_request);
94 :
95 m : return true;
96 m : }
97 :
98 m : namespace kasko {
99 :
100 m : ServiceBridge::ServiceBridge(const base::string16& protocol,
101 m : const base::string16& endpoint,
102 m : std::unique_ptr<Service> service)
103 m : : protocol_(protocol),
104 m : endpoint_(endpoint),
105 m : service_(std::move(service)),
106 m : running_(false) {
107 : // It's a bad idea to have two instances stepping on each other's toes.
108 m : CHECK(!g_service_bridge);
109 :
110 m : DCHECK(!protocol_.empty());
111 m : DCHECK(!endpoint_.empty());
112 m : DCHECK(service_);
113 m : g_service_bridge = this;
114 m : }
115 :
116 m : ServiceBridge::~ServiceBridge() {
117 : // It's a bad idea to shut down without stopping the service. It's also a bad
118 : // idea to block unexpectedly in our destructor.
119 m : CHECK(!running_);
120 :
121 m : DCHECK_EQ(this, g_service_bridge);
122 m : g_service_bridge = NULL;
123 m : }
124 :
125 m : bool ServiceBridge::Run() {
126 m : if (running_) return true;
127 :
128 m : RPC_STATUS status = ::RpcServerUseProtseqEp(
129 m : common::rpc::AsRpcWstr(&protocol_[0]), RPC_C_LISTEN_MAX_CALLS_DEFAULT,
130 m : common::rpc::AsRpcWstr(&endpoint_[0]), NULL /* Security descriptor. */);
131 :
132 : // RPC_S_DUPLICATE_ENDPOINT seems to be possible if a previous instance has
133 : // already registered this protocol and endpoint. The end result is still that
134 : // the endpoint is properly configured for this protocol.
135 m : if (status != RPC_S_OK && status != RPC_S_DUPLICATE_ENDPOINT) {
136 m : LOG(ERROR) << "Failed to init RPC protocol: " << ::common::LogWe(status)
137 m : << ".";
138 m : } else {
139 m : std::unique_ptr<common::rpc::ScopedRpcInterfaceRegistration>
140 m : interface_registration(new common::rpc::ScopedRpcInterfaceRegistration(
141 m : KaskoService_Kasko_v1_0_s_ifspec));
142 :
143 m : if (interface_registration->status() == RPC_S_OK) {
144 m : status = ::RpcServerListen(1, // Minimum number of handler threads.
145 m : RPC_C_LISTEN_MAX_CALLS_DEFAULT, TRUE);
146 :
147 m : if (status != RPC_S_OK) {
148 m : LOG(ERROR) << "Failed to run RPC server: " << ::common::LogWe(status)
149 m : << ".";
150 m : } else {
151 m : running_ = true;
152 m : interface_registration_ = std::move(interface_registration);
153 m : }
154 m : }
155 m : }
156 :
157 m : return running_;
158 m : }
159 :
160 m : void ServiceBridge::Stop() {
161 m : if (!running_) return;
162 :
163 : // This call prevents new requests from being accepted.
164 m : RPC_STATUS status = ::RpcMgmtStopServerListening(NULL);
165 m : if (status != RPC_S_OK) {
166 : // If this fails, we could end up servicing calls in a bad state.
167 m : LOG(FATAL) << "Failed to stop the RPC server: " << ::common::LogWe(status)
168 m : << ".";
169 m : }
170 :
171 : // This call will block until all active requests are completed.
172 m : status = ::RpcMgmtWaitServerListen();
173 m : if (status != RPC_S_OK) {
174 : // If this fails, we could end up servicing calls in a bad state.
175 m : LOG(FATAL) << "Failed to wait for RPC server shutdown: "
176 m : << ::common::LogWe(status) << ".";
177 m : }
178 :
179 m : interface_registration_.reset();
180 m : running_ = false;
181 m : }
182 :
183 m : } // namespace kasko
|