| /* "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; |
| } |