Vulnerability: Multiple buffer overflows in drivers/staging/wlags49_h2/wl_priv.c in the Linux kernel before 3.12 allow local users to cause a denial of service or possibly have unspecified other impact by leveraging the CAP_NET_ADMIN capability and providing a long station-name string, related to the (1) wvlan_uil_put_info and (2) wvlan_set_station_nickname functions.
Vulnerability ID: CVE-2013-4514
CWE: CWE-119
Severity score: 6.9
Project: https://github.com/torvalds/linux
Published: 2013-11-12
File: drivers/staging/wlags49_h2/wl_priv.c
Commit message: staging: wlags49_h2: buffer overflow setting station name

We need to check the length parameter before doing the memcpy().  I've
actually changed it to strlcpy() as well so that it's NUL terminated.

You need CAP_NET_ADMIN to trigger these so it's not the end of the
world.

Reported-by: Nico Golde <nico@ngolde.de>
Reported-by: Fabian Yamaguchi <fabs@goesec.de>
Signed-off-by: Dan Carpenter <dan.carpenter@oracle.com>
Cc: stable@kernel.org
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Commit author: Dan Carpenter

Vulnerable version (before fix):
 int wvlan_uil_put_info(struct uilreq *urq, struct wl_private *lp) 	ltv_t                   *pLtv; 	bool_t                  ltvAllocated = FALSE; 	ENCSTRCT                sEncryption;  #ifdef USE_WDS 	hcf_16                  hcfPort  = HCF_PORT_0; int wvlan_uil_put_info(struct uilreq *urq, struct wl_private *lp) 					break; 				case CFG_CNF_OWN_NAME: 					memset(lp->StationName, 0, sizeof(lp->StationName)); 					memcpy((void *)lp->StationName, (void *)&pLtv->u.u8[2], (size_t)pLtv->u.u16[0]); 					pLtv->u.u16[0] = CNV_INT_TO_LITTLE(pLtv->u.u16[0]); 					break; 				case CFG_CNF_LOAD_BALANCING: int wvlan_set_station_nickname(struct net_device *dev, { 	struct wl_private *lp = wl_priv(dev); 	unsigned long flags; 	int         ret = 0; 	/*------------------------------------------------------------------------*/  int wvlan_set_station_nickname(struct net_device *dev, 	wl_lock(lp, &flags);  	memset(lp->StationName, 0, sizeof(lp->StationName));  	memcpy(lp->StationName, extra, wrqu->data.length);  	/* Commit the adapter parameters */ 	wl_apply(lp);

Fixed version (after fix):
 int wvlan_uil_put_info(struct uilreq *urq, struct wl_private *lp) 	ltv_t                   *pLtv; 	bool_t                  ltvAllocated = FALSE; 	ENCSTRCT                sEncryption; 	size_t			len;  #ifdef USE_WDS 	hcf_16                  hcfPort  = HCF_PORT_0; int wvlan_uil_put_info(struct uilreq *urq, struct wl_private *lp) 					break; 				case CFG_CNF_OWN_NAME: 					memset(lp->StationName, 0, sizeof(lp->StationName)); 					len = min_t(size_t, pLtv->u.u16[0], sizeof(lp->StationName)); 					strlcpy(lp->StationName, &pLtv->u.u8[2], len); 					pLtv->u.u16[0] = CNV_INT_TO_LITTLE(pLtv->u.u16[0]); 					break; 				case CFG_CNF_LOAD_BALANCING: int wvlan_set_station_nickname(struct net_device *dev, { 	struct wl_private *lp = wl_priv(dev); 	unsigned long flags; 	size_t len; 	int         ret = 0; 	/*------------------------------------------------------------------------*/  int wvlan_set_station_nickname(struct net_device *dev, 	wl_lock(lp, &flags);  	memset(lp->StationName, 0, sizeof(lp->StationName)); 	len = min_t(size_t, wrqu->data.length, sizeof(lp->StationName)); 	strlcpy(lp->StationName, extra, len);  	/* Commit the adapter parameters */ 	wl_apply(lp);

Patch:
patch: @@ -570,6 +570,7 @@ int wvlan_uil_put_info(struct uilreq *urq, struct wl_private *lp)
 	ltv_t                   *pLtv;
 	bool_t                  ltvAllocated = FALSE;
 	ENCSTRCT                sEncryption;
+	size_t			len;
 
 #ifdef USE_WDS
 	hcf_16                  hcfPort  = HCF_PORT_0;
@@ -686,7 +687,8 @@ int wvlan_uil_put_info(struct uilreq *urq, struct wl_private *lp)
 					break;
 				case CFG_CNF_OWN_NAME:
 					memset(lp->StationName, 0, sizeof(lp->StationName));
-					memcpy((void *)lp->StationName, (void *)&pLtv->u.u8[2], (size_t)pLtv->u.u16[0]);
+					len = min_t(size_t, pLtv->u.u16[0], sizeof(lp->StationName));
+					strlcpy(lp->StationName, &pLtv->u.u8[2], len);
 					pLtv->u.u16[0] = CNV_INT_TO_LITTLE(pLtv->u.u16[0]);
 					break;
 				case CFG_CNF_LOAD_BALANCING:
@@ -1783,6 +1785,7 @@ int wvlan_set_station_nickname(struct net_device *dev,
 {
 	struct wl_private *lp = wl_priv(dev);
 	unsigned long flags;
+	size_t len;
 	int         ret = 0;
 	/*------------------------------------------------------------------------*/
 
@@ -1793,8 +1796,8 @@ int wvlan_set_station_nickname(struct net_device *dev,
 	wl_lock(lp, &flags);
 
 	memset(lp->StationName, 0, sizeof(lp->StationName));
-
-	memcpy(lp->StationName, extra, wrqu->data.length);
+	len = min_t(size_t, wrqu->data.length, sizeof(lp->StationName));
+	strlcpy(lp->StationName, extra, len);
 
 	/* Commit the adapter parameters */
 	wl_apply(lp);
path: drivers/staging/wlags49_h2/wl_priv.c