/* "The yam_ioctl function in drivers/net/hamradio/yam.c in the Linux kernel
   before 3.12.8 does not initialize a certain structure member, which allows
   local users to obtain sensitive information from kernel memory by
   leveraging the CAP_NET_ADMIN capability for an SIOCYAMGCFG ioctl call."

   Fixed e.g. by e7834c71c2cacc621ddc64bd71f83ef2054f6539 on linux-3.12.y
   in linux-stable.  */

#include <string.h>

#include "test-uaccess.h"

/* Adapted from include/linux/yam.h  */

struct yamcfg {
	unsigned int mask;		/* Mask of commands */
	unsigned int iobase;	/* IO Base of COM port */
	unsigned int irq;		/* IRQ of COM port */
	unsigned int bitrate;	/* Bit rate of radio port */
	unsigned int baudrate;	/* Baud rate of the RS232 port */
	unsigned int txdelay;	/* TxDelay */
	unsigned int txtail;	/* TxTail */
	unsigned int persist;	/* Persistence */
	unsigned int slottime;	/* Slottime */
	unsigned int mode;		/* mode 0 (simp), 1(Dupl), 2(Dupl+delay) */
	unsigned int holddly;	/* PTT delay in FullDuplex 2 mode */
};

struct yamdrv_ioctl_cfg {
	int cmd; /* { dg-message "field 'cmd' is uninitialized \\(4 bytes\\)" } */
	struct yamcfg cfg;
};

/* Adapted from include/asm-generic/errno-base.h  */

#define	EFAULT		14	/* Bad address */

/* Adapted from drivers/net/hamradio/yam.c  */

struct yam_port {
	/* [...snip...] */

	int bitrate;
	int baudrate;
	int iobase;
	int irq;
	int dupmode;

	/* [...snip...] */

	int txd;				/* tx delay */
	int holdd;				/* duplex ptt delay */
	int txtail;				/* txtail delay */
	int slot;				/* slottime */
	int pers;				/* persistence */

	/* [...snip...] */
};

/* Broken version, leaving yi.cmd uninitialized.  */

static int yam_ioctl(/* [...snip...] */
		     void __user *dst, struct yam_port *yp)
{
	struct yamdrv_ioctl_cfg yi; /* { dg-message "region created on stack here" "memspace event" } */
	/* { dg-message "capacity: 48 bytes" "capacity event" { target *-*-* } .-1 } */

	/* [...snip...] */

	/* case SIOCYAMGCFG: */
		yi.cfg.mask = 0xffffffff;
		yi.cfg.iobase = yp->iobase;
		yi.cfg.irq = yp->irq;
		yi.cfg.bitrate = yp->bitrate;
		yi.cfg.baudrate = yp->baudrate;
		yi.cfg.mode = yp->dupmode;
		yi.cfg.txdelay = yp->txd;
		yi.cfg.holddly = yp->holdd;
		yi.cfg.txtail = yp->txtail;
		yi.cfg.persist = yp->pers;
		yi.cfg.slottime = yp->slot;
		if (copy_to_user(dst, &yi, sizeof(struct yamdrv_ioctl_cfg))) /* { dg-warning "potential exposure of sensitive information by copying uninitialized data from stack" "warning" } */
			/* { dg-message "4 bytes are uninitialized" "how much note" { target *-*-* } .-1 } */
			 return -EFAULT;
	/* [...snip...] */

	return 0;
}

/* Fixed version, with a memset.  */

static int yam_ioctl_fixed(/* [...snip...] */
			   void __user *dst, struct yam_port *yp)
{
	struct yamdrv_ioctl_cfg yi;

	/* [...snip...] */

	/* case SIOCYAMGCFG: */
		memset(&yi, 0, sizeof(yi));
		yi.cfg.mask = 0xffffffff;
		yi.cfg.iobase = yp->iobase;
		yi.cfg.irq = yp->irq;
		yi.cfg.bitrate = yp->bitrate;
		yi.cfg.baudrate = yp->baudrate;
		yi.cfg.mode = yp->dupmode;
		yi.cfg.txdelay = yp->txd;
		yi.cfg.holddly = yp->holdd;
		yi.cfg.txtail = yp->txtail;
		yi.cfg.persist = yp->pers;
		yi.cfg.slottime = yp->slot;
		if (copy_to_user(dst, &yi, sizeof(struct yamdrv_ioctl_cfg))) /* { dg-bogus "" } */
			 return -EFAULT;
	/* [...snip...] */

	return 0;
}
