[patch] farsync: fix info leak in ioctl

Summary

CVE CVE-2014-1444
Author Salva Peiró
Date October 2013 - Discovery of the vulnerability.
Impact The vulnerability discloses 2 bytes of kernel process stack.
Affected Versions From Linux-2.6.12-rc2 to linux-3.15-rc3
Bug Timespan 8.5 years: 2005-04-16 to 2013-10-14 commit 1da177e4

InfoLeak Description

The fst_get_iface() code fails to initialize the two padding bytes of struct sync_serial_settings after the ->loopback field. Add an explicit memset(0) before filling the structure to avoid the info leak.

Analysis of the Leak Code

The code of fs_get_iface() is annotated above with the interesting steps:

  • [1] The analysis of the struct layout shows a two byte hole.
  • [2] The sync struct is allocated on the stack.
  • [3] The contents of the sync struct are filled.
  • [4] The contents of the sync struct are copied to user.
/* From from #include <linux/hdlc/ioctl.h> */
struct sync_serial_settings {
        unsigned int clock_rate;
        unsigned int clock_type;
        unsigned short loopback;    // [1] 2-byte hole from [10-12]
};

/* From linux/drivers/net/wan/farsync.c */
1986   static int
1987   fst_get_iface(struct fst_card_info *card, struct fst_port_info *port,
1988             struct ifreq *ifr)
1989   {
1990        sync_serial_settings sync;                                                  // [2]
2001        int i;
            /* ... */
2027        i = port->index;
2028        sync.clock_rate = FST_RDL(card, portConfig[i].lineSpeed);                   // [3]
2029        /* Lucky card and linux use same encoding here */
2030        sync.clock_type = FST_RDB(card, portConfig[i].internalClock) ==
2031            INTCLK ? CLOCK_INT : CLOCK_EXT;
2032        sync.loopback = 0;
2033
2034        if (copy_to_user(ifr->ifr_settings.ifs_ifsu.sync, &sync, sizeof (sync))) {  // [4]
2035            return -EFAULT;
2036        }
            /* ... */
2040    }

Analysis of the Leak

To check if fst_get_iface() initialise the two byte hole at the end of sync_serial_settings sync, We verify that the assembly emitted by the compiler performs the initialisation of the sync.loopback = 0. The listing shows that sync.loopback is filled at [5] using a movw of 2-byte word that leaves the 2-bytes uninitalised and leaking data.

8132eeb0 <fst_get_iface>:
8132eeb0:   55                      push   %ebp
8132eeb1:   89 e5                   mov    %esp,%ebp
8132eeb3:   56                      push   %esi
8132eeb4:   53                      push   %ebx
8132eeb5:   8d 19                   lea    (%ecx),%ebx
8132eeb7:   8d 64 24 f4             lea    -0xc(%esp),%esp
    ...
8132ef0b:   8d 55 ec                lea    -0x14(%ebp),%edx
8132ef0e:   0f b6 c0                movzbl %al,%eax
8132ef11:   66 c7 45 f4 00 00       movw   $0x0,-0xc(%ebp)      /* [5] sync.loopback=0 */
8132ef17:   83 c0 01                add    $0x1,%eax
8132ef1a:   89 45 f0                mov    %eax,-0x10(%ebp)
8132ef1d:   8b 43 18                mov    0x18(%ebx),%eax
8132ef20:   e8 5b 7d f3 ff          call   81266c80 <copy_to_user>

Fixing the Infoleak

Based on the analysis performed a small patch has been prepared that adds an explicit memset(0) before filling the structure to avoid the info leak. Closer analysis revealed this infoleak occurring at other places that used struct sync_serial_settings, as a result, patches fixing the infoleak are pushed upstream to the Linux Kernel: