acrn-hypervisor/tools/acrn-crashlog/common/cmdutils.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

237 lines
5.1 KiB
C

/*
* Copyright (C) 2018 Intel Corporation
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <unistd.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <malloc.h>
#include <errno.h>
#include <stdarg.h>
#include <sys/wait.h>
#include "cmdutils.h"
#include "strutils.h"
#include "log_sys.h"
/**
* Execute a command described in an array of pointers to null-terminated
* strings, and redirect the output to specified file. The file will be
* created/truncated if it exists/non-exists. This function will be blocked
* until the child process exits.
*
* @param argv An array of pointers to null-terminated strings that
* represent the argument list available to the new program.
* The array of pointers must be terminated by a null pointer.
* @param outfile File to record command's output, NULL means that this
* function doesn't handle command's output.
*
* @return If a child process could not be created, or its status could not be
* retrieved, or it was killed/stopped by signal, the return value
* is -1.
* If all system calls succeed, then the return value is the
* termination status of the child process used to execute command.
*/
int execv_out2file(char *argv[], char *outfile)
{
pid_t pid;
pid = fork();
if (pid < 0) {
LOGE("fork error (%s)\n", strerror(errno));
return -1;
}
if (pid == 0) {
int res;
int fd = -1;
if (outfile) {
fd = open(outfile, O_WRONLY | O_CREAT | O_TRUNC, 0664);
if (fd < 0) {
LOGE("open error (%s)\n", strerror(errno));
return -1;
}
res = dup2(fd, STDOUT_FILENO);
if (res < 0) {
close(fd);
LOGE("dup2 error (%s)\n", strerror(errno));
return -1;
}
}
res = execvp(argv[0], argv);
if (res == -1) {
if (fd > 0)
close(fd);
LOGE("execvp (%s) failed, error (%s)\n", argv[0],
strerror(errno));
return -1;
}
} else {
pid_t res;
int status;
int exit_status;
res = waitpid(pid, &status, 0);
if (res == -1) {
LOGE("waitpid failed, error (%s)\n", strerror(errno));
return res;
}
if (WIFEXITED(status)) {
exit_status = WEXITSTATUS(status);
if (!exit_status)
LOGI("%s exited, status=0\n", argv[0]);
else
LOGE("%s exited, status=%d\n", argv[0],
exit_status);
return exit_status;
} else if (WIFSIGNALED(status)) {
LOGE("%s killed by signal %d\n", argv[0],
WTERMSIG(status));
} else if (WIFSTOPPED(status)) {
LOGE("%s stopped by signal %d\n", argv[0],
WSTOPSIG(status));
}
}
return -1;
}
int debugfs_cmd(char *loop_dev, char *cmd, char *outfile)
{
char *argv[5] = {"debugfs", "-R", NULL, NULL, 0};
argv[2] = cmd;
argv[3] = loop_dev;
return execv_out2file(argv, outfile);
}
/**
* Execute a command described in format string, and redirect the output
* to specified file. The file will be created/truncated if it
* exists/non-exists. This function will be blocked until the child process
* exits.
*
* @param outfile File to record command's output, NULL means that this
* function doesn't handle command's output.
* @param fmt Format string of command.
*
* @return If a child process could not be created, or its status could not be
* retrieved, or it was killed/stopped by signal, the return value
* is -1.
* If all system calls succeed, then the return value is the
* termination status of the child process used to execute command.
*/
int exec_out2file(char *outfile, char *fmt, ...)
{
va_list args;
char *cmd;
char *start;
char *p;
int ret;
int argc;
char **argv;
int i = 0;
va_start(args, fmt);
ret = vasprintf(&cmd, fmt, args);
va_end(args);
if (ret < 0)
return ret;
strtrim(cmd);
argc = strcnt(cmd, ' ') + 1;
argv = (char **)calloc(argc + 1, sizeof(char *));
if (argv == NULL) {
free(cmd);
LOGE("calloc failed, error (%s)\n", strerror(errno));
return -1;
}
/* string to argv[] */
start = cmd;
argv[i++] = start;
while (start && (p = strchr(start, ' '))) {
argv[i++] = p + 1;
*p = 0;
if (*(p + 1) != '"')
start = p + 1;
else
start = strchr(p + 2, '"');
}
ret = execv_out2file(argv, outfile);
free(argv);
free(cmd);
return ret;
}
/**
* Execute a command described in format string, and redirect the output
* to memory. The memory is allocated by this function and needs to be freed
* after return.
*
* @param fmt Format string of command.
*
* @return a pointer to command's output if successful, or NULL if not.
*/
char *exec_out2mem(char *fmt, ...)
{
va_list args;
char *cmd;
FILE *pp;
char *out = NULL;
char *new;
char tmp[1024];
int memsize = 0;
int newlen = 0;
int len = 0;
int ret;
va_start(args, fmt);
ret = vasprintf(&cmd, fmt, args);
va_end(args);
if (ret < 0)
return NULL;
pp = popen(cmd, "r");
if (!pp)
goto free_cmd;
while (fgets(tmp, 1024, pp) != NULL) {
newlen += strlen(tmp);
if (newlen + 1 > memsize) {
memsize += 1024;
new = realloc(out, memsize);
if (!new) {
if (out) {
free(out);
out = NULL;
}
goto end;
} else {
out = new;
}
}
memcpy(out + len, tmp, strlen(tmp) + 1);
len = newlen;
}
end:
pclose(pp);
free_cmd:
free(cmd);
return out;
}