Bug 219472 - Out of bounds access in vlan
Summary: Out of bounds access in vlan
Status: New
Alias: None
Product: Base System
Classification: Unclassified
Component: kern (show other bugs)
Version: CURRENT
Hardware: Any Any
: --- Affects Only Me
Assignee: freebsd-net
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2017-05-23 12:36 UTC by CTurt
Modified: 2017-05-30 20:14 UTC (History)
2 users (show)

See Also:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description CTurt 2017-05-23 12:36:18 UTC
This whole struct (`ifv_mib`) is user controllable through the `IFDATA_LINKSPECIFIC` sysctl command:

https://github.com/freebsd/freebsd/blob/release/11.0.1/sys/net/if_mib.c#L117

	case IFDATA_LINKSPECIFIC:
		error = SYSCTL_OUT(req, ifp->if_linkmib, ifp->if_linkmiblen);
		if (error || !req->newptr)
			goto out;

		error = SYSCTL_IN(req, ifp->if_linkmib, ifp->if_linkmiblen);
		if (error)
			goto out;
			break;

In the case of `struct ifvlan`, the contained `if_linkmib` is a `struct ifv_linkmib` containing a `uint16_t` called `ifvm_vid`:

https://github.com/freebsd/freebsd/blob/release/11.0.1/sys/net/if_vlan.c#L108

struct	ifvlan {
	struct	ifvlantrunk *ifv_trunk;
	struct	ifnet *ifv_ifp;
#define	TRUNK(ifv)	((ifv)->ifv_trunk)
#define	PARENT(ifv)	((ifv)->ifv_trunk->parent)
	void	*ifv_cookie;
	int	ifv_pflags;	/* special flags we have set on parent */
	struct	ifv_linkmib {
		int	ifvm_encaplen;	/* encapsulation length */
		int	ifvm_mtufudge;	/* MTU fudged by this much */
		int	ifvm_mintu;	/* min transmission unit */
		uint16_t ifvm_proto;	/* encapsulation ethertype */
		uint16_t ifvm_tag;	/* tag to apply on packets leaving if */
              	uint16_t ifvm_vid;	/* VLAN ID */
		uint8_t	ifvm_pcp;	/* Priority Code Point (PCP). */
	}	ifv_mib;
	SLIST_HEAD(, vlan_mc_entry) vlan_mc_listhead;
#ifndef VLAN_ARRAY
	LIST_ENTRY(ifvlan) ifv_list;
#endif
};
#define	ifv_proto	ifv_mib.ifvm_proto
#define	ifv_tag		ifv_mib.ifvm_tag
#define	ifv_vid 	ifv_mib.ifvm_vid
#define	ifv_pcp		ifv_mib.ifvm_pcp
#define	ifv_encaplen	ifv_mib.ifvm_encaplen
#define	ifv_mtufudge	ifv_mib.ifvm_mtufudge
#define	ifv_mintu	ifv_mib.ifvm_mintu

Thus, it follows that `ifv->ifv_vid` is a completely user controlled `uint16_t`, through the `IFDATA_LINKSPECIFIC` `sysctl` name.

This value is used as an index to perform reads and writes on the `vlans` array of size `0x1000` in multiple places.

https://github.com/freebsd/freebsd/blob/release/11.0.1/sys/net/if_vlan.c#L427

static __inline int
vlan_inshash(struct ifvlantrunk *trunk, struct ifvlan *ifv)
{

	if (trunk->vlans[ifv->ifv_vid] != NULL)
		return EEXIST;
	trunk->vlans[ifv->ifv_vid] = ifv;
	trunk->refcnt++;

	return (0);
}

static __inline int
vlan_remhash(struct ifvlantrunk *trunk, struct ifvlan *ifv)
{

	trunk->vlans[ifv->ifv_vid] = NULL;
	trunk->refcnt--;

	return (0);
}

...

However, this is a static array of size `VLAN_ARRAY_SIZE` (`0x1000`) elements:

https://github.com/freebsd/freebsd/blob/release/11.0.1/sys/net/ethernet.h#L86

#define EVL_VLID_MASK 0x0FFF

https://github.com/freebsd/freebsd/blob/release/11.0.1/sys/net/if_vlan.c#L89

struct ifvlantrunk {
	struct	ifnet   *parent;	/* parent interface of this trunk */
	struct	rmlock	lock;
#ifdef VLAN_ARRAY
#define	VLAN_ARRAY_SIZE	(EVL_VLID_MASK + 1)
	struct	ifvlan	*vlans[VLAN_ARRAY_SIZE]; /* static table */
#else
	struct	ifvlanhead *hash;	/* dynamic hash-list table */
	uint16_t	hmask;
	uint16_t	hwidth;
#endif
	int		refcnt;
};

So, out of bounds access is possible if `ifv_vid` is set to a value greater than `0xfff`.