BuildRequires: libdw-devel libdw
BuildRequires: libcap-devel
BuildRequires: pkgconfig(json-c)
+BuildRequires: pkgconfig(openssl1.1)
%if %{with doc}
BuildRequires: doxygen
#define WAIT_FOR_OPT_TIMEOUT_SEC 60
#define SPACE_REQUIRED_KB 1024
+#define HASH_LEN_MAX 0x100
+#define HASH_SEP '='
#define MINICOREDUMPER_TIMEOUT_MS DEFAULT_COMMAND_TIMEOUT_MS
#define LIVEDUMPER_TIMEOUT_MS DEFAULT_COMMAND_TIMEOUT_MS
"--prstatus_fd", prstatus_fd_str,
"--signal", sig_str,
"--tid-comm", cinfo->comm,
+ "--hash_native", cinfo->call_stack_hash ?: "none",
legacy_notification_str,
NULL };
return is_ok;
}
-static bool execute_crash_stack(const struct crash_info *cinfo, int *exit_code)
+static bool execute_crash_stack(struct crash_info *cinfo, int *exit_code)
{
char pid_str[11], tid_str[11], sig_str[11], prstatus_fd_str[11];
bool is_ok = false;
"--pid", pid_str,
"--tid", tid_str,
"--sig", sig_str,
+ "--output", cinfo->info_path,
"--output-json", cinfo->info_json,
"--prstatus_fd", prstatus_fd_str,
NULL };
- int fd = open(cinfo->info_path, O_WRONLY | O_CREAT, 0600);
- if (fd < 0) {
- _E("open %s error: %m", cinfo->info_path);
+ int p[2];
+ if (pipe2(p, O_NONBLOCK) < 0) {
+ _E("Cannot create a pipe (%d): %m", errno);
return false;
}
- spawn_param_s param = { .fn = spawn_setstdout, .u.int_val = fd };
+ spawn_param_s param = { .fn = spawn_setstdout, .u.int_val = p[1] };
is_ok = spawn_wait(args, NULL, ¶m, CRASH_STACK_TIMEOUT_MS, exit_code);
- close(fd);
+
+ char hash[HASH_LEN_MAX];
+ if (read(p[0], hash, sizeof(hash)) == -1)
+ _E("Read hash error: %m");
+ else
+ _D("Call stack hash: %s", hash);
+
+ hash[HASH_LEN_MAX-1] = '\0';
+ close(p[0]);
+ close(p[1]);
+
+ char *separator = strchr(hash, HASH_SEP);
+ char *endofline = strchr(hash, '\n');
+ if (separator == NULL || endofline == NULL || endofline < (separator + 1) || endofline < 0 || separator < 0) {
+ _E("Unsuspected hash format");
+ goto out;
+ }
+
+ cinfo->call_stack_hash = strndup(separator + 1, endofline - separator - 1);
+ if (cinfo->call_stack_hash == NULL)
+ _E("Saving the hash failed: %m");
out:
return is_ok;
free(cinfo->appid);
free(cinfo->pkgid);
free(cinfo->executable_path);
+ free(cinfo->call_stack_hash);
}
void crash_info_init(struct crash_info *cinfo)
cinfo->appid = NULL;
cinfo->pkgid = NULL;
cinfo->lock_fd = -1;
+ cinfo->call_stack_hash = NULL;
}
static void release_crashed_process()
char *executable_path;
char *appid;
char *pkgid;
+ char *call_stack_hash;
time_t time_info;
int lock_fd;
};
#define ARM_REG_PC 15
#define AARCH64_REG_LR 30
+enum signal_type {
+ SIG_NORMAL = 0,
+ SIG_EXTENDED = 1
+};
+
struct RegInfo {
char *name;
long long int value;
char *appid;
char *pkgid;
char *tid_comm;
+ char *hash_native;
};
static int _get_important_registers(int fd, struct RegInfo **reg_info)
return g_variant_new("(ssss)", notify_params->cmd_name, notify_params->cmd_path, notify_params->appid, notify_params->pkgid);
}
-static GVariant *build_message_data(const struct NotifyParams *notify_params)
+static GVariant *build_message_data(const struct NotifyParams *notify_params, const enum signal_type sig_type)
{
assert(notify_params);
g_variant_builder_close(&md_builder);
}
+ if (sig_type == SIG_NORMAL) {
+ g_variant_builder_open(&md_builder, G_VARIANT_TYPE("{sv}"));
+ g_variant_builder_add(&md_builder, "s", "sys.callstack_native_hash");
+ g_variant_builder_add(&md_builder, "v", g_variant_new_string(notify_params->hash_native));
+ g_variant_builder_close(&md_builder);
+ }
+
struct RegInfo *reg_info;
int regs_count = _get_important_registers(notify_params->prstatus_fd, ®_info);
*/
static bool send_signals(GDBusConnection *conn, const struct NotifyParams *notify_params)
{
- GVariant *data = build_message_data(notify_params);
+ GVariant *data = build_message_data(notify_params, SIG_EXTENDED);
if (!data) {
_E("Error while preparing data for " PROCESS_CRASHED_EX " signal");
return false;
(void)g_variant_ref_sink(data);
send_one_signal(conn, PROCESS_CRASHED_EX, data);
+ g_variant_unref(data);
- GVariant *legacy = notify_params->legacy_notification
+ data = notify_params->legacy_notification
? build_legacy_message_data(notify_params)
- : data;
+ : build_message_data(notify_params, SIG_NORMAL);
- if (legacy)
- send_one_signal(conn, PROCESS_CRASHED, legacy);
+ if (data)
+ send_one_signal(conn, PROCESS_CRASHED, data);
else
_W("Error while preparing data for " PROCESS_CRASHED " signal");
- /* No need to free `legacy' GVariant as it's either already freed by the send call
- * (ie. it's floating ref in legacy case) or referencing sinked ref, which is freed
- * below.
- */
g_variant_unref(data);
GError *error = NULL;
FLAG_PRSTATUS_FD,
FLAG_SIGNAL,
FLAG_TID_COMM,
+ FLAG_HASH_NATIVE,
FLAG_LEGACY_NOTIFICATION,
};
static const struct option options[] = {
{ .name = "prstatus_fd", .has_arg = required_argument, .flag = NULL, .val = FLAG_PRSTATUS_FD },
{ .name = "signal", .has_arg = required_argument, .flag = NULL, .val = FLAG_SIGNAL },
{ .name = "tid-comm", .has_arg = required_argument, .flag = NULL, .val = FLAG_TID_COMM },
+ { .name = "hash_native", .has_arg = required_argument, .flag = NULL, .val = FLAG_HASH_NATIVE },
{ .name = "legacy-sig", .has_arg = no_argument, .flag = NULL, .val = FLAG_LEGACY_NOTIFICATION },
{ NULL },
};
params->signal = atoi(optarg);
else if (FLAG_TID_COMM == val)
params->tid_comm = optarg;
+ else if (FLAG_HASH_NATIVE == val)
+ params->hash_native = optarg;
else if (FLAG_LEGACY_NOTIFICATION == val)
params->legacy_notification = true;
} while (val != -1);
{
assert(progname);
- printf("%s --prstatus_fd N --pid PID --tid TID --cmdline CMDLINE --cmdpath CMDPATH --appid APPID --pkgid PKGID --signal SIGNALNR [--legacy-sig]\n", progname);
+ printf("%s --prstatus_fd N --pid PID --tid TID --cmdline CMDLINE --cmdpath CMDPATH --appid APPID --pkgid PKGID --signal SIGNALNR --hash_native HASH [--legacy-sig]\n", progname);
}
int main(int ac, char *av[])
pkg_check_modules(LIBUNWIND REQUIRED libunwind-generic)
pkg_check_modules(DLOG REQUIRED dlog)
pkg_check_modules(JSON REQUIRED json-c)
-set_property(TARGET ${CRASH_STACK_BIN} APPEND_STRING PROPERTY COMPILE_FLAGS "${JSON_CFLAGS} ${LIBUNWIND_CFLAGS} ${DLOG_CFLAGS}")
+pkg_check_modules(LIBCRYPTO REQUIRED libcrypto1.1)
+set_property(TARGET ${CRASH_STACK_BIN} APPEND_STRING PROPERTY COMPILE_FLAGS "${JSON_CFLAGS} ${LIBUNWIND_CFLAGS} ${DLOG_CFLAGS} ${LIBCRYPTO_CFLAGS}")
# Linking
-target_link_libraries(${CRASH_STACK_BIN} ${DLOG_LIBRARIES} ${LIBUNWIND_LIBRARIES} ${EBL_LIBRARY} ${JSON_LIBRARIES} dl stdc++)
+target_link_libraries(${CRASH_STACK_BIN} ${DLOG_LIBRARIES} ${LIBUNWIND_LIBRARIES} ${EBL_LIBRARY} ${JSON_LIBRARIES} ${LIBCRYPTO_LIBRARIES} dl stdc++)
# Installing
install(TARGETS ${CRASH_STACK_BIN} DESTINATION libexec)
#include <json-c/json.h>
#include <limits.h>
#include <linux/prctl.h>
+#include <openssl/sha.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
json_object_object_add(jobj, K_CALLSTACK, j_callstack);
}
+static void __print_hash(Callstack *callstack, json_object *jobj, struct addr_node *maps)
+{
+ assert(callstack);
+ assert(maps);
+
+ SHA256_CTX hash_ctx;
+ SHA256_Init(&hash_ctx);
+
+ for (size_t i = 0; i < callstack->elems; i++) {
+ // We won't be able to substract the start address so we skip
+ // this entry
+ if (callstack->proc[i].module_name == NULL)
+ continue;
+
+ struct addr_node *m_node = maps;
+ while (m_node) {
+ if (m_node->fpath == NULL)
+ continue;
+
+ if (strcmp(m_node->fpath, callstack->proc[i].module_name) == 0)
+ break;
+
+ m_node = m_node->next;
+ }
+ if (!m_node) {
+ _W("Not found maps entry for %s", callstack->proc[i].module_name);
+ continue;
+ }
+ uintptr_t addr = callstack->proc[i].addr + callstack->proc[i].module_offset - m_node->startaddr;
+ SHA256_Update(&hash_ctx, &addr, sizeof(addr));
+ }
+
+ unsigned char hash[SHA256_DIGEST_LENGTH];
+ SHA256_Final(hash, &hash_ctx);
+
+ char hash_str[SHA256_DIGEST_LENGTH*2+1];
+
+ for (int i = 0; i < SHA256_DIGEST_LENGTH; i++)
+ snprintf(&hash_str[i*2], 2 + 1, "%02x", hash[i]);
+
+ static const char *hash_desc = "STACK_SHA256";
+ char *hash_str_full;
+ if (asprintf(&hash_str_full, "%s=%s", hash_desc, hash_str) == -1 || hash_str_full == NULL) {
+ _E("Hash saving error: %m");
+ return;
+ }
+
+ _I("%s", hash_str_full);
+ json_object_object_add(jobj, K_NATIVE_HASH, json_object_new_string(hash_str));
+ fprintf(stdout, "%s\n", hash_str_full);
+ free(hash_str_full);
+}
+
void callstack_constructor(Callstack *callstack)
{
size_t it;
* @param jobj JSON object
* @param pid PID of the inspected process
*/
-static void __crash_stack_print_maps(json_object *jobj, pid_t pid)
+static struct addr_node* __crash_stack_print_maps(json_object *jobj, pid_t pid)
{
char file_path[PATH_MAX];
struct addr_node *head = NULL;
close(fd);
}
if (head == NULL)
- return;
+ return NULL;
t_node = head;
json_object *j_maps = json_object_new_array();
}
json_object_object_add(jobj, K_MAPS, j_maps);
- free_all_nodes(head);
+ return head;
}
static struct addr_node *get_addr_list_from_maps(int fd)
pid_t pid = 0;
pid_t tid = 0;
char *p = NULL;
+ int res = 0;
json_object *j_root = json_object_new_object();
json_object_object_add(j_root, K_REP_VER, json_object_new_int(INFO_JSON_VERSION));
__crash_stack_print_threads(j_root, pid, tid);
/* Maps information */
- __crash_stack_print_maps(j_root, pid);
+ struct addr_node *maps = __crash_stack_print_maps(j_root, pid);
if (pid <= 1) {
_E( "Usage: %s [--output file] [--erroutput file] [--output-json file] [--pid <pid> [--tid <tid>]]",
argv[0]);
- return 1;
+ res = 1;
+ goto exit;
}
- if (-1 == __get_registers_fd(tid, prstatus_fd))
- return 3333;
+ if (-1 == __get_registers_fd(tid, prstatus_fd)) {
+ res = 3333;
+ goto exit;
+ }
/* Unwind call stack */
Callstack callstack;
/* Print the results */
__print_callstack(&callstack, j_root, tid);
+ if (maps)
+ __print_hash(&callstack, j_root, maps);
+
if (jsonoutputfile != NULL) {
fprintf(jsonoutputfile, "%s", json_object_to_json_string_ext(j_root, JSON_C_TO_STRING_PRETTY));
fclose(jsonoutputfile);
callstack_destructor(&callstack);
json_object_put(j_root);
- return 0;
+exit:
+ if (maps)
+ free_all_nodes(maps);
+ return res;
}
#define K_CALL_ADDR "addr"
#define K_CALL_M_NAME "module_name"
#define K_CALL_M_OFFSET "module_offset"
+#define K_NATIVE_HASH "native_hash"
#define REG_AARCH64_SP 31
#define REG_AARCH64_PC 32
fprintf(fp, "End of Call Stack\n");
}
+static void print_native_hash(json_object *j_root, FILE *fp)
+{
+ assert(j_root);
+ assert(fp);
+
+ json_object *j_native_hash;
+ if (!json_object_object_get_ex(j_root, K_NATIVE_HASH, &j_native_hash)) {
+ _W("No native hash in the JSON object");
+ return;
+ }
+
+ json_object_get_string(j_native_hash);
+ fprintf(fp,
+ "STACK_SHA256: %s\n",
+ json_object_get_string(j_native_hash));
+}
+
+
void print_report_info(json_object *j_root, FILE *fp)
{
assert(j_root);
print_threads(j_root, fp);
print_maps(j_root, fp);
print_callstack(j_root, fp);
+ print_native_hash(j_root, fp);
}
for TMPFILE in $TMP1 $TMP2; do
PATTERN="path=/Org/Tizen/System/Crash/Crash; interface=org\.tizen\.system\.crash\.Crash; member=ProcessCrashed"
score=0
+ if [ "${TMPFILE}" = ${TMP1} ]; then
+ MAX_SCORE=6
+ else
+ MAX_SCORE=5
+ fi
+
if egrep "$PATTERN" $TMPFILE; then
if egrep "string \"kenny" $TMPFILE; then
score=$(($score + 1))
has_process_crashed_ex=yes
fi
- if [ $score -eq 5 ]; then
+ if [ "${TMPFILE}" = ${TMP1} ]; then
+ if egrep -A1 "string \"sys.callstack_native_hash" $TMPFILE | egrep 'variant.*string \"57b32ee3a2bb53a816dd102657ca210d84b9b9ac9a765f811f8131011b40a2a9'; then
+ score=$(($score + 1))
+ fi
+ fi
+
+ if [ $score -eq ${MAX_SCORE} ]; then
continue
fi
fi