linux-stable/tools/testing/selftests/connector/proc_filter.c
Shuah Khan 04786c0659 selftests: connector: Fix input argument error paths to skip
Fix input argument parsing paths to skip from their error legs.
This fix helps to avoid false test failure reports without running
the test.

Signed-off-by: Shuah Khan <skhan@linuxfoundation.org>
Reviewed-by: Anjali Kulkarni <anjali.k.kulkarni@oracle.com>
Link: https://lore.kernel.org/r/20230729002403.4278-1-skhan@linuxfoundation.org
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
2023-07-31 20:11:42 -07:00

310 lines
7.2 KiB
C

// SPDX-License-Identifier: GPL-2.0-only
#include <sys/types.h>
#include <sys/epoll.h>
#include <sys/socket.h>
#include <linux/netlink.h>
#include <linux/connector.h>
#include <linux/cn_proc.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <strings.h>
#include <errno.h>
#include <signal.h>
#include <string.h>
#include "../kselftest.h"
#define NL_MESSAGE_SIZE (sizeof(struct nlmsghdr) + sizeof(struct cn_msg) + \
sizeof(struct proc_input))
#define NL_MESSAGE_SIZE_NF (sizeof(struct nlmsghdr) + sizeof(struct cn_msg) + \
sizeof(int))
#define MAX_EVENTS 1
volatile static int interrupted;
static int nl_sock, ret_errno, tcount;
static struct epoll_event evn;
static int filter;
#ifdef ENABLE_PRINTS
#define Printf printf
#else
#define Printf ksft_print_msg
#endif
int send_message(void *pinp)
{
char buff[NL_MESSAGE_SIZE];
struct nlmsghdr *hdr;
struct cn_msg *msg;
hdr = (struct nlmsghdr *)buff;
if (filter)
hdr->nlmsg_len = NL_MESSAGE_SIZE;
else
hdr->nlmsg_len = NL_MESSAGE_SIZE_NF;
hdr->nlmsg_type = NLMSG_DONE;
hdr->nlmsg_flags = 0;
hdr->nlmsg_seq = 0;
hdr->nlmsg_pid = getpid();
msg = (struct cn_msg *)NLMSG_DATA(hdr);
msg->id.idx = CN_IDX_PROC;
msg->id.val = CN_VAL_PROC;
msg->seq = 0;
msg->ack = 0;
msg->flags = 0;
if (filter) {
msg->len = sizeof(struct proc_input);
((struct proc_input *)msg->data)->mcast_op =
((struct proc_input *)pinp)->mcast_op;
((struct proc_input *)msg->data)->event_type =
((struct proc_input *)pinp)->event_type;
} else {
msg->len = sizeof(int);
*(int *)msg->data = *(enum proc_cn_mcast_op *)pinp;
}
if (send(nl_sock, hdr, hdr->nlmsg_len, 0) == -1) {
ret_errno = errno;
perror("send failed");
return -3;
}
return 0;
}
int register_proc_netlink(int *efd, void *input)
{
struct sockaddr_nl sa_nl;
int err = 0, epoll_fd;
nl_sock = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_CONNECTOR);
if (nl_sock == -1) {
ret_errno = errno;
perror("socket failed");
return -1;
}
bzero(&sa_nl, sizeof(sa_nl));
sa_nl.nl_family = AF_NETLINK;
sa_nl.nl_groups = CN_IDX_PROC;
sa_nl.nl_pid = getpid();
if (bind(nl_sock, (struct sockaddr *)&sa_nl, sizeof(sa_nl)) == -1) {
ret_errno = errno;
perror("bind failed");
return -2;
}
epoll_fd = epoll_create1(EPOLL_CLOEXEC);
if (epoll_fd < 0) {
ret_errno = errno;
perror("epoll_create1 failed");
return -2;
}
err = send_message(input);
if (err < 0)
return err;
evn.events = EPOLLIN;
evn.data.fd = nl_sock;
if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, nl_sock, &evn) < 0) {
ret_errno = errno;
perror("epoll_ctl failed");
return -3;
}
*efd = epoll_fd;
return 0;
}
static void sigint(int sig)
{
interrupted = 1;
}
int handle_packet(char *buff, int fd, struct proc_event *event)
{
struct nlmsghdr *hdr;
hdr = (struct nlmsghdr *)buff;
if (hdr->nlmsg_type == NLMSG_ERROR) {
perror("NLMSG_ERROR error\n");
return -3;
} else if (hdr->nlmsg_type == NLMSG_DONE) {
event = (struct proc_event *)
((struct cn_msg *)NLMSG_DATA(hdr))->data;
tcount++;
switch (event->what) {
case PROC_EVENT_EXIT:
Printf("Exit process %d (tgid %d) with code %d, signal %d\n",
event->event_data.exit.process_pid,
event->event_data.exit.process_tgid,
event->event_data.exit.exit_code,
event->event_data.exit.exit_signal);
break;
case PROC_EVENT_FORK:
Printf("Fork process %d (tgid %d), parent %d (tgid %d)\n",
event->event_data.fork.child_pid,
event->event_data.fork.child_tgid,
event->event_data.fork.parent_pid,
event->event_data.fork.parent_tgid);
break;
case PROC_EVENT_EXEC:
Printf("Exec process %d (tgid %d)\n",
event->event_data.exec.process_pid,
event->event_data.exec.process_tgid);
break;
case PROC_EVENT_UID:
Printf("UID process %d (tgid %d) uid %d euid %d\n",
event->event_data.id.process_pid,
event->event_data.id.process_tgid,
event->event_data.id.r.ruid,
event->event_data.id.e.euid);
break;
case PROC_EVENT_GID:
Printf("GID process %d (tgid %d) gid %d egid %d\n",
event->event_data.id.process_pid,
event->event_data.id.process_tgid,
event->event_data.id.r.rgid,
event->event_data.id.e.egid);
break;
case PROC_EVENT_SID:
Printf("SID process %d (tgid %d)\n",
event->event_data.sid.process_pid,
event->event_data.sid.process_tgid);
break;
case PROC_EVENT_PTRACE:
Printf("Ptrace process %d (tgid %d), Tracer %d (tgid %d)\n",
event->event_data.ptrace.process_pid,
event->event_data.ptrace.process_tgid,
event->event_data.ptrace.tracer_pid,
event->event_data.ptrace.tracer_tgid);
break;
case PROC_EVENT_COMM:
Printf("Comm process %d (tgid %d) comm %s\n",
event->event_data.comm.process_pid,
event->event_data.comm.process_tgid,
event->event_data.comm.comm);
break;
case PROC_EVENT_COREDUMP:
Printf("Coredump process %d (tgid %d) parent %d, (tgid %d)\n",
event->event_data.coredump.process_pid,
event->event_data.coredump.process_tgid,
event->event_data.coredump.parent_pid,
event->event_data.coredump.parent_tgid);
break;
default:
break;
}
}
return 0;
}
int handle_events(int epoll_fd, struct proc_event *pev)
{
char buff[CONNECTOR_MAX_MSG_SIZE];
struct epoll_event ev[MAX_EVENTS];
int i, event_count = 0, err = 0;
event_count = epoll_wait(epoll_fd, ev, MAX_EVENTS, -1);
if (event_count < 0) {
ret_errno = errno;
if (ret_errno != EINTR)
perror("epoll_wait failed");
return -3;
}
for (i = 0; i < event_count; i++) {
if (!(ev[i].events & EPOLLIN))
continue;
if (recv(ev[i].data.fd, buff, sizeof(buff), 0) == -1) {
ret_errno = errno;
perror("recv failed");
return -3;
}
err = handle_packet(buff, ev[i].data.fd, pev);
if (err < 0)
return err;
}
return 0;
}
int main(int argc, char *argv[])
{
int epoll_fd, err;
struct proc_event proc_ev;
struct proc_input input;
signal(SIGINT, sigint);
if (argc > 2) {
printf("Expected 0(assume no-filter) or 1 argument(-f)\n");
exit(KSFT_SKIP);
}
if (argc == 2) {
if (strcmp(argv[1], "-f") == 0) {
filter = 1;
} else {
printf("Valid option : -f (for filter feature)\n");
exit(KSFT_SKIP);
}
}
if (filter) {
input.event_type = PROC_EVENT_NONZERO_EXIT;
input.mcast_op = PROC_CN_MCAST_LISTEN;
err = register_proc_netlink(&epoll_fd, (void*)&input);
} else {
enum proc_cn_mcast_op op = PROC_CN_MCAST_LISTEN;
err = register_proc_netlink(&epoll_fd, (void*)&op);
}
if (err < 0) {
if (err == -2)
close(nl_sock);
if (err == -3) {
close(nl_sock);
close(epoll_fd);
}
exit(1);
}
while (!interrupted) {
err = handle_events(epoll_fd, &proc_ev);
if (err < 0) {
if (ret_errno == EINTR)
continue;
if (err == -2)
close(nl_sock);
if (err == -3) {
close(nl_sock);
close(epoll_fd);
}
exit(1);
}
}
if (filter) {
input.mcast_op = PROC_CN_MCAST_IGNORE;
send_message((void*)&input);
} else {
enum proc_cn_mcast_op op = PROC_CN_MCAST_IGNORE;
send_message((void*)&op);
}
close(epoll_fd);
close(nl_sock);
printf("Done total count: %d\n", tcount);
exit(0);
}