[PATCH RESEND 1/8] netlink: call get_fd_nl_family before decode nlmsghdr

JingPiao Chen chenjingpiao at gmail.com
Sat Aug 5 12:59:12 UTC 2017


On Tue, Aug 04, 2017 Dmitry V. Levin wrote:
> On Tue, Aug 01, 2017 at 07:48:41AM +0800, JingPiao Chen wrote:
> > Prepare for NETLINK_KOBJECT_UEVENT decode. The messages
> > of NETLINK_KOBJECT_UEVENT do not contain nlmsghdr.
> 
> A netlink message without a netlink message header?  Ouch.
> Could you give a link to the exact place in kernel sources
> or documentation where this marvel of design is described, please?

Documentation/kobject.txt: 166
Uevents
=======

After a kobject has been registered with the kobject core, you need to
announce to the world that it has been created.  This can be done with a
call to kobject_uevent()::

    int kobject_uevent(struct kobject *kobj, enum kobject_action action);

lib/kobject_uevent.c:
/**
 * kobject_uevent - notify userspace by sending an uevent
 *
 * @kobj: struct kobject that the action is happening to
 * @action: action that is happening
 *
 * Returns 0 if kobject_uevent() is completed with success or the
 * corresponding error when it fails.
 */
int kobject_uevent(struct kobject *kobj, enum kobject_action action)
{
	return kobject_uevent_env(kobj, action, NULL);
}
EXPORT_SYMBOL_GPL(kobject_uevent);

/**
 * kobject_uevent_env - send an uevent with environmental data
 *
 * @kobj: struct kobject that the action is happening to
 * @action: action that is happening
 * @envp_ext: pointer to environmental data
 *
 * Returns 0 if kobject_uevent_env() is completed with success or the
 * corresponding error when it fails.
 */
int kobject_uevent_env(struct kobject *kobj, enum kobject_action action,
		       char *envp_ext[])
{
	...
	/* default keys */
	retval = add_uevent_var(env, "ACTION=%s", action_string);
	if (retval)
		goto exit;
	retval = add_uevent_var(env, "DEVPATH=%s", devpath);
	if (retval)
		goto exit;
	retval = add_uevent_var(env, "SUBSYSTEM=%s", subsystem);
	if (retval)
		goto exit;

	/* keys passed in from the caller */
	if (envp_ext) {
		for (i = 0; envp_ext[i]; i++) {
			retval = add_uevent_var(env, "%s", envp_ext[i]);
			if (retval)
				goto exit;
		}
	}

	...
	/* send netlink message */
	list_for_each_entry(ue_sk, &uevent_sock_list, list) {
		struct sock *uevent_sock = ue_sk->sk;
		struct sk_buff *skb;
		size_t len;

		if (!netlink_has_listeners(uevent_sock, 1))
			continue;

		/* allocate message with the maximum possible size */
		len = strlen(action_string) + strlen(devpath) + 2;
		skb = alloc_skb(len + env->buflen, GFP_KERNEL);
		if (skb) {
			char *scratch;

			/* add header */
			scratch = skb_put(skb, len);
			sprintf(scratch, "%s@%s", action_string, devpath);

			/* copy keys to our continuous event payload buffer */
			for (i = 0; i < env->envp_idx; i++) {
				len = strlen(env->envp[i]) + 1;
				scratch = skb_put(skb, len);
				strcpy(scratch, env->envp[i]);
			}

			NETLINK_CB(skb).dst_group = 1;
			retval = netlink_broadcast_filtered(uevent_sock, skb,
							    0, 1, GFP_KERNEL,
							    kobj_bcast_filter,
							    kobj);
			/* ENOBUFS should be handled in userspace */
			if (retval == -ENOBUFS || retval == -ESRCH)
				retval = 0;
		} else
			retval = -ENOMEM;
	}
	...
}

/**
 * add_uevent_var - add key value string to the environment buffer
 * @env: environment buffer structure
 * @format: printf format for the key=value pair
 *
 * Returns 0 if environment variable was added successfully or -ENOMEM
 * if no space was available.
 */
int add_uevent_var(struct kobj_uevent_env *env, const char *format, ...)
{
	...
	len = vsnprintf(&env->buf[env->buflen],
			sizeof(env->buf) - env->buflen,
			format, args);
	...
}

A demo of NETLINK_KOBJECT_UEVENT:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <linux/netlink.h>

int
main(void)
{
	struct sockaddr_nl nl;
	size_t i, len;
	int fd;
	char buf[4096];

	if ((fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_KOBJECT_UEVENT)) == -1) {
		perror("socket");
		return EXIT_FAILURE;
	}

	memset(&nl, 0, sizeof(nl));
	nl.nl_family = AF_NETLINK;
	nl.nl_pid = getpid();
	nl.nl_groups = 1;

	if (bind(fd, (struct sockaddr *)&nl, sizeof(nl)) == -1) {
		perror("bind");
		return EXIT_FAILURE;
	}

	for (;;) {
		len = recv(fd, buf, sizeof(buf), 0);
		if (!len)
			break;
		write(STDOUT_FILENO, buf, len);
	}
	close(fd);

	return 0;
}

$ gcc uevent.c
$ ./a.out # Remove your mouse

--
JingPiao Chen




More information about the Strace-devel mailing list