mirror of
https://github.com/openappsec/openappsec.git
synced 2025-06-28 16:41:02 +03:00
500 lines
15 KiB
C++
Executable File
500 lines
15 KiB
C++
Executable File
// 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 <algorithm>
|
|
#include <string>
|
|
#include <memory>
|
|
#include <fstream>
|
|
#include <streambuf>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <fcntl.h>
|
|
#include <unistd.h>
|
|
#include <signal.h>
|
|
#include <iostream>
|
|
#include <iosfwd>
|
|
#include <chrono>
|
|
#include <boost/regex.hpp>
|
|
|
|
#ifdef UNWIND_LIBRARY
|
|
#define UNW_LOCAL_ONLY
|
|
#include <cxxabi.h>
|
|
#include <libunwind.h>
|
|
#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<I_SignalHandler>::From<SignalHandler>
|
|
{
|
|
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<vector<string>>
|
|
getBacktrace() override
|
|
{
|
|
#ifdef UNWIND_LIBRARY
|
|
|
|
vector<string> 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<I_MainLoop>::by<SignalHandler>()->addOneTimeRoutine(
|
|
I_MainLoop::RoutineType::Offline,
|
|
[this] ()
|
|
{
|
|
string service_name = "Unnamed Nano Service";
|
|
if (Singleton::exists<I_Environment>()) {
|
|
auto name = Singleton::Consume<I_Environment>::by<SignalHandler>()->get<string>(
|
|
"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<string>(
|
|
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<I_TimeGet>::by<SignalHandler>();
|
|
chrono::microseconds curr_time = i_time!=nullptr ? i_time->getWalltime() : chrono::microseconds(0);
|
|
|
|
if (!Singleton::exists<I_Messaging>()) return;
|
|
|
|
AudienceTeam audience_team = AudienceTeam::NONE;
|
|
if (Singleton::exists<I_Environment>()) {
|
|
auto team = Singleton::Consume<I_Environment>::by<SignalHandler>()->get<AudienceTeam>("Audience Team");
|
|
if (team.ok()) audience_team = *team;
|
|
}
|
|
|
|
set<ReportIS::Tags> 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<I_AgentDetails>::by<SignalHandler>()->getAgentId()),
|
|
tags,
|
|
Tags::INFORMATIONAL
|
|
);
|
|
|
|
message_to_fog << LogField("eventMessage", trace_file_data);
|
|
|
|
string fog_signalHandler_uri = getConfigurationWithDefault<string>(
|
|
"/api/v1/agents/events",
|
|
"SignalHandler",
|
|
"fogSignalHandlerURI"
|
|
);
|
|
|
|
LogRest signalHandler_client_rest(message_to_fog);
|
|
|
|
Singleton::Consume<I_Messaging>::by<SignalHandler>()->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<int> 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<I_MainLoop>::by<SignalHandler>()->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<I_MainLoop>::by<SignalHandler>()->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<Impl>()) {}
|
|
SignalHandler::~SignalHandler() {}
|
|
|
|
void SignalHandler::init() { pimpl->init(); }
|
|
|
|
void
|
|
SignalHandler::preload()
|
|
{
|
|
registerExpectedConfiguration<int>("SignalHandler", "outputFilePath");
|
|
registerExpectedConfiguration<int>("SignalHandler", "fogSignalHandlerURI");
|
|
}
|