Index: tools/exception_port_tool.cc |
diff --git a/tools/exception_port_tool.cc b/tools/exception_port_tool.cc |
deleted file mode 100644 |
index bc6b637fbc2493a39e5ee4ad4f524d8a27efbe84..0000000000000000000000000000000000000000 |
--- a/tools/exception_port_tool.cc |
+++ /dev/null |
@@ -1,594 +0,0 @@ |
-// Copyright 2014 The Crashpad Authors. All rights reserved. |
-// |
-// Licensed under the Apache License, Version 2.0 (the "License"); |
-// you may not use this file except in compliance with the License. |
-// You may obtain a copy of the License at |
-// |
-// http://www.apache.org/licenses/LICENSE-2.0 |
-// |
-// Unless required by applicable law or agreed to in writing, software |
-// distributed under the License is distributed on an "AS IS" BASIS, |
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
-// See the License for the specific language governing permissions and |
-// limitations under the License. |
- |
-#include <errno.h> |
-#include <getopt.h> |
-#include <libgen.h> |
-#include <mach/mach.h> |
-#include <servers/bootstrap.h> |
-#include <stdio.h> |
-#include <stdlib.h> |
-#include <string.h> |
-#include <unistd.h> |
- |
-#include <string> |
-#include <vector> |
- |
-#include "base/basictypes.h" |
-#include "base/mac/mach_logging.h" |
-#include "base/mac/scoped_mach_port.h" |
-#include "base/strings/stringprintf.h" |
-#include "tools/tool_support.h" |
-#include "util/mach/exception_ports.h" |
-#include "util/mach/mach_extensions.h" |
-#include "util/mach/symbolic_constants_mach.h" |
-#include "util/mach/task_for_pid.h" |
-#include "util/posix/drop_privileges.h" |
-#include "util/stdlib/string_number_conversion.h" |
- |
-namespace crashpad { |
-namespace { |
- |
-//! \brief Manages a pool of Mach send rights, deallocating all send rights upon |
-//! destruction. |
-//! |
-//! This class effectively implements what a vector of |
-//! base::mac::ScopedMachSendRight objects would be. |
-//! |
-//! The various “show” operations performed by this program display Mach ports |
-//! by their names as they are known in this task. For this to be useful, rights |
-//! to the same ports must have consistent names across successive calls. This |
-//! cannot be guaranteed if the rights are deallocated as soon as they are used, |
-//! because if that deallocation causes the task to lose its last right to a |
-//! port, subsequently regaining a right to the same port would cause it to be |
-//! known by a new name in this task. |
-//! |
-//! Instead of immediately deallocating send rights that are used for display, |
-//! they can be added to this pool. The pool collects send rights, ensuring that |
-//! they remain alive in this task, and that subsequent calls that obtain the |
-//! same rights cause them to be known by the same name. All rights are |
-//! deallocated upon destruction. |
-class MachSendRightPool { |
- public: |
- MachSendRightPool() |
- : send_rights_() { |
- } |
- |
- ~MachSendRightPool() { |
- for (mach_port_t send_right : send_rights_) { |
- kern_return_t kr = mach_port_deallocate(mach_task_self(), send_right); |
- MACH_LOG_IF(ERROR, kr != KERN_SUCCESS, kr) << "mach_port_deallocate"; |
- } |
- } |
- |
- //! \brief Adds a send right to the pool. |
- //! |
- //! \param[in] send_right The send right to be added. The pool object takes |
- //! ownership of the send right, which remains valid until the pool object |
- //! is destroyed. |
- //! |
- //! It is possible and in fact likely that one pool will wind up owning the |
- //! same send right multiple times. This is acceptable, because send rights |
- //! are reference-counted. |
- void AddSendRight(mach_port_t send_right) { |
- send_rights_.push_back(send_right); |
- } |
- |
- private: |
- std::vector<mach_port_t> send_rights_; |
- |
- DISALLOW_COPY_AND_ASSIGN(MachSendRightPool); |
-}; |
- |
-struct ExceptionHandlerDescription { |
- ExceptionPorts::TargetType target_type; |
- exception_mask_t mask; |
- exception_behavior_t behavior; |
- thread_state_flavor_t flavor; |
- std::string handler; |
-}; |
- |
-const char kHandlerNull[] = "NULL"; |
-const char kHandlerBootstrapColon[] = "bootstrap:"; |
- |
-// Populates |description| based on a textual representation in |
-// |handler_string_ro|, returning true on success and false on failure (parse |
-// error). The --help string describes the format of |handler_string_ro|. |
-// Briefly, it is a comma-separated string that allows the members of |
-// |description| to be specified as "field=value". Values for "target" can be |
-// "host", "task", or "thread"; values for "handler" are of the form |
-// "bootstrap:service_name" where service_name will be looked up with the |
-// bootstrap server; and values for the other fields are interpreted by |
-// SymbolicConstantsMach. |
-bool ParseHandlerString(const char* handler_string_ro, |
- ExceptionHandlerDescription* description) { |
- const char kTargetEquals[] = "target="; |
- const char kMaskEquals[] = "mask="; |
- const char kBehaviorEquals[] = "behavior="; |
- const char kFlavorEquals[] = "flavor="; |
- const char kHandlerEquals[] = "handler="; |
- |
- std::string handler_string(handler_string_ro); |
- char* handler_string_c = &handler_string[0]; |
- |
- char* token; |
- while ((token = strsep(&handler_string_c, ",")) != nullptr) { |
- if (strncmp(token, kTargetEquals, strlen(kTargetEquals)) == 0) { |
- const char* value = token + strlen(kTargetEquals); |
- if (strcmp(value, "host") == 0) { |
- description->target_type = ExceptionPorts::kTargetTypeHost; |
- } else if (strcmp(value, "task") == 0) { |
- description->target_type = ExceptionPorts::kTargetTypeTask; |
- } else if (strcmp(value, "thread") == 0) { |
- description->target_type = ExceptionPorts::kTargetTypeThread; |
- } else { |
- return false; |
- } |
- } else if (strncmp(token, kMaskEquals, strlen(kMaskEquals)) == 0) { |
- const char* value = token + strlen(kMaskEquals); |
- if (!StringToExceptionMask( |
- value, |
- kAllowFullName | kAllowShortName | kAllowNumber | kAllowOr, |
- &description->mask)) { |
- return false; |
- } |
- } else if (strncmp(token, kBehaviorEquals, strlen(kBehaviorEquals)) == 0) { |
- const char* value = token + strlen(kBehaviorEquals); |
- if (!StringToExceptionBehavior( |
- value, |
- kAllowFullName | kAllowShortName | kAllowNumber, |
- &description->behavior)) { |
- return false; |
- } |
- } else if (strncmp(token, kFlavorEquals, strlen(kFlavorEquals)) == 0) { |
- const char* value = token + strlen(kFlavorEquals); |
- if (!StringToThreadStateFlavor( |
- value, |
- kAllowFullName | kAllowShortName | kAllowNumber, |
- &description->flavor)) { |
- return false; |
- } |
- } else if (strncmp(token, kHandlerEquals, strlen(kHandlerEquals)) == 0) { |
- const char* value = token + strlen(kHandlerEquals); |
- if (strcmp(value, kHandlerNull) != 0 && |
- strncmp(value, |
- kHandlerBootstrapColon, |
- strlen(kHandlerBootstrapColon)) != 0) { |
- return false; |
- } |
- description->handler = std::string(value); |
- } else { |
- return false; |
- } |
- } |
- |
- return true; |
-} |
- |
-// ShowExceptionPorts() shows handlers as numeric mach_port_t values, which are |
-// opaque and meaningless on their own. ShowBootstrapService() can be used to |
-// look up a service with the bootstrap server by name and show its mach_port_t |
-// value, which can then be associated with handlers shown by |
-// ShowExceptionPorts(). Any send rights obtained by this function are added to |
-// |mach_send_right_pool|. |
-void ShowBootstrapService(const std::string& service_name, |
- MachSendRightPool* mach_send_right_pool) { |
- mach_port_t service_port; |
- kern_return_t kr = bootstrap_look_up( |
- bootstrap_port, const_cast<char*>(service_name.c_str()), &service_port); |
- if (kr != BOOTSTRAP_SUCCESS) { |
- BOOTSTRAP_LOG(ERROR, kr) << "bootstrap_look_up " << service_name; |
- return; |
- } |
- |
- mach_send_right_pool->AddSendRight(service_port); |
- |
- printf("service %s %#x\n", service_name.c_str(), service_port); |
-} |
- |
-// Prints information about all exception ports known for |exception_ports|. If |
-// |numeric| is true, all information is printed in numeric form, otherwise, it |
-// will be converted to symbolic constants where possible by |
-// SymbolicConstantsMach. If |is_new| is true, information will be presented as |
-// “new exception ports”, indicating that they show the state of the exception |
-// ports after SetExceptionPort() has been called. Any send rights obtained by |
-// this function are added to |mach_send_right_pool|. |
-void ShowExceptionPorts(const ExceptionPorts& exception_ports, |
- bool numeric, |
- bool is_new, |
- MachSendRightPool* mach_send_right_pool) { |
- const char* target_name = exception_ports.TargetTypeName(); |
- |
- std::vector<ExceptionPorts::ExceptionHandler> handlers; |
- if (!exception_ports.GetExceptionPorts(ExcMaskAll() | EXC_MASK_CRASH, |
- &handlers)) { |
- return; |
- } |
- |
- const char* age_name = is_new ? "new " : ""; |
- |
- if (handlers.size() == 0) { |
- printf("no %s%s exception ports\n", age_name, target_name); |
- } |
- |
- for (size_t port_index = 0; port_index < handlers.size(); ++port_index) { |
- mach_send_right_pool->AddSendRight(handlers[port_index].port); |
- |
- if (numeric) { |
- printf( |
- "%s%s exception port %zu, mask %#x, port %#x, " |
- "behavior %#x, flavor %u\n", |
- age_name, |
- target_name, |
- port_index, |
- handlers[port_index].mask, |
- handlers[port_index].port, |
- handlers[port_index].behavior, |
- handlers[port_index].flavor); |
- } else { |
- std::string mask_string = ExceptionMaskToString( |
- handlers[port_index].mask, kUseShortName | kUnknownIsEmpty | kUseOr); |
- if (mask_string.empty()) { |
- mask_string.assign("?"); |
- } |
- |
- std::string behavior_string = ExceptionBehaviorToString( |
- handlers[port_index].behavior, kUseShortName | kUnknownIsEmpty); |
- if (behavior_string.empty()) { |
- behavior_string.assign("?"); |
- } |
- |
- std::string flavor_string = ThreadStateFlavorToString( |
- handlers[port_index].flavor, kUseShortName | kUnknownIsEmpty); |
- if (flavor_string.empty()) { |
- flavor_string.assign("?"); |
- } |
- |
- printf( |
- "%s%s exception port %zu, mask %#x (%s), port %#x, " |
- "behavior %#x (%s), flavor %u (%s)\n", |
- age_name, |
- target_name, |
- port_index, |
- handlers[port_index].mask, |
- mask_string.c_str(), |
- handlers[port_index].port, |
- handlers[port_index].behavior, |
- behavior_string.c_str(), |
- handlers[port_index].flavor, |
- flavor_string.c_str()); |
- } |
- } |
-} |
- |
-// Sets the exception port for |target_port|, a send right to a thread, task, or |
-// host port, to |description|, which identifies what type of port |target_port| |
-// is and describes an exception port to be set. Returns true on success. |
-// |
-// This function may be called more than once if setting different handlers is |
-// desired. |
-bool SetExceptionPort(const ExceptionHandlerDescription* description, |
- mach_port_t target_port) { |
- base::mac::ScopedMachSendRight service_port_owner; |
- exception_handler_t service_port = MACH_PORT_NULL; |
- kern_return_t kr; |
- if (description->handler.compare( |
- 0, strlen(kHandlerBootstrapColon), kHandlerBootstrapColon) == 0) { |
- const char* service_name = |
- description->handler.c_str() + strlen(kHandlerBootstrapColon); |
- kr = bootstrap_look_up( |
- bootstrap_port, const_cast<char*>(service_name), &service_port); |
- if (kr != BOOTSTRAP_SUCCESS) { |
- BOOTSTRAP_LOG(ERROR, kr) << "bootstrap_look_up " << service_name; |
- return false; |
- } |
- |
- // The service port doesn’t need to be added to a MachSendRightPool because |
- // it’s not used for display at all. ScopedMachSendRight is sufficient. |
- service_port_owner.reset(service_port); |
- } else if (description->handler != kHandlerNull) { |
- return false; |
- } |
- |
- ExceptionPorts exception_ports(description->target_type, target_port); |
- if (!exception_ports.SetExceptionPort(description->mask, |
- service_port, |
- description->behavior, |
- description->flavor)) { |
- return false; |
- } |
- |
- return true; |
-} |
- |
-void Usage(const std::string& me) { |
- fprintf(stderr, |
-"Usage: %s [OPTION]... [COMMAND [ARG]...]\n" |
-"View and change Mach exception ports, and run COMMAND if supplied.\n" |
-"\n" |
-" -s, --set-handler=DESCRIPTION set an exception port to DESCRIPTION, see below\n" |
-" --show-bootstrap=SERVICE look up and display a service registered with\n" |
-" the bootstrap server\n" |
-" -p, --pid=PID operate on PID instead of the current task\n" |
-" -h, --show-host display original host exception ports\n" |
-" -t, --show-task display original task exception ports\n" |
-" --show-thread display original thread exception ports\n" |
-" -H, --show-new-host display modified host exception ports\n" |
-" -T, --show-new-task display modified task exception ports\n" |
-" --show-new-thread display modified thread exception ports\n" |
-" -n, --numeric display values numerically, not symbolically\n" |
-" --help display this help and exit\n" |
-" --version output version information and exit\n" |
-"\n" |
-"Any operations on host exception ports require superuser permissions.\n" |
-"\n" |
-"DESCRIPTION is formatted as a comma-separated sequence of tokens, where each\n" |
-"token consists of a key and value separated by an equals sign. Available keys:\n" |
-" target which target's exception ports to set: host, task, or thread\n" |
-" mask the mask of exception types to handle: CRASH, ALL, or others\n" |
-" behavior the specific exception handler routine to call: DEFAULT, STATE,\n" |
-" or STATE_IDENTITY, possibly with MACH_EXCEPTION_CODES.\n" |
-" flavor the thread state flavor passed to the handler: architecture-specific\n" |
-" handler the exception handler: NULL or bootstrap:SERVICE, indicating that\n" |
-" the handler should be looked up with the bootstrap server\n" |
-"The default DESCRIPTION is\n" |
-" target=task,mask=CRASH,behavior=DEFAULT|MACH,flavor=NONE,handler=NULL\n", |
- me.c_str()); |
- ToolSupport::UsageTail(me); |
-} |
- |
-int ExceptionPortToolMain(int argc, char* argv[]) { |
- const std::string me(basename(argv[0])); |
- |
- enum ExitCode { |
- kExitSuccess = EXIT_SUCCESS, |
- |
- // To differentiate this tool’s errors from errors in the programs it execs, |
- // use a high exit code for ordinary failures instead of EXIT_FAILURE. This |
- // is the same rationale for using the distinct exit codes for exec |
- // failures. |
- kExitFailure = 125, |
- |
- // Like env, use exit code 126 if the program was found but could not be |
- // invoked, and 127 if it could not be found. |
- // http://pubs.opengroup.org/onlinepubs/9699919799/utilities/env.html |
- kExitExecFailure = 126, |
- kExitExecENOENT = 127, |
- }; |
- |
- enum OptionFlags { |
- // “Short” (single-character) options. |
- kOptionSetPort = 's', |
- kOptionPid = 'p', |
- kOptionShowHost = 'h', |
- kOptionShowTask = 't', |
- kOptionShowNewHost = 'H', |
- kOptionShowNewTask = 'T', |
- kOptionNumeric = 'n', |
- |
- // Long options without short equivalents. |
- kOptionLastChar = 255, |
- kOptionShowBootstrap, |
- kOptionShowThread, |
- kOptionShowNewThread, |
- |
- // Standard options. |
- kOptionHelp = -2, |
- kOptionVersion = -3, |
- }; |
- |
- struct { |
- std::vector<const char*> show_bootstrap; |
- std::vector<ExceptionHandlerDescription> set_handler; |
- pid_t pid; |
- task_t alternate_task; |
- bool show_host; |
- bool show_task; |
- bool show_thread; |
- bool show_new_host; |
- bool show_new_task; |
- bool show_new_thread; |
- bool numeric; |
- } options = {}; |
- |
- const struct option long_options[] = { |
- {"set-handler", required_argument, nullptr, kOptionSetPort}, |
- {"show-bootstrap", required_argument, nullptr, kOptionShowBootstrap}, |
- {"pid", required_argument, nullptr, kOptionPid}, |
- {"show-host", no_argument, nullptr, kOptionShowHost}, |
- {"show-task", no_argument, nullptr, kOptionShowTask}, |
- {"show-thread", no_argument, nullptr, kOptionShowThread}, |
- {"show-new-host", no_argument, nullptr, kOptionShowNewHost}, |
- {"show-new-task", no_argument, nullptr, kOptionShowNewTask}, |
- {"show-new-thread", no_argument, nullptr, kOptionShowNewThread}, |
- {"numeric", no_argument, nullptr, kOptionNumeric}, |
- {"help", no_argument, nullptr, kOptionHelp}, |
- {"version", no_argument, nullptr, kOptionVersion}, |
- {nullptr, 0, nullptr, 0}, |
- }; |
- |
- int opt; |
- while ((opt = getopt_long(argc, argv, "+s:p:htHTn", long_options, nullptr)) != |
- -1) { |
- switch (opt) { |
- case kOptionSetPort: { |
- options.set_handler.push_back({}); |
- ExceptionHandlerDescription* description = &options.set_handler.back(); |
- description->target_type = ExceptionPorts::kTargetTypeTask; |
- description->mask = EXC_MASK_CRASH; |
- description->behavior = EXCEPTION_DEFAULT | MACH_EXCEPTION_CODES; |
- description->flavor = THREAD_STATE_NONE; |
- description->handler = "NULL"; |
- if (!ParseHandlerString(optarg, description)) { |
- fprintf(stderr, |
- "%s: invalid exception handler: %s\n", |
- me.c_str(), |
- optarg); |
- return kExitFailure; |
- } |
- break; |
- } |
- case kOptionShowBootstrap: |
- options.show_bootstrap.push_back(optarg); |
- break; |
- case kOptionPid: |
- if (!StringToNumber(optarg, &options.pid)) { |
- fprintf(stderr, "%s: invalid pid: %s\n", me.c_str(), optarg); |
- return kExitFailure; |
- } |
- break; |
- case kOptionShowHost: |
- options.show_host = true; |
- break; |
- case kOptionShowTask: |
- options.show_task = true; |
- break; |
- case kOptionShowThread: |
- options.show_thread = true; |
- break; |
- case kOptionShowNewHost: |
- options.show_new_host = true; |
- break; |
- case kOptionShowNewTask: |
- options.show_new_task = true; |
- break; |
- case kOptionShowNewThread: |
- options.show_new_thread = true; |
- break; |
- case kOptionNumeric: |
- options.numeric = true; |
- break; |
- case kOptionHelp: |
- Usage(me); |
- return kExitSuccess; |
- case kOptionVersion: |
- ToolSupport::Version(me); |
- return kExitSuccess; |
- default: |
- ToolSupport::UsageHint(me, nullptr); |
- return kExitFailure; |
- } |
- } |
- argc -= optind; |
- argv += optind; |
- |
- if (options.show_bootstrap.empty() && !options.show_host && |
- !options.show_task && !options.show_thread && |
- options.set_handler.empty() && argc == 0) { |
- ToolSupport::UsageHint(me, "nothing to do"); |
- return kExitFailure; |
- } |
- |
- base::mac::ScopedMachSendRight alternate_task_owner; |
- if (options.pid) { |
- if (argc) { |
- ToolSupport::UsageHint(me, "cannot combine -p with COMMAND"); |
- return kExitFailure; |
- } |
- |
- options.alternate_task = TaskForPID(options.pid); |
- if (options.alternate_task == TASK_NULL) { |
- return kExitFailure; |
- } |
- alternate_task_owner.reset(options.alternate_task); |
- } |
- |
- // This tool may have been installed as a setuid binary so that TaskForPID() |
- // could succeed. Drop any privileges now that they’re no longer necessary. |
- DropPrivileges(); |
- |
- MachSendRightPool mach_send_right_pool; |
- |
- // Show bootstrap services requested. |
- for (const char* service : options.show_bootstrap) { |
- ShowBootstrapService(service, &mach_send_right_pool); |
- } |
- |
- // Show the original exception ports. |
- if (options.show_host) { |
- ShowExceptionPorts( |
- ExceptionPorts(ExceptionPorts::kTargetTypeHost, HOST_NULL), |
- options.numeric, |
- false, |
- &mach_send_right_pool); |
- } |
- if (options.show_task) { |
- ShowExceptionPorts( |
- ExceptionPorts(ExceptionPorts::kTargetTypeTask, options.alternate_task), |
- options.numeric, |
- false, |
- &mach_send_right_pool); |
- } |
- if (options.show_thread) { |
- ShowExceptionPorts( |
- ExceptionPorts(ExceptionPorts::kTargetTypeThread, THREAD_NULL), |
- options.numeric, |
- false, |
- &mach_send_right_pool); |
- } |
- |
- if (!options.set_handler.empty()) { |
- // Set new exception handlers. |
- for (ExceptionHandlerDescription description : options.set_handler) { |
- if (!SetExceptionPort( |
- &description, |
- description.target_type == ExceptionPorts::kTargetTypeTask |
- ? options.alternate_task |
- : TASK_NULL)) { |
- return kExitFailure; |
- } |
- } |
- |
- // Show changed exception ports. |
- if (options.show_new_host) { |
- ShowExceptionPorts( |
- ExceptionPorts(ExceptionPorts::kTargetTypeHost, HOST_NULL), |
- options.numeric, |
- true, |
- &mach_send_right_pool); |
- } |
- if (options.show_new_task) { |
- ShowExceptionPorts( |
- ExceptionPorts(ExceptionPorts::kTargetTypeTask, |
- options.alternate_task), |
- options.numeric, |
- true, |
- &mach_send_right_pool); |
- } |
- if (options.show_new_thread) { |
- ShowExceptionPorts( |
- ExceptionPorts(ExceptionPorts::kTargetTypeThread, THREAD_NULL), |
- options.numeric, |
- true, |
- &mach_send_right_pool); |
- } |
- } |
- |
- if (argc) { |
- // Using the remaining arguments, start a new program with the new set of |
- // exception ports in effect. |
- execvp(argv[0], argv); |
- PLOG(ERROR) << "execvp " << argv[0]; |
- return errno == ENOENT ? kExitExecENOENT : kExitExecFailure; |
- } |
- |
- return kExitSuccess; |
-} |
- |
-} // namespace |
-} // namespace crashpad |
- |
-int main(int argc, char* argv[]) { |
- return crashpad::ExceptionPortToolMain(argc, argv); |
-} |