acrn-hypervisor/tools/acrn-crashlog/usercrash/client.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

238 lines
6.0 KiB
C

/*
* Copyright (C) <2018> Intel Corporation
* SPDX-License-Identifier: BSD-3-Clause
*/
/*
* Copyright (C) 2018 Intel Corporation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with 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 <errno.h>
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include "crash_dump.h"
#include "packet.h"
#include "log_sys.h"
#include "protocol.h"
#include "version.h"
/**
* Usercrash works as C/S model: usercrash_c works as usercrash client to
* collect crash logs and information once crash event occurs. For each time,
* usercrash_c receives 3 params from core_dump and sends connect request event
* to usercrash_s, then it receives file fd from server to fill crash info into
* the file. After this work is done, it will notify server that dump work is
* completed.
*/
/**
* @sockfd: the socket fd.
* set_timeout is used to set timeout for the sockfd, in case client is blocked
* when client cannot receive the data from server, or send data to server
*/
static int set_timeout(int sockfd)
{
struct timeval timeout = {50, 0};
if (setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, &timeout,
sizeof(timeout)) != 0) {
LOGE("failed to set receive timeout\n");
return -1;
}
if (setsockopt(sockfd, SOL_SOCKET, SO_SNDTIMEO, &timeout,
sizeof(timeout)) != 0) {
LOGE("failed to set send timeout\n");
return -1;
}
return 0;
}
/**
* @pid: crash process pid.
* @usercrashd_socket: client socket fd pointer, get the value from
* usercrashd_connect function.
* @output_fd: file fd, receives from server to store dump file.
* @name: crash process name
* usercrashd_connect is used to connect to server, and get the crash file fd
* from server
*/
static int usercrashd_connect(int pid, int *usercrashd_socket,
int *output_fd, const char *name)
{
int sockfd;
int tmp_output_fd;
ssize_t rc;
int flags;
struct crash_packet packet = {0};
if (name == NULL) {
LOGE("crash process name is NULL\n");
return -1;
}
sockfd = socket_local_client(SOCKET_NAME, SOCK_SEQPACKET);
if (sockfd == -1) {
LOGE("failed to connect to usercrashd, error (%s)\n",
strerror(errno));
return -1;
}
packet.packet_type = kDumpRequest;
packet.pid = pid;
strncpy(packet.name, name, COMM_NAME_LEN);
packet.name[COMM_NAME_LEN - 1] = '\0';
if (set_timeout(sockfd)) {
close(sockfd);
return -1;
}
if (write(sockfd, &packet, sizeof(packet)) !=
sizeof(packet)) {
LOGE("failed to write DumpRequest packet, error (%s)\n",
strerror(errno));
close(sockfd);
return -1;
}
rc = recv_fd(sockfd, &packet, sizeof(packet),
&tmp_output_fd);
if (rc == -1) {
LOGE("failed to read response to DumpRequest packet, ");
LOGE("error (%s)\n", strerror(errno));
close(sockfd);
return -1;
} else if (rc != sizeof(packet)) {
LOGE("received DumpRequest response packet of incorrect ");
LOGE("length (expected %lu, got %ld)\n", sizeof(packet), rc);
goto fail;
}
if (packet.packet_type != kPerformDump) {
LOGE("unexpected dump response:%d\n", packet.packet_type);
goto fail;
}
/**
* Make the fd O_APPEND so that our output is guaranteed to be at the
* end of a file. (This also makes selinux rules consistent, because
* selinux distinguishes between writing to a regular fd, and writing
* to an fd with O_APPEND)
*/
flags = fcntl(tmp_output_fd, F_GETFL);
if (fcntl(tmp_output_fd, F_SETFL, flags | O_APPEND) != 0) {
LOGE("failed to set output fd flags, error (%s)\n",
strerror(errno));
goto fail;
}
*usercrashd_socket = sockfd;
*output_fd = tmp_output_fd;
return 0;
fail:
close(sockfd);
close(tmp_output_fd);
return -1;
}
/**
* @usercrashd_socket: client socket fd, used to communicate with server.
* usercrashd_notify_completion is used to tell the server it has done the
* dump, the server will pop another crash from the queue and execute the dump
* process
*/
static int usercrashd_notify_completion(int usercrashd_socket)
{
struct crash_packet packet = {0};
packet.packet_type = kCompletedDump;
if (set_timeout(usercrashd_socket)) {
close(usercrashd_socket);
return -1;
}
if (write(usercrashd_socket, &packet,
sizeof(packet)) != sizeof(packet)) {
return -1;
}
return 0;
}
static void print_usage(void)
{
printf("usercrash - tool to dump crash info for the crashes in the ");
printf("userspace on sos. It's the client role of usercrash.\n");
printf("[Usage]\n");
printf("\t--coredump, usercrash_c <pid> <comm> <sig> ");
printf("(root role to run)\n");
printf("[Option]\n");
printf("\t-h: print this usage message\n");
printf("\t-v: print usercrash_c version\n");
}
int main(int argc, char *argv[])
{
int pid;
int sig;
int out_fd;
int sock;
int ret;
if (argc > 1) {
if (strcmp(argv[1], "-v") == 0) {
printf("version is %d.%d-%s, build by %s@%s\n",
UC_MAJOR_VERSION, UC_MINOR_VERSION,
UC_BUILD_VERSION, UC_BUILD_USER,
UC_BUILD_TIME);
return 0;
}
if (strcmp(argv[1], "-h") == 0) {
print_usage();
return 0;
}
} else
print_usage();
if (getuid() != 0) {
LOGE("failed to execute usercrash_c, root is required\n");
exit(EXIT_FAILURE);
}
if (argc == 4) {
/* it's from coredump */
pid = atoi(argv[1]);
sig = atoi(argv[3]);
ret = usercrashd_connect(pid, &sock, &out_fd, argv[2]);
if (ret) {
LOGE("usercrashd_connect failed, error (%s)\n",
strerror(errno));
exit(EXIT_FAILURE);
}
crash_dump(pid, sig, out_fd);
close(out_fd);
if (usercrashd_notify_completion(sock)) {
LOGE("failed to notify usercrashd of completion");
close(sock);
exit(EXIT_FAILURE);
}
close(sock);
} else {
print_usage();
return 0;
}
return 0;
}