acrn-hypervisor/tools/acrn-crashlog/usercrash/crash_dump.c
xiaojin2 cb39badf82 tools: acrn-crashlog: fix potential issues under common and usercrash
This patch is to fix buffer overflow, return value not unified and
variable type not matched issues. And add some judge logic to improve
code quality.

Changes:
1. Handle the fd properly in the failing case.
2. Fix buffer overflow issues and null pointer access issues.
3. Fix the format issue in log_sys.c.
4. Remove the useless branch and adjust the function logic.
5. Add some checks for the string length before using strcpy/strcat/memcpy.
6. Fix strncpy null-terminated issues.
7. Change the return value to unify the return type.

Signed-off-by: CHEN Gang <gang.c.chen@intel.com>
Signed-off-by: xiaojin2 <xiaojing.liu@intel.com>
Reviewed-by: Zhi Jin <zhi.jin@intel.com>
Reviewed-by: Liu Xinwu <xinwu.liu@intel.com>
Acked-by: Zhang Di <di.zhang@intel.com>
2018-06-21 11:29:20 +08:00

312 lines
6.3 KiB
C

/*
* Copyright (C) <2018> Intel Corporation
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <string.h>
#include <unistd.h>
#include <stdarg.h>
#include "log_sys.h"
#include "crash_dump.h"
#include "cmdutils.h"
#include "fsutils.h"
#include "strutils.h"
#define GET_TID "/proc/%d/status"
#define GET_COMM "/proc/%d/exe"
#define GET_K_STACK "/proc/%d/stack"
#define GET_MAPS "/proc/%d/maps"
#define GET_OPEN_FILES "/proc/%d/fd"
#define DEBUGGER_SIGNAL (__SIGRTMIN + 3)
#define DUMP_FILE "/tmp/core"
#define BUFFER_SIZE 8196
#define LINK_LEN 512
static void loginfo(int fd, const char *fmt, ...)
{
char buf[512];
va_list ap;
size_t len;
va_start(ap, fmt);
vsnprintf(buf, sizeof(buf), fmt, ap);
va_end(ap);
len = strlen(buf);
if (len <= 0)
return;
write(fd, buf, len);
}
static const char *get_signame(int sig)
{
switch (sig) {
case SIGABRT:
return "SIGABRT";
case SIGBUS:
return "SIGBUS";
case SIGFPE:
return "SIGFPE";
case SIGILL:
return "SIGILL";
case SIGSEGV:
return "SIGSEGV";
#if defined(SIGSTKFLT)
case SIGSTKFLT:
return "SIGSTKFLT";
#endif
case SIGSTOP:
return "SIGSTOP";
case SIGSYS:
return "SIGSYS";
case SIGTRAP:
return "SIGTRAP";
case DEBUGGER_SIGNAL:
return "<debuggerd signal>";
default:
return "?";
}
}
/**
* Save core dump to file.
* @filename: core dump file name
* return 0 on success, or -1 on error
*/
static int save_coredump(const char *filename)
{
size_t read_len;
size_t write_len;
char input_buffer[BUFFER_SIZE];
FILE *dump_file;
/* open dump file in write and binary mode */
dump_file = fopen(filename, "wb");
if (dump_file == NULL) {
LOGE("fopen core file failed\n");
return -1;
}
/* Read from STDIN and write to dump file */
while (1) {
read_len = fread(input_buffer, 1, BUFFER_SIZE, stdin);
if (read_len == 0)
break;
write_len = fwrite(input_buffer, 1, read_len, dump_file);
if (write_len == 0) {
LOGE("fwrite error\n");
fclose(dump_file);
return -1;
}
}
fclose(dump_file);
return 0;
}
static int get_backtrace(int pid, int fd, int sig, const char *comm)
{
char *membkt;
char format[512];
loginfo(fd, "\nBackTrace:\n\n");
memset(format, 0, sizeof(format));
if (sig == DEBUGGER_SIGNAL) {
snprintf(format, sizeof(format), "-p %d", pid);
} else {
snprintf(format, sizeof(format), "%s %s", comm, DUMP_FILE);
if (save_coredump(DUMP_FILE) == -1) {
LOGE("save core file failed\n");
return -1;
}
}
membkt = exec_out2mem(GET_GDB_INFO, format);
if (!membkt) {
LOGE("get gdb info failed\n");
return -1;
}
write(fd, membkt, strlen(membkt));
free(membkt);
return 0;
}
/**
* Save process proc info to file.
* @pid: process pid
* @fd: usercrash file fd
* @path: process proc path
* @name: a string save to file
* return 0 on success, or -1 on error
*/
static int save_proc_info(int pid, int fd, const char *path, const char *name)
{
int ret;
char *data;
char format[128];
unsigned long size;
loginfo(fd, "\n%s:\n\n", name);
memset(format, 0, sizeof(format));
snprintf(format, sizeof(format), path, pid);
ret = read_file(format, &size, (void *)&data);
if (ret) {
LOGE("read file failed\n");
return -1;
}
write(fd, data, size);
free(data);
return 0;
}
static int get_openfiles(int pid, int fd, const char *path, const char *name)
{
int ret;
int loop;
int fdcount;
char *fdname;
char format[128];
char *files[256];
char linkname[LINK_LEN];
loginfo(fd, "\n%s:\n\n", name);
memset(format, 0, sizeof(format));
snprintf(format, sizeof(format), path, pid);
fdcount = lsdir(format, files, ARRAY_SIZE(files));
if (fdcount < 0) {
LOGE("get fd list failed\n");
return -1;
}
for (loop = 2; loop < fdcount; loop++) {
memset(linkname, 0, LINK_LEN);
ret = readlink(files[loop], linkname, LINK_LEN);
if (ret < 0 || ret >= LINK_LEN) {
LOGE("get fd link fd failed\n");
continue;
}
fdname = strrchr(files[loop], '/') + 1;
loginfo(fd, "%s -> %s\n", fdname, linkname);
}
for (loop = 0; loop < fdcount; loop++)
free(files[loop]);
return 0;
}
static int save_usercrash_file(int pid, int tgid, const char *comm,
int sig, int out_fd)
{
int ret;
loginfo(out_fd, "*** *** *** *** *** *** *** *** *** *** *** *** *** ");
loginfo(out_fd, "*** *** ***\n");
loginfo(out_fd, "pid: %d, tgid: %d, comm: %s\n\n\n", pid, tgid, comm);
loginfo(out_fd, "signal: %d (%s)\n", sig, get_signame(sig));
ret = save_proc_info(pid, out_fd, GET_K_STACK, "Stack");
if (ret) {
LOGE("save stack failed\n");
return -1;
}
ret = save_proc_info(pid, out_fd, GET_MAPS, "Maps");
if (ret) {
LOGE("save maps failed\n");
return -1;
}
ret = get_openfiles(pid, out_fd, GET_OPEN_FILES, "Open files");
if (ret) {
LOGE("save openfiles failed\n");
return -1;
}
ret = get_backtrace(pid, out_fd, sig, comm);
if (ret) {
LOGE("save backtrace failed\n");
return -1;
}
return 0;
}
static int get_key_value(int pid, const char *path, const char *key,
char *value, size_t value_len)
{
int len;
int ret;
unsigned long size;
char *data;
char *msg;
char *start;
char *end;
char format[128];
memset(format, 0, sizeof(format));
snprintf(format, sizeof(format), path, pid);
ret = read_file(format, &size, (void *)&data);
if (ret || !data) {
LOGE("read file failed\n");
return -1;
}
msg = strstr(data, key);
if (!msg) {
LOGE("find key:%s failed\n", key);
free(data);
return -1;
}
end = strchr(msg, '\n');
if (end == NULL)
end = data + size;
start = msg + strlen(key);
len = end - start;
if (len >= (int)value_len) {
free(data);
return -1;
}
memcpy(value, start, len);
*(value + len) = 0;
free(data);
return 0;
}
/**
* Get and save process info to out_fd.
* @pid: process pid
* @sig: signal from core dump
* @out_fd: file fd to save info
*/
void crash_dump(int pid, int sig, int out_fd)
{
int tgid;
int ret;
char comm[LINK_LEN];
char result[16];
char format[128];
memset(format, 0, sizeof(format));
snprintf(format, sizeof(format), GET_COMM, pid);
ret = readlink(format, comm, LINK_LEN);
if (ret < 0 || ret >= LINK_LEN) {
LOGE("get process exe link failed\n");
return;
}
comm[ret] = '\0';
ret = get_key_value(pid, GET_TID, "Tgid:", result, sizeof(result));
if (ret) {
LOGE("get Tgid error\n");
return;
}
tgid = atoi(result);
if (!sig)
sig = DEBUGGER_SIGNAL;
if (save_usercrash_file(pid, tgid, comm, sig, out_fd))
LOGE("dump log file failed\n");
}