CVE-2013-4514: Improper Restriction of Operations within the Bounds of a Memory Buffer (CWE-119)
value: 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.
CWE description: The product performs operations on a memory buffer, but it reads from or writes to a memory location outside the buffer's intended boundary. This may result in read or write operations on unexpected memory locations that could be linked to other variables, data structures, or internal program data.
Published: 2013-11-12T14:35Z
Repository: https://github.com/torvalds/linux
Language: C
Files changed: drivers/staging/wlags49_h2/wl_priv.c
Commit date: 2013-10-30 12:24:49 -0700
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>

Diff: diff --git a/drivers/staging/wlags49_h2/wl_priv.c b/drivers/staging/wlags49_h2/wl_priv.c
index c97e0e154d285d..7e10dcdc309008 100644
--- a/drivers/staging/wlags49_h2/wl_priv.c
+++ b/drivers/staging/wlags49_h2/wl_priv.c
@@ -568,10 +568,11 @@ int wvlan_uil_put_info(struct uilreq *urq, struct wl_private *lp)
 {
 	int                     result = 0;
 	ltv_t                   *pLtv;
 	bool_t                  ltvAllocated = FALSE;
 	ENCSTRCT                sEncryption;
+	size_t			len;
 
 #ifdef USE_WDS
 	hcf_16                  hcfPort  = HCF_PORT_0;
 #endif  /* USE_WDS */
 	/*------------------------------------------------------------------------*/
@@ -684,11 +685,12 @@ int wvlan_uil_put_info(struct uilreq *urq, struct wl_private *lp)
 					lp->holdoverDuration    = pLtv->u.u16[0];
 					pLtv->u.u16[0]          = CNV_INT_TO_LITTLE(pLtv->u.u16[0]);
 					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:
 					lp->loadBalancing       = pLtv->u.u16[0];
 					pLtv->u.u16[0]          = CNV_INT_TO_LITTLE(pLtv->u.u16[0]);
@@ -1781,22 +1783,23 @@ int wvlan_set_station_nickname(struct net_device *dev,
 		      union iwreq_data *wrqu,
 		      char *extra)
 {
 	struct wl_private *lp = wl_priv(dev);
 	unsigned long flags;
+	size_t len;
 	int         ret = 0;
 	/*------------------------------------------------------------------------*/
 
 
 	DBG_FUNC("wvlan_set_station_nickname");
 	DBG_ENTER(DbgInfo);
 
 	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);
 	wl_unlock(lp, &flags);
 

Vulnerable code: 					memcpy((void *)lp->StationName, (void *)&pLtv->u.u8[2], (size_t)pLtv->u.u16[0]);

	memcpy(lp->StationName, extra, wrqu->data.length);
Fixed code: 	size_t			len;
					len = min_t(size_t, pLtv->u.u16[0], sizeof(lp->StationName));
					strlcpy(lp->StationName, &pLtv->u.u8[2], len);
	size_t len;
	len = min_t(size_t, wrqu->data.length, sizeof(lp->StationName));
	strlcpy(lp->StationName, extra, len);