Commit ab8ed951 authored by Aaron Campbell's avatar Aaron Campbell Committed by David S. Miller

connector: fix out-of-order cn_proc netlink message delivery

The proc connector messages include a sequence number, allowing userspace
programs to detect lost messages.  However, performing this detection is
currently more difficult than necessary, since netlink messages can be
delivered to the application out-of-order.  To fix this, leave pre-emption
disabled during cn_netlink_send(), and use GFP_NOWAIT.

The following was written as a test case.  Building the kernel w/ make -j32
proved a reliable way to generate out-of-order cn_proc messages.

int
main(int argc, char *argv[])
{
	static uint32_t last_seq[CPU_SETSIZE], seq;
	int cpu, fd;
	struct sockaddr_nl sa;
	struct __attribute__((aligned(NLMSG_ALIGNTO))) {
		struct nlmsghdr nl_hdr;
		struct __attribute__((__packed__)) {
			struct cn_msg cn_msg;
			struct proc_event cn_proc;
		};
	} rmsg;
	struct __attribute__((aligned(NLMSG_ALIGNTO))) {
		struct nlmsghdr nl_hdr;
		struct __attribute__((__packed__)) {
			struct cn_msg cn_msg;
			enum proc_cn_mcast_op cn_mcast;
		};
	} smsg;

	fd = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_CONNECTOR);
	if (fd < 0) {
		perror("socket");
	}

	sa.nl_family = AF_NETLINK;
	sa.nl_groups = CN_IDX_PROC;
	sa.nl_pid = getpid();
	if (bind(fd, (struct sockaddr *)&sa, sizeof(sa)) < 0) {
		perror("bind");
	}

	memset(&smsg, 0, sizeof(smsg));
	smsg.nl_hdr.nlmsg_len = sizeof(smsg);
	smsg.nl_hdr.nlmsg_pid = getpid();
	smsg.nl_hdr.nlmsg_type = NLMSG_DONE;
	smsg.cn_msg.id.idx = CN_IDX_PROC;
	smsg.cn_msg.id.val = CN_VAL_PROC;
	smsg.cn_msg.len = sizeof(enum proc_cn_mcast_op);
	smsg.cn_mcast = PROC_CN_MCAST_LISTEN;
	if (send(fd, &smsg, sizeof(smsg), 0) != sizeof(smsg)) {
		perror("send");
	}

	while (recv(fd, &rmsg, sizeof(rmsg), 0) == sizeof(rmsg)) {
		cpu = rmsg.cn_proc.cpu;
		if (cpu < 0) {
			continue;
		}
		seq = rmsg.cn_msg.seq;
		if ((last_seq[cpu] != 0) && (seq != last_seq[cpu] + 1)) {
			printf("out-of-order seq=%d on cpu=%d\n", seq, cpu);
		}
		last_seq[cpu] = seq;
	}

	/* NOTREACHED */

	perror("recv");

	return -1;
}
Signed-off-by: default avatarAaron Campbell <aaron@monkey.org>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 0888d5f3
...@@ -56,11 +56,21 @@ static struct cb_id cn_proc_event_id = { CN_IDX_PROC, CN_VAL_PROC }; ...@@ -56,11 +56,21 @@ static struct cb_id cn_proc_event_id = { CN_IDX_PROC, CN_VAL_PROC };
/* proc_event_counts is used as the sequence number of the netlink message */ /* proc_event_counts is used as the sequence number of the netlink message */
static DEFINE_PER_CPU(__u32, proc_event_counts) = { 0 }; static DEFINE_PER_CPU(__u32, proc_event_counts) = { 0 };
static inline void get_seq(__u32 *ts, int *cpu) static inline void send_msg(struct cn_msg *msg)
{ {
preempt_disable(); preempt_disable();
*ts = __this_cpu_inc_return(proc_event_counts) - 1;
*cpu = smp_processor_id(); msg->seq = __this_cpu_inc_return(proc_event_counts) - 1;
((struct proc_event *)msg->data)->cpu = smp_processor_id();
/*
* Preemption remains disabled during send to ensure the messages are
* ordered according to their sequence numbers.
*
* If cn_netlink_send() fails, the data is not sent.
*/
cn_netlink_send(msg, 0, CN_IDX_PROC, GFP_NOWAIT);
preempt_enable(); preempt_enable();
} }
...@@ -77,7 +87,6 @@ void proc_fork_connector(struct task_struct *task) ...@@ -77,7 +87,6 @@ void proc_fork_connector(struct task_struct *task)
msg = buffer_to_cn_msg(buffer); msg = buffer_to_cn_msg(buffer);
ev = (struct proc_event *)msg->data; ev = (struct proc_event *)msg->data;
memset(&ev->event_data, 0, sizeof(ev->event_data)); memset(&ev->event_data, 0, sizeof(ev->event_data));
get_seq(&msg->seq, &ev->cpu);
ev->timestamp_ns = ktime_get_ns(); ev->timestamp_ns = ktime_get_ns();
ev->what = PROC_EVENT_FORK; ev->what = PROC_EVENT_FORK;
rcu_read_lock(); rcu_read_lock();
...@@ -92,8 +101,7 @@ void proc_fork_connector(struct task_struct *task) ...@@ -92,8 +101,7 @@ void proc_fork_connector(struct task_struct *task)
msg->ack = 0; /* not used */ msg->ack = 0; /* not used */
msg->len = sizeof(*ev); msg->len = sizeof(*ev);
msg->flags = 0; /* not used */ msg->flags = 0; /* not used */
/* If cn_netlink_send() failed, the data is not sent */ send_msg(msg);
cn_netlink_send(msg, 0, CN_IDX_PROC, GFP_KERNEL);
} }
void proc_exec_connector(struct task_struct *task) void proc_exec_connector(struct task_struct *task)
...@@ -108,7 +116,6 @@ void proc_exec_connector(struct task_struct *task) ...@@ -108,7 +116,6 @@ void proc_exec_connector(struct task_struct *task)
msg = buffer_to_cn_msg(buffer); msg = buffer_to_cn_msg(buffer);
ev = (struct proc_event *)msg->data; ev = (struct proc_event *)msg->data;
memset(&ev->event_data, 0, sizeof(ev->event_data)); memset(&ev->event_data, 0, sizeof(ev->event_data));
get_seq(&msg->seq, &ev->cpu);
ev->timestamp_ns = ktime_get_ns(); ev->timestamp_ns = ktime_get_ns();
ev->what = PROC_EVENT_EXEC; ev->what = PROC_EVENT_EXEC;
ev->event_data.exec.process_pid = task->pid; ev->event_data.exec.process_pid = task->pid;
...@@ -118,7 +125,7 @@ void proc_exec_connector(struct task_struct *task) ...@@ -118,7 +125,7 @@ void proc_exec_connector(struct task_struct *task)
msg->ack = 0; /* not used */ msg->ack = 0; /* not used */
msg->len = sizeof(*ev); msg->len = sizeof(*ev);
msg->flags = 0; /* not used */ msg->flags = 0; /* not used */
cn_netlink_send(msg, 0, CN_IDX_PROC, GFP_KERNEL); send_msg(msg);
} }
void proc_id_connector(struct task_struct *task, int which_id) void proc_id_connector(struct task_struct *task, int which_id)
...@@ -150,14 +157,13 @@ void proc_id_connector(struct task_struct *task, int which_id) ...@@ -150,14 +157,13 @@ void proc_id_connector(struct task_struct *task, int which_id)
return; return;
} }
rcu_read_unlock(); rcu_read_unlock();
get_seq(&msg->seq, &ev->cpu);
ev->timestamp_ns = ktime_get_ns(); ev->timestamp_ns = ktime_get_ns();
memcpy(&msg->id, &cn_proc_event_id, sizeof(msg->id)); memcpy(&msg->id, &cn_proc_event_id, sizeof(msg->id));
msg->ack = 0; /* not used */ msg->ack = 0; /* not used */
msg->len = sizeof(*ev); msg->len = sizeof(*ev);
msg->flags = 0; /* not used */ msg->flags = 0; /* not used */
cn_netlink_send(msg, 0, CN_IDX_PROC, GFP_KERNEL); send_msg(msg);
} }
void proc_sid_connector(struct task_struct *task) void proc_sid_connector(struct task_struct *task)
...@@ -172,7 +178,6 @@ void proc_sid_connector(struct task_struct *task) ...@@ -172,7 +178,6 @@ void proc_sid_connector(struct task_struct *task)
msg = buffer_to_cn_msg(buffer); msg = buffer_to_cn_msg(buffer);
ev = (struct proc_event *)msg->data; ev = (struct proc_event *)msg->data;
memset(&ev->event_data, 0, sizeof(ev->event_data)); memset(&ev->event_data, 0, sizeof(ev->event_data));
get_seq(&msg->seq, &ev->cpu);
ev->timestamp_ns = ktime_get_ns(); ev->timestamp_ns = ktime_get_ns();
ev->what = PROC_EVENT_SID; ev->what = PROC_EVENT_SID;
ev->event_data.sid.process_pid = task->pid; ev->event_data.sid.process_pid = task->pid;
...@@ -182,7 +187,7 @@ void proc_sid_connector(struct task_struct *task) ...@@ -182,7 +187,7 @@ void proc_sid_connector(struct task_struct *task)
msg->ack = 0; /* not used */ msg->ack = 0; /* not used */
msg->len = sizeof(*ev); msg->len = sizeof(*ev);
msg->flags = 0; /* not used */ msg->flags = 0; /* not used */
cn_netlink_send(msg, 0, CN_IDX_PROC, GFP_KERNEL); send_msg(msg);
} }
void proc_ptrace_connector(struct task_struct *task, int ptrace_id) void proc_ptrace_connector(struct task_struct *task, int ptrace_id)
...@@ -197,7 +202,6 @@ void proc_ptrace_connector(struct task_struct *task, int ptrace_id) ...@@ -197,7 +202,6 @@ void proc_ptrace_connector(struct task_struct *task, int ptrace_id)
msg = buffer_to_cn_msg(buffer); msg = buffer_to_cn_msg(buffer);
ev = (struct proc_event *)msg->data; ev = (struct proc_event *)msg->data;
memset(&ev->event_data, 0, sizeof(ev->event_data)); memset(&ev->event_data, 0, sizeof(ev->event_data));
get_seq(&msg->seq, &ev->cpu);
ev->timestamp_ns = ktime_get_ns(); ev->timestamp_ns = ktime_get_ns();
ev->what = PROC_EVENT_PTRACE; ev->what = PROC_EVENT_PTRACE;
ev->event_data.ptrace.process_pid = task->pid; ev->event_data.ptrace.process_pid = task->pid;
...@@ -215,7 +219,7 @@ void proc_ptrace_connector(struct task_struct *task, int ptrace_id) ...@@ -215,7 +219,7 @@ void proc_ptrace_connector(struct task_struct *task, int ptrace_id)
msg->ack = 0; /* not used */ msg->ack = 0; /* not used */
msg->len = sizeof(*ev); msg->len = sizeof(*ev);
msg->flags = 0; /* not used */ msg->flags = 0; /* not used */
cn_netlink_send(msg, 0, CN_IDX_PROC, GFP_KERNEL); send_msg(msg);
} }
void proc_comm_connector(struct task_struct *task) void proc_comm_connector(struct task_struct *task)
...@@ -230,7 +234,6 @@ void proc_comm_connector(struct task_struct *task) ...@@ -230,7 +234,6 @@ void proc_comm_connector(struct task_struct *task)
msg = buffer_to_cn_msg(buffer); msg = buffer_to_cn_msg(buffer);
ev = (struct proc_event *)msg->data; ev = (struct proc_event *)msg->data;
memset(&ev->event_data, 0, sizeof(ev->event_data)); memset(&ev->event_data, 0, sizeof(ev->event_data));
get_seq(&msg->seq, &ev->cpu);
ev->timestamp_ns = ktime_get_ns(); ev->timestamp_ns = ktime_get_ns();
ev->what = PROC_EVENT_COMM; ev->what = PROC_EVENT_COMM;
ev->event_data.comm.process_pid = task->pid; ev->event_data.comm.process_pid = task->pid;
...@@ -241,7 +244,7 @@ void proc_comm_connector(struct task_struct *task) ...@@ -241,7 +244,7 @@ void proc_comm_connector(struct task_struct *task)
msg->ack = 0; /* not used */ msg->ack = 0; /* not used */
msg->len = sizeof(*ev); msg->len = sizeof(*ev);
msg->flags = 0; /* not used */ msg->flags = 0; /* not used */
cn_netlink_send(msg, 0, CN_IDX_PROC, GFP_KERNEL); send_msg(msg);
} }
void proc_coredump_connector(struct task_struct *task) void proc_coredump_connector(struct task_struct *task)
...@@ -256,7 +259,6 @@ void proc_coredump_connector(struct task_struct *task) ...@@ -256,7 +259,6 @@ void proc_coredump_connector(struct task_struct *task)
msg = buffer_to_cn_msg(buffer); msg = buffer_to_cn_msg(buffer);
ev = (struct proc_event *)msg->data; ev = (struct proc_event *)msg->data;
memset(&ev->event_data, 0, sizeof(ev->event_data)); memset(&ev->event_data, 0, sizeof(ev->event_data));
get_seq(&msg->seq, &ev->cpu);
ev->timestamp_ns = ktime_get_ns(); ev->timestamp_ns = ktime_get_ns();
ev->what = PROC_EVENT_COREDUMP; ev->what = PROC_EVENT_COREDUMP;
ev->event_data.coredump.process_pid = task->pid; ev->event_data.coredump.process_pid = task->pid;
...@@ -266,7 +268,7 @@ void proc_coredump_connector(struct task_struct *task) ...@@ -266,7 +268,7 @@ void proc_coredump_connector(struct task_struct *task)
msg->ack = 0; /* not used */ msg->ack = 0; /* not used */
msg->len = sizeof(*ev); msg->len = sizeof(*ev);
msg->flags = 0; /* not used */ msg->flags = 0; /* not used */
cn_netlink_send(msg, 0, CN_IDX_PROC, GFP_KERNEL); send_msg(msg);
} }
void proc_exit_connector(struct task_struct *task) void proc_exit_connector(struct task_struct *task)
...@@ -281,7 +283,6 @@ void proc_exit_connector(struct task_struct *task) ...@@ -281,7 +283,6 @@ void proc_exit_connector(struct task_struct *task)
msg = buffer_to_cn_msg(buffer); msg = buffer_to_cn_msg(buffer);
ev = (struct proc_event *)msg->data; ev = (struct proc_event *)msg->data;
memset(&ev->event_data, 0, sizeof(ev->event_data)); memset(&ev->event_data, 0, sizeof(ev->event_data));
get_seq(&msg->seq, &ev->cpu);
ev->timestamp_ns = ktime_get_ns(); ev->timestamp_ns = ktime_get_ns();
ev->what = PROC_EVENT_EXIT; ev->what = PROC_EVENT_EXIT;
ev->event_data.exit.process_pid = task->pid; ev->event_data.exit.process_pid = task->pid;
...@@ -293,7 +294,7 @@ void proc_exit_connector(struct task_struct *task) ...@@ -293,7 +294,7 @@ void proc_exit_connector(struct task_struct *task)
msg->ack = 0; /* not used */ msg->ack = 0; /* not used */
msg->len = sizeof(*ev); msg->len = sizeof(*ev);
msg->flags = 0; /* not used */ msg->flags = 0; /* not used */
cn_netlink_send(msg, 0, CN_IDX_PROC, GFP_KERNEL); send_msg(msg);
} }
/* /*
...@@ -325,7 +326,7 @@ static void cn_proc_ack(int err, int rcvd_seq, int rcvd_ack) ...@@ -325,7 +326,7 @@ static void cn_proc_ack(int err, int rcvd_seq, int rcvd_ack)
msg->ack = rcvd_ack + 1; msg->ack = rcvd_ack + 1;
msg->len = sizeof(*ev); msg->len = sizeof(*ev);
msg->flags = 0; /* not used */ msg->flags = 0; /* not used */
cn_netlink_send(msg, 0, CN_IDX_PROC, GFP_KERNEL); send_msg(msg);
} }
/** /**
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment