// Copyright (C) 2022 Check Point Software Technologies Ltd. All rights reserved. // Licensed under the Apache License, Version 2.0 (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 "signal_handler.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef UNWIND_LIBRARY #define UNW_LOCAL_ONLY #include #include #endif // UNWIND_LIBRARY #include "debug.h" #include "common.h" #include "config.h" #include "mainloop.h" #include "report/log_rest.h" #include "report/report.h" #include "agent_core_utilities.h" #define stack_trace_max_len 64 #define STACK_SIZE (1024 * 1024) // 1 MB stack size using namespace std; using namespace ReportIS; USE_DEBUG_FLAG(D_SIGNAL_HANDLER); class SignalHandler::Impl : Singleton::Provide::From { public: void fini() { if (out_trace_file_fd != -1) close(out_trace_file_fd); out_trace_file_fd = -1; if (alt_stack.ss_sp != nullptr) { free(alt_stack.ss_sp); alt_stack.ss_sp = nullptr; alt_stack_initialized = false; } } void dumpErrorReport(const string &error) override { ofstream export_error_file(trace_file_path); export_error_file << error << endl; } void init() { alt_stack.ss_sp = nullptr; addSignalHandlerRoutine(); addReloadConfigurationRoutine(); } Maybe> getBacktrace() override { #ifdef UNWIND_LIBRARY vector symbols; unw_cursor_t cursor; unw_context_t context; // Initialize cursor to current frame for local unwinding. unw_getcontext(&context); unw_init_local(&cursor, &context); char buf[1024]; // Unwind frames one by one, going up the frame stack. while (unw_step(&cursor) > 0) { unw_word_t offset, pc; unw_get_reg(&cursor, UNW_REG_IP, &pc); if (pc == 0) break; char sym[256]; if (unw_get_proc_name(&cursor, sym, sizeof(sym), &offset) == 0) { char *nameptr = sym; int status; char *demangled = abi::__cxa_demangle(sym, nullptr, nullptr, &status); if (status == 0) { nameptr = demangled; } snprintf(buf, sizeof(buf), "(%s+0x%lx) [0x%lx]", nameptr, offset, pc); free(demangled); } else { snprintf(buf, sizeof(buf), "-- error: unable to obtain symbol name for this frame"); } symbols.push_back(buf); } return symbols; #else // UNWIND_LIBRARY return genError("Could not print any backtrace entries using uclibc (backtrace_symbols not supported)"); #endif // UNWIND_LIBRARY } private: void addSignalHandlerRoutine() { Singleton::Consume::by()->addOneTimeRoutine( I_MainLoop::RoutineType::Offline, [this] () { string service_name = "Unnamed Nano Service"; if (Singleton::exists()) { auto name = Singleton::Consume::by()->get( "Service Name" ); if (name.ok()) service_name = *name; } string service_underscore_name = service_name; replace(service_underscore_name.begin(), service_underscore_name.end(), ' ', '_'); string logFilesPath = getLogFilesPathConfig(); trace_file_path = getConfigurationWithDefault( logFilesPath + "/nano_agent/trace_export_files/" + service_underscore_name + "_trace_file.dbg", "SignalHandler", "outputFilePath" ); ifstream in_trace_file(trace_file_path); if (in_trace_file.peek() != ifstream::traits_type::eof()) { stringstream buffer; buffer << in_trace_file.rdbuf(); if (buffer.str() != " " && buffer.str() != "\n") { const boost::regex reg("(\\+0x[A-z0-9]*)|( [0x[A-z0-9]*])"); const string fixed_trace_str = NGEN::Regex::regexReplace( __FILE__, __LINE__, buffer.str(), reg, "" ); generateLog(fixed_trace_str); dbgInfo(D_SIGNAL_HANDLER) << "Service started after crash ERROR: " << endl << fixed_trace_str; } } in_trace_file.close(); ofstream out_trace_file(trace_file_path, ofstream::out | ofstream::trunc); out_trace_file.close(); setSignalHanlders(); }, "Send crash trace report" ); } void generateLog(const string &trace_file_data) { auto i_time = Singleton::Consume::by(); chrono::microseconds curr_time = i_time!=nullptr ? i_time->getWalltime() : chrono::microseconds(0); if (!Singleton::exists()) return; AudienceTeam audience_team = AudienceTeam::NONE; if (Singleton::exists()) { auto team = Singleton::Consume::by()->get("Audience Team"); if (team.ok()) audience_team = *team; } set tags; Report message_to_fog( "Nano service startup after crash", curr_time, Type::EVENT, Level::LOG, LogLevel::ERROR, Audience::INTERNAL, audience_team, Severity::HIGH, Priority::HIGH, chrono::seconds(0), LogField("agentId", Singleton::Consume::by()->getAgentId()), tags, Tags::INFORMATIONAL ); message_to_fog << LogField("eventMessage", trace_file_data); string fog_signalHandler_uri = getConfigurationWithDefault( "/api/v1/agents/events", "SignalHandler", "fogSignalHandlerURI" ); LogRest signalHandler_client_rest(message_to_fog); Singleton::Consume::by()->sendAsyncMessage( HTTPMethod::POST, fog_signalHandler_uri, signalHandler_client_rest, MessageCategory::LOG ); dbgInfo(D_SIGNAL_HANDLER) << "Sent crash log to fog" << endl; } void setSignalHanlders() { out_trace_file_fd = open(trace_file_path.c_str(), O_CREAT | O_RDWR, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); int errno_copy = errno; if (out_trace_file_fd < 0) { dbgError(D_SIGNAL_HANDLER) << "Failed to open signal handler backtrace file. Path: " << trace_file_path << ", Errno: " << to_string(errno_copy) << ", Error: " << strerror(errno_copy); } setHandlerPerSignalNum(); } bool setupAlternateSignalStack() { if (alt_stack_initialized) return true; alt_stack.ss_sp = malloc(STACK_SIZE); if (alt_stack.ss_sp == nullptr) { dbgWarning(D_SIGNAL_HANDLER) << "Failed to allocate alternate stack"; return false; } alt_stack.ss_size = STACK_SIZE; alt_stack.ss_flags = 0; if (sigaltstack(&alt_stack, nullptr) == -1) { dbgWarning(D_SIGNAL_HANDLER) << "Failed to set up alternate stack"; free(alt_stack.ss_sp); return false; } dbgInfo(D_SIGNAL_HANDLER) << "Alternate stack allocated successfully. Allocated size: " << STACK_SIZE; alt_stack_initialized = true; return true; } void setHandlerPerSignalNum() { static const vector signals = { SIGABRT, SIGKILL, SIGQUIT, SIGINT, SIGTERM, SIGSEGV, SIGBUS, SIGILL, SIGFPE, SIGPIPE, SIGUSR2 }; if (!setupAlternateSignalStack()) { dbgWarning(D_SIGNAL_HANDLER) << "Failed to set up alternate signal stack"; for (int sig : signals) { signal(sig, signalHandlerCB); } return; } struct sigaction sa; memset(&sa, 0, sizeof(sa)); sa.sa_flags = SA_SIGINFO | SA_ONSTACK; sa.sa_sigaction = signalActionHandlerCB; sigemptyset(&sa.sa_mask); for (int sig : signals) { if (sig == SIGKILL || sig == SIGSTOP) { signal(sig, signalHandlerCB); continue; } if (sigaction(sig, &sa, nullptr) == -1) { dbgError(D_SIGNAL_HANDLER) << "Failed to set signal handler for signal " << sig; } } } // LCOV_EXCL_START Reason: Cannot crash unitest or send signal during execution static bool writeData(const char *data, uint32_t len) { uint32_t bytes_sent = 0; while (bytes_sent < len) { int res = write(out_trace_file_fd, data + bytes_sent, len - bytes_sent); if (res <= 0) return false; bytes_sent += res; } return true; } static void signalHandlerCB(int _signal) { const char *signal_name = strsignal(_signal); char signal_num[3]; snprintf(signal_num, sizeof(signal_num), "%d", _signal); if (out_trace_file_fd == -1) exit(_signal); reset_signal_handler = true; switch(_signal) { case SIGABRT: case SIGKILL: case SIGQUIT: case SIGINT: case SIGTERM: { fini_signal_flag = true; return; } case SIGSEGV: case SIGBUS: case SIGILL: case SIGFPE: { break; } case SIGPIPE: { return; } case SIGUSR2: { reload_settings_flag = true; return; } } const char *signal_error_prefix = "Caught signal "; writeData(signal_error_prefix, strlen(signal_error_prefix)); writeData(signal_num, sizeof(signal_num)); if (strlen(signal_name)) { const char *open_braces = "("; writeData(open_braces, strlen(open_braces)); writeData(signal_name, strlen(signal_name)); const char *close_braces = ")\n"; writeData(close_braces, strlen(close_braces)); } printStackTrace(); close(out_trace_file_fd); out_trace_file_fd = -1; exit(_signal); } static void signalActionHandlerCB(int signum, siginfo_t *, void *) { signalHandlerCB(signum); } static void printStackTrace() { #ifdef UNWIND_LIBRARY if (out_trace_file_fd == -1) return; const char *stack_trace_title = "Stack trace:\n"; writeData(stack_trace_title, strlen(stack_trace_title)); unw_cursor_t cursor; unw_context_t uc; unw_getcontext(&uc); if (unw_init_local(&cursor, &uc) < 0) { const char *unw_init_local_error = "unw_init_local failed!\n"; writeData(unw_init_local_error, strlen(unw_init_local_error)); return; } char name[256]; unw_word_t ip, sp, off; for (uint i = 0 ; i < stack_trace_max_len ; i++) { unw_get_reg(&cursor, UNW_REG_IP, &ip); unw_get_reg(&cursor, UNW_REG_SP, &sp); int procNameRc = unw_get_proc_name(&cursor, name, sizeof(name), &off); if (procNameRc == 0 || procNameRc == -UNW_ENOMEM) { const char *open_braces = "<"; writeData(open_braces, strlen(open_braces)); writeData(name, strnlen(name, sizeof(name))); if (procNameRc != 0) { const char *dots = "..."; writeData(dots, strlen(dots)); } const char *close_braces = ">\n"; writeData(close_braces, strlen(close_braces)); } else { const char *error = " -- error: unable to obtain symbol name for this frame\n"; writeData(error, strlen(error)); } if (unw_step(&cursor) <= 0) return; } #else // UNWIND_LIBRARY const char *uclibc_error = "Could not print any backtrace entries using uclibc (backtrace_symbols not supported)\n"; writeData(uclibc_error, strlen(uclibc_error)); #endif // UNWIND_LIBRARY } // LCOV_EXCL_STOP void addReloadConfigurationRoutine() { Singleton::Consume::by()->addOneTimeRoutine( I_MainLoop::RoutineType::System, [&] () { while (true) { if (reset_signal_handler) { setHandlerPerSignalNum(); } if (reload_settings_flag == true) { reload_settings_flag = false; if (reloadConfiguration("")) { dbgInfo(D_SIGNAL_HANDLER) << "Reloaded configuration"; } else { dbgWarning(D_SIGNAL_HANDLER) << "Failed to reload configuration"; } } Singleton::Consume::by()->yield(chrono::seconds(1)); } }, "Reload configuration signal handler" ); } static string trace_file_path; static bool reload_settings_flag; static bool reset_signal_handler; static int out_trace_file_fd; static stack_t alt_stack; static bool alt_stack_initialized; }; string SignalHandler::Impl::trace_file_path; bool SignalHandler::Impl::reload_settings_flag = false; bool SignalHandler::Impl::reset_signal_handler = false; int SignalHandler::Impl::out_trace_file_fd = -1; stack_t SignalHandler::Impl::alt_stack; bool SignalHandler::Impl::alt_stack_initialized = false; SignalHandler::SignalHandler() : Component("SignalHandler"), pimpl(make_unique()) {} SignalHandler::~SignalHandler() {} void SignalHandler::init() { pimpl->init(); } void SignalHandler::preload() { registerExpectedConfiguration("SignalHandler", "outputFilePath"); registerExpectedConfiguration("SignalHandler", "fogSignalHandlerURI"); }