Commit message: atl2: Disable unimplemented scatter/gather feature

atl2 includes NETIF_F_SG in hw_features even though it has no support
for non-linear skbs.  This bug was originally harmless since the
driver does not claim to implement checksum offload and that used to
be a requirement for SG.

Now that SG and checksum offload are independent features, if you
explicitly enable SG *and* use one of the rare protocols that can use
SG without checkusm offload, this potentially leaks sensitive
information (before you notice that it just isn't working).  Therefore
this obscure bug has been designated CVE-2016-2117.

Reported-by: Justin Yackoski <jyackoski@crypto-nite.com>
Signed-off-by: Ben Hutchings <ben@decadent.org.uk>
Fixes: ec5f06156423 ("net: Kill link between CSUM and SG features.")
Signed-off-by: David S. Miller <davem@davemloft.net>
CVE: CVE-2016-2117
CWE: CWE-200
Vulnerability description: The atl2_probe function in drivers/net/ethernet/atheros/atlx/atl2.c in the Linux kernel through 4.5.2 incorrectly enables scatter/gather I/O, which allows remote attackers to obtain sensitive information from kernel memory by reading packet data.

Project: linux
File: atl2.c

static void atl2_reinit_locked(struct atl2_adapter *adapter)
{
	WARN_ON(in_interrupt());
	while (test_and_set_bit(__ATL2_RESETTING, &adapter->flags))
		msleep(1);
	atl2_down(adapter);
	atl2_up(adapter);
	clear_bit(__ATL2_RESETTING, &adapter->flags);
}

Project: linux
File: atl2.c

static void atl2_restore_vlan(struct atl2_adapter *adapter)
{
	atl2_vlan_mode(adapter->netdev, adapter->netdev->features);
}

Project: linux
File: atl2.c

static int atl2_set_wol(struct net_device *netdev, struct ethtool_wolinfo *wol)
{
	struct atl2_adapter *adapter = netdev_priv(netdev);

	if (wol->wolopts & (WAKE_ARP | WAKE_MAGICSECURE))
		return -EOPNOTSUPP;

	if (wol->wolopts & (WAKE_UCAST | WAKE_BCAST | WAKE_MCAST))
		return -EOPNOTSUPP;

	/* these settings will always override what we currently have */
	adapter->wol = 0;

	if (wol->wolopts & WAKE_MAGIC)
		adapter->wol |= ATLX_WUFC_MAG;
	if (wol->wolopts & WAKE_PHY)
		adapter->wol |= ATLX_WUFC_LNKC;

	return 0;
}

Project: linux
File: atl2.c

static void atl2_shutdown(struct pci_dev *pdev)
{
	atl2_suspend(pdev, PMSG_SUSPEND);
}

Project: linux
File: atl2.c

static void atl2_vlan_mode(struct net_device *netdev,
	netdev_features_t features)
{
	struct atl2_adapter *adapter = netdev_priv(netdev);
	u32 ctrl;

	atl2_irq_disable(adapter);

	ctrl = ATL2_READ_REG(&adapter->hw, REG_MAC_CTRL);
	__atl2_vlan_mode(features, &ctrl);
	ATL2_WRITE_REG(&adapter->hw, REG_MAC_CTRL, ctrl);

	atl2_irq_enable(adapter);
}

Project: linux
File: atl2.c

static int get_permanent_address(struct atl2_hw *hw)
{
	u32 Addr[2];
	u32 i, Control;
	u16 Register;
	u8  EthAddr[ETH_ALEN];
	bool KeyValid;

	if (is_valid_ether_addr(hw->perm_mac_addr))
		return 0;

	Addr[0] = 0;
	Addr[1] = 0;

	if (!atl2_check_eeprom_exist(hw)) { /* eeprom exists */
		Register = 0;
		KeyValid = false;

		/* Read out all EEPROM content */
		i = 0;
		while (1) {
			if (atl2_read_eeprom(hw, i + 0x100, &Control)) {
				if (KeyValid) {
					if (Register == REG_MAC_STA_ADDR)
						Addr[0] = Control;
					else if (Register ==
						(REG_MAC_STA_ADDR + 4))
						Addr[1] = Control;
					KeyValid = false;
				} else if ((Control & 0xff) == 0x5A) {
					KeyValid = true;
					Register = (u16) (Control >> 16);
				} else {
			/* assume data end while encount an invalid KEYWORD */
					break;
				}
			} else {
				break; /* read error */
			}
			i += 4;
		}

		*(u32 *) &EthAddr[2] = LONGSWAP(Addr[0]);
		*(u16 *) &EthAddr[0] = SHORTSWAP(*(u16 *) &Addr[1]);

		if (is_valid_ether_addr(EthAddr)) {
			memcpy(hw->perm_mac_addr, EthAddr, ETH_ALEN);
			return 0;
		}
		return 1;
	}

	/* see if SPI flash exists? */
	Addr[0] = 0;
	Addr[1] = 0;
	Register = 0;
	KeyValid = false;
	i = 0;
	while (1) {
		if (atl2_spi_read(hw, i + 0x1f000, &Control)) {
			if (KeyValid) {
				if (Register == REG_MAC_STA_ADDR)
					Addr[0] = Control;
				else if (Register == (REG_MAC_STA_ADDR + 4))
					Addr[1] = Control;
				KeyValid = false;
			} else if ((Control & 0xff) == 0x5A) {
				KeyValid = true;
				Register = (u16) (Control >> 16);
			} else {
				break; /* data end */
			}
		} else {
			break; /* read error */
		}
		i += 4;
	}

	*(u32 *) &EthAddr[2] = LONGSWAP(Addr[0]);
	*(u16 *) &EthAddr[0] = SHORTSWAP(*(u16 *)&Addr[1]);
	if (is_valid_ether_addr(EthAddr)) {
		memcpy(hw->perm_mac_addr, EthAddr, ETH_ALEN);
		return 0;
	}
	/* maybe MAC-address is from BIOS */
	Addr[0] = ATL2_READ_REG(hw, REG_MAC_STA_ADDR);
	Addr[1] = ATL2_READ_REG(hw, REG_MAC_STA_ADDR + 4);
	*(u32 *) &EthAddr[2] = LONGSWAP(Addr[0]);
	*(u16 *) &EthAddr[0] = SHORTSWAP(*(u16 *) &Addr[1]);

	if (is_valid_ether_addr(EthAddr)) {
		memcpy(hw->perm_mac_addr, EthAddr, ETH_ALEN);
		return 0;
	}

	return 1;
}