Re: if_clone.c allows creating multiple interfaces with the same name

2011-11-30 Thread John Baldwin
On Tuesday, November 29, 2011 9:28:42 am Gleb Smirnoff wrote:
>   Daan,
> 
> On Tue, Nov 29, 2011 at 01:07:13AM +0100, Daan Vreeken wrote:
> D> Thanks for the looking into this and for your quick commit. I like your 
> twist
> D> on the patch with the move from the unit bitmap to allocating unit numbers
> D> with alloc_unr(9).
> D> 
> D> I do have two comments on the new code though.
> D> Before, in the !wildcard case, ifc_alloc_unit() would return ENOSPC when 
> the
> D> user requested a unit number larger than ifc->ifc_maxunit. Now the function
> D> returns EEXIST in that case because alloc_unr_specific() returns -1 both
> D> when a number is already allocated and when the number exceeds it's limits.
> D> This leads to the somewhat confusing:
> D> 
> D> # ifconfig bridge123456 create
> D> ifconfig: SIOCIFCREATE2: File exists
> D> 
> D> Apart from that I'm a bit worried about the "/* XXXGL: yep, it's a unit 
> leak
> D> */" part of your change. Once a unit number is leaked, there currently 
> seems
> D> to be no way to ever get it back. In a worst case scenario, where the 
> names of
> D> multiple interfaces in a row collides with the numbers alloc_unr() 
> returns, an
> D> entire row of unit numbers could be leaked during the creation of a single
> D> cloned interface.
> D> We have a product where cloned interfaces come and go very frequently. I'd
> D> hate to run out of unit numbers after 'just a few' years of uptime ;-)
> D> 
> D> I've created a new patch on top of your change that fixes both of these
> D> problems. Could you have a look at the attached diff?
> 
> Thanks, I will work on applying it.
> 
> D> > Considering the second part, that adds locking. Unfortunately, right now 
> we
> D> > have numerous races in the network configuration ocde. Many 
> SIOCSsomething
> D> > ioctls can race with each other producing unpredictable results and 
> kernel
> D> > panics. So, running two ifconfig(8) in parallel is a bad idea today. :(
> D> > Your patch with IFNET_NAMING_LOCK() just plumbs one race case: a race
> D> > between two SIOCSIFNAME ioctls. And it doesn't plumb a race between
> D> > SIOCSIFNAME vs SIOCIFCREATE, because IFNET_NAMING_LOCK() is dropped after
> D> > unit allocation, but prior to interface attachement to global interface
> D> > list.
> D> 
> D> Are you sure? With the patch in the PR, the relevant code in 
> D> ifc_simple_create() would become :
> D> 
> D>...
> D>IFNET_NAMING_LOCK();
> D>err = ifc_alloc_unit(ifc, &unit);
> D>if (err != 0) {
> D>IFNET_NAMING_UNLOCK();
> D>return (err);
> D>}
> D> 
> D>err = ifcs->ifcs_create(ifc, unit, params);
> D>IFNET_NAMING_UNLOCK();
> D>if (err != 0) {
> D>ifc_free_unit(ifc, unit);
> D>return (err);
> D>}
> D>...
> D> 
> D> The lock is acquired before the call to ifc_alloc_unit().
> D> In the non-error case (e.g. when creating an if_bridge interface) the call
> D> ends up calling bridge_clone_create(), which calls ether_ifattach(), which
> D> calls if_attach() -> if_attach_internal() where the ifp is added to the 
> ifnet
> D> list.
> D> Only when that's done, the lock is dropped.
> D> 
> D> Am I overlooking something obvious here?
> 
> The code above isn't correct since it holds mutex when calling 
> ifcs->ifcs_create().
> These methods may (they actually do) call malloc() with M_WAITOK.
> 
> In general FreeBSD uses M_WAITOK on the configuration code paths, like
> any SIOCSsomething ioctl. So to do correct protection, you first need to
> allocate every kind of memory needed, then lock mutex, then run through 
> configuration
> sequence, then release mutex and in case of error free all allocated memory.
> Sounds easy, but isn't in practice, especially when several network modules
> are involved.
> 
> So I'm still thinking about...
> 
> D> > From my point of view, we need a generic approach to ioctl() vs ioctl()
> D> > races, may be some global serializer of all re-configuration requests of
> D> > interfaces and addresses.
> 
> ... but several developers have noted that this approach may have some hidden
> problems.  When experimenting with new CARP, I have tried it on the 
> carp_ioctl()
> without any problems. The idea is simple:
> 
> static int serializer = 0;
> static struct mtx serializer_mtx;
> MTX_SYSINIT("sermtx", &serializer_mtx, "ioctl serializer mutex", MTX_DEF);
> 
> int
> foo_ioctl()
> {
>   mtx_lock(&serializer_mtx);
>   if (serializer != 0)
>   msleep(&serializer, &serializer_mtx, 0, "ioctl", 0);
>   serializer = 1;
>   mtx_unlock(&serializer_mtx);
> 
>   ... any code here, uncluding calls to different lower layers...
> 
>   mtx_lock(&serializer_mtx);
>   serializer = 0;
>   wakeup(&serializer);
>   mtx_unlock(&serializer_mtx);
> 
>   return (error);
> }
> 
> I have tried this for carp_ioctl() and it worked fine. You can try it for
> entire ifioctl() and all its descendants, but be a

Re: if_clone.c allows creating multiple interfaces with the same name

2011-11-29 Thread Daan Vreeken
Hi Glebius,

On Tuesday 29 November 2011 15:28:42 Gleb Smirnoff wrote:
>   Daan,
>
> On Tue, Nov 29, 2011 at 01:07:13AM +0100, Daan Vreeken wrote:
> D> Thanks for the looking into this and for your quick commit. I like your
> D> twist on the patch with the move from the unit bitmap to allocating unit
> D> numbers with alloc_unr(9).
> D>
> D> I do have two comments on the new code though.
> D> Before, in the !wildcard case, ifc_alloc_unit() would return ENOSPC when
> D> the user requested a unit number larger than ifc->ifc_maxunit. Now the
> D> function returns EEXIST in that case because alloc_unr_specific()
> D> returns -1 both when a number is already allocated and when the number
> D> exceeds it's limits. This leads to the somewhat confusing:
> D>
> D> # ifconfig bridge123456 create
> D> ifconfig: SIOCIFCREATE2: File exists
> D>
> D> Apart from that I'm a bit worried about the "/* XXXGL: yep, it's a unit
> D> leak */" part of your change. Once a unit number is leaked, there
> D> currently seems to be no way to ever get it back. In a worst case
> D> scenario, where the names of multiple interfaces in a row collides with
> D> the numbers alloc_unr() returns, an entire row of unit numbers could be
> D> leaked during the creation of a single cloned interface.
> D> We have a product where cloned interfaces come and go very frequently.
> D> I'd hate to run out of unit numbers after 'just a few' years of uptime
> D> ;-)
> D> I've created a new patch on top of your change that fixes both of these
> D> problems. Could you have a look at the attached diff?
>
> Thanks, I will work on applying it.

Great!

> D> > Considering the second part, that adds locking. Unfortunately, right
> D> > now we have numerous races in the network configuration ocde. Many
> D> > SIOCSsomething ioctls can race with each other producing unpredictable
> D> > results and kernel panics. So, running two ifconfig(8) in parallel is
> D> > a bad idea today. :(
> D> > Your patch with IFNET_NAMING_LOCK() just plumbs one race case: a race
> D> > between two SIOCSIFNAME ioctls. And it doesn't plumb a race between
> D> > SIOCSIFNAME vs SIOCIFCREATE, because IFNET_NAMING_LOCK() is dropped
> D> > after unit allocation, but prior to interface attachement to global
> D> > interface list. 
> D>
> D> Are you sure? With the patch in the PR, the relevant code in
> D> ifc_simple_create() would become :
> D>
> D>...
> D>IFNET_NAMING_LOCK();
> D>err = ifc_alloc_unit(ifc, &unit);
> D>if (err != 0) {
> D>IFNET_NAMING_UNLOCK();
> D>return (err);
> D>}
> D>
> D>err = ifcs->ifcs_create(ifc, unit, params);
> D>IFNET_NAMING_UNLOCK();
> D>if (err != 0) {
> D>ifc_free_unit(ifc, unit);
> D>return (err);
> D>}
> D>...
> D>
> D> The lock is acquired before the call to ifc_alloc_unit().
> D> In the non-error case (e.g. when creating an if_bridge interface) the
> D> call ends up calling bridge_clone_create(), which calls
> D> ether_ifattach(), which calls if_attach() -> if_attach_internal() where
> D> the ifp is added to the ifnet list.
> D> Only when that's done, the lock is dropped.
> D>
> D> Am I overlooking something obvious here?
>
> The code above isn't correct since it holds mutex when calling
> ifcs->ifcs_create(). These methods may (they actually do) call malloc()
> with M_WAITOK.

I'd noticed the malloc() too (see my comment in the PR: "We can't use 
IFNET_WLOCK() here since the code paths may sleep.")
The IFNET_WLOCK() macro is defined as:

#define IFNET_WLOCK() do {  
\
sx_xlock(&ifnet_sxlock);
\
rw_wlock(&ifnet_rwlock);
\
} while (0)

Since rwlocks cannot be held while sleeping, I've added the IFNET_NAMING_ 
macros in the patch, defining them as :

#define IFNET_NAMING_LOCK() sx_xlock(&ifnet_sxlock);
#define IFNET_NAMING_UNLOCK()   sx_unlock(&ifnet_sxlock);

This only does the first half of the work IFNET_WLOCK() normally does. My 
understanding from reading the sx(9) manual is that holding an sx lock while 
sleeping should be allowed.
(WITNESS and INVARIANTS didn't seem to disagree with me. :)
After acquiring the ifnet_sxlock, malloc() should be allowed to sleep while 
allocating memory for the new interface. When it's done sleeping, the 
interface can be added to the ifnet list in if_attach().
if_attach() will IFNET_WLOCK() which should recurse on the ifnet_sxlock and 
acquire a lock on the ifnet_rwlock.

(I'm still confused as to why this wouldn't work.)


> In general FreeBSD uses M_WAITOK on the configuration code paths, like
> any SIOCSsomething ioctl. So to do correct protection, you first need to
> allocate every kind of memory needed, then lock mutex, then run through
> configuration sequence, then release mutex and in case of error free all
> allocated memory

Re: if_clone.c allows creating multiple interfaces with the same name

2011-11-29 Thread Gleb Smirnoff
  Daan,

On Tue, Nov 29, 2011 at 01:07:13AM +0100, Daan Vreeken wrote:
D> Thanks for the looking into this and for your quick commit. I like your twist
D> on the patch with the move from the unit bitmap to allocating unit numbers
D> with alloc_unr(9).
D> 
D> I do have two comments on the new code though.
D> Before, in the !wildcard case, ifc_alloc_unit() would return ENOSPC when the
D> user requested a unit number larger than ifc->ifc_maxunit. Now the function
D> returns EEXIST in that case because alloc_unr_specific() returns -1 both
D> when a number is already allocated and when the number exceeds it's limits.
D> This leads to the somewhat confusing:
D> 
D> # ifconfig bridge123456 create
D> ifconfig: SIOCIFCREATE2: File exists
D> 
D> Apart from that I'm a bit worried about the "/* XXXGL: yep, it's a unit leak
D> */" part of your change. Once a unit number is leaked, there currently seems
D> to be no way to ever get it back. In a worst case scenario, where the names 
of
D> multiple interfaces in a row collides with the numbers alloc_unr() returns, 
an
D> entire row of unit numbers could be leaked during the creation of a single
D> cloned interface.
D> We have a product where cloned interfaces come and go very frequently. I'd
D> hate to run out of unit numbers after 'just a few' years of uptime ;-)
D> 
D> I've created a new patch on top of your change that fixes both of these
D> problems. Could you have a look at the attached diff?

Thanks, I will work on applying it.

D> > Considering the second part, that adds locking. Unfortunately, right now we
D> > have numerous races in the network configuration ocde. Many SIOCSsomething
D> > ioctls can race with each other producing unpredictable results and kernel
D> > panics. So, running two ifconfig(8) in parallel is a bad idea today. :(
D> > Your patch with IFNET_NAMING_LOCK() just plumbs one race case: a race
D> > between two SIOCSIFNAME ioctls. And it doesn't plumb a race between
D> > SIOCSIFNAME vs SIOCIFCREATE, because IFNET_NAMING_LOCK() is dropped after
D> > unit allocation, but prior to interface attachement to global interface
D> > list.
D> 
D> Are you sure? With the patch in the PR, the relevant code in 
D> ifc_simple_create() would become :
D> 
D>  ...
D>  IFNET_NAMING_LOCK();
D>  err = ifc_alloc_unit(ifc, &unit);
D>  if (err != 0) {
D>  IFNET_NAMING_UNLOCK();
D>  return (err);
D>  }
D> 
D>  err = ifcs->ifcs_create(ifc, unit, params);
D>  IFNET_NAMING_UNLOCK();
D>  if (err != 0) {
D>  ifc_free_unit(ifc, unit);
D>  return (err);
D>  }
D>  ...
D> 
D> The lock is acquired before the call to ifc_alloc_unit().
D> In the non-error case (e.g. when creating an if_bridge interface) the call
D> ends up calling bridge_clone_create(), which calls ether_ifattach(), which
D> calls if_attach() -> if_attach_internal() where the ifp is added to the ifnet
D> list.
D> Only when that's done, the lock is dropped.
D> 
D> Am I overlooking something obvious here?

The code above isn't correct since it holds mutex when calling 
ifcs->ifcs_create().
These methods may (they actually do) call malloc() with M_WAITOK.

In general FreeBSD uses M_WAITOK on the configuration code paths, like
any SIOCSsomething ioctl. So to do correct protection, you first need to
allocate every kind of memory needed, then lock mutex, then run through 
configuration
sequence, then release mutex and in case of error free all allocated memory.
Sounds easy, but isn't in practice, especially when several network modules
are involved.

So I'm still thinking about...

D> > From my point of view, we need a generic approach to ioctl() vs ioctl()
D> > races, may be some global serializer of all re-configuration requests of
D> > interfaces and addresses.

... but several developers have noted that this approach may have some hidden
problems.  When experimenting with new CARP, I have tried it on the carp_ioctl()
without any problems. The idea is simple:

static int serializer = 0;
static struct mtx serializer_mtx;
MTX_SYSINIT("sermtx", &serializer_mtx, "ioctl serializer mutex", MTX_DEF);

int
foo_ioctl()
{
mtx_lock(&serializer_mtx);
if (serializer != 0)
msleep(&serializer, &serializer_mtx, 0, "ioctl", 0);
serializer = 1;
mtx_unlock(&serializer_mtx);

... any code here, uncluding calls to different lower layers...

mtx_lock(&serializer_mtx);
serializer = 0;
wakeup(&serializer);
mtx_unlock(&serializer_mtx);

return (error);
}

I have tried this for carp_ioctl() and it worked fine. You can try it for
entire ifioctl() and all its descendants, but be aware of hidden problems :)

-- 
Totus tuus, Glebius.
___
freebsd-current@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/freebsd-current
To unsubscribe, send any mail to "freebsd-current-unsubscr...@freebsd.org"

Re: if_clone.c allows creating multiple interfaces with the same name

2011-11-28 Thread Daan Vreeken
Hi Glebius,

On Friday 25 November 2011 15:32:58 Gleb Smirnoff wrote:
> On Fri, Nov 25, 2011 at 05:19:35PM +0400, Gleb Smirnoff wrote:
> T> On Thu, Nov 24, 2011 at 09:28:51AM +0100, Daan Vreeken wrote:
> T> D> Recently I've discovered a bug in if_clone.c and if.c where the code
> allows T> D> multiple interfaces to be created with exactly the same name
> (which leads to T> D> all sorts of other interesting problems).
> T> D> I've submitted a PR about this with patches, which can be found here
> : T> D>
> T> D> http://www.freebsd.org/cgi/query-pr.cgi?pr=162789
> T> D>
> T> D> Could anyone take a look at it?
> T>
> T> I decided to simply if_clone code utilizing generic unit allocator.
> Patch T> atteched. Now I'll try to merge it with your ideas.
>
> Here is if_cloner patched with additional ifunit() check, as you suggested.
> Please review my patch and test it, and then we can commit it.

Thanks for the looking into this and for your quick commit. I like your twist
on the patch with the move from the unit bitmap to allocating unit numbers
with alloc_unr(9).

I do have two comments on the new code though.
Before, in the !wildcard case, ifc_alloc_unit() would return ENOSPC when the
user requested a unit number larger than ifc->ifc_maxunit. Now the function
returns EEXIST in that case because alloc_unr_specific() returns -1 both
when a number is already allocated and when the number exceeds it's limits.
This leads to the somewhat confusing:

# ifconfig bridge123456 create
ifconfig: SIOCIFCREATE2: File exists

Apart from that I'm a bit worried about the "/* XXXGL: yep, it's a unit leak
*/" part of your change. Once a unit number is leaked, there currently seems
to be no way to ever get it back. In a worst case scenario, where the names of
multiple interfaces in a row collides with the numbers alloc_unr() returns, an
entire row of unit numbers could be leaked during the creation of a single
cloned interface.
We have a product where cloned interfaces come and go very frequently. I'd
hate to run out of unit numbers after 'just a few' years of uptime ;-)

I've created a new patch on top of your change that fixes both of these
problems. Could you have a look at the attached diff?
In case the attachment doesn't survive the list, it can also be found here:


http://www.vitsch.nl/pub-diffs/if_clone-ENOSPC-and-unr-leak-fix-2011-11-29.diff


> Considering the second part, that adds locking. Unfortunately, right now we
> have numerous races in the network configuration ocde. Many SIOCSsomething
> ioctls can race with each other producing unpredictable results and kernel
> panics. So, running two ifconfig(8) in parallel is a bad idea today. :(
> Your patch with IFNET_NAMING_LOCK() just plumbs one race case: a race
> between two SIOCSIFNAME ioctls. And it doesn't plumb a race between
> SIOCSIFNAME vs SIOCIFCREATE, because IFNET_NAMING_LOCK() is dropped after
> unit allocation, but prior to interface attachement to global interface
> list.

Are you sure? With the patch in the PR, the relevant code in 
ifc_simple_create() would become :

...
IFNET_NAMING_LOCK();
err = ifc_alloc_unit(ifc, &unit);
if (err != 0) {
IFNET_NAMING_UNLOCK();
return (err);
}

err = ifcs->ifcs_create(ifc, unit, params);
IFNET_NAMING_UNLOCK();
if (err != 0) {
ifc_free_unit(ifc, unit);
return (err);
}
...

The lock is acquired before the call to ifc_alloc_unit().
In the non-error case (e.g. when creating an if_bridge interface) the call
ends up calling bridge_clone_create(), which calls ether_ifattach(), which
calls if_attach() -> if_attach_internal() where the ifp is added to the ifnet
list.
Only when that's done, the lock is dropped.

Am I overlooking something obvious here?


> From my point of view, we need a generic approach to ioctl() vs ioctl()
> races, may be some global serializer of all re-configuration requests of
> interfaces and addresses.


Thanks,
-- 
Daan Vreeken
Vitsch Electronics
http://Vitsch.nl
tel: +31-(0)40-7113051 / +31-(0)6-46210825
KvK nr: 17174380
Index: sys/net/if_clone.c
===
--- sys/net/if_clone.c	(revision 228109)
+++ sys/net/if_clone.c	(working copy)
@@ -436,30 +436,49 @@
 }
 
 int
-ifc_alloc_unit(struct if_clone *ifc, int *unit)
+ifc_alloc_unit(struct if_clone *ifc, int *unit_nr)
 {
 	char name[IFNAMSIZ];
-	int wildcard;
+	int ret, unit, wildcard;
 
-	wildcard = (*unit < 0);
+	unit = *unit_nr;
+	wildcard = (unit < 0);
 retry:
-	if (wildcard) {
-		*unit = alloc_unr(ifc->ifc_unrhdr);
-		if (*unit == -1)
+	if (unit > ifc->ifc_maxunit)
+		return (ENOSPC);
+
+	if (unit < 0) {
+		/* first, try to allocate a unit number automatically */
+		unit = alloc_unr(ifc->ifc_unrhdr);
+		if (unit == -1)
 			return (ENOSPC);
 	} else {
-		*unit = alloc_unr_specific(ifc->ifc_unrhdr, *unit);
-		if (*unit == -

Re: if_clone.c allows creating multiple interfaces with the same name

2011-11-25 Thread Gleb Smirnoff
On Fri, Nov 25, 2011 at 05:19:35PM +0400, Gleb Smirnoff wrote:
T> On Thu, Nov 24, 2011 at 09:28:51AM +0100, Daan Vreeken wrote:
T> D> Recently I've discovered a bug in if_clone.c and if.c where the code 
allows 
T> D> multiple interfaces to be created with exactly the same name (which leads 
to 
T> D> all sorts of other interesting problems).
T> D> I've submitted a PR about this with patches, which can be found here :
T> D> 
T> D>   http://www.freebsd.org/cgi/query-pr.cgi?pr=162789
T> D> 
T> D> Could anyone take a look at it?
T> 
T> I decided to simply if_clone code utilizing generic unit allocator. Patch
T> atteched. Now I'll try to merge it with your ideas.

Here is if_cloner patched with additional ifunit() check, as you suggested. 
Please
review my patch and test it, and then we can commit it.

Considering the second part, that adds locking. Unfortunately, right now we have
numerous races in the network configuration ocde. Many SIOCSsomething ioctls
can race with each other producing unpredictable results and kernel panics.
So, running two ifconfig(8) in parallel is a bad idea today. :( Your patch with
IFNET_NAMING_LOCK() just plumbs one race case: a race between two SIOCSIFNAME
ioctls. And it doesn't plumb a race between SIOCSIFNAME vs SIOCIFCREATE,
because IFNET_NAMING_LOCK() is dropped after unit allocation, but prior to
interface attachement to global interface list.

>From my point of view, we need a generic approach to ioctl() vs ioctl() races,
may be some global serializer of all re-configuration requests of interfaces
and addresses.

-- 
Totus tuus, Glebius.
Index: if_clone.c
===
--- if_clone.c	(revision 227964)
+++ if_clone.c	(working copy)
@@ -282,33 +282,34 @@
 /*
  * Register a network interface cloner.
  */
-void
+int
 if_clone_attach(struct if_clone *ifc)
 {
-	int len, maxclone;
+	struct if_clone *ifc1;
 
-	/*
-	 * Compute bitmap size and allocate it.
-	 */
-	maxclone = ifc->ifc_maxunit + 1;
-	len = maxclone >> 3;
-	if ((len << 3) < maxclone)
-		len++;
-	ifc->ifc_units = malloc(len, M_CLONE, M_WAITOK | M_ZERO);
-	ifc->ifc_bmlen = len;
+	KASSERT(ifc->ifc_name != NULL, ("%s: no name\n", __func__));
+
 	IF_CLONE_LOCK_INIT(ifc);
 	IF_CLONE_ADDREF(ifc);
+	ifc->ifc_unrhdr = new_unrhdr(0, ifc->ifc_maxunit, &ifc->ifc_mtx);
+	LIST_INIT(&ifc->ifc_iflist);
 
 	IF_CLONERS_LOCK();
+	LIST_FOREACH(ifc1, &V_if_cloners, ifc_list)
+		if (strcmp(ifc->ifc_name, ifc1->ifc_name) == 0) {
+			IF_CLONERS_UNLOCK();
+			IF_CLONE_REMREF(ifc);
+			return (EEXIST);
+		}
 	LIST_INSERT_HEAD(&V_if_cloners, ifc, ifc_list);
 	V_if_cloners_count++;
 	IF_CLONERS_UNLOCK();
 
-	LIST_INIT(&ifc->ifc_iflist);
-
 	if (ifc->ifc_attach != NULL)
 		(*ifc->ifc_attach)(ifc);
 	EVENTHANDLER_INVOKE(if_clone_event, ifc);
+
+	return (0);
 }
 
 /*
@@ -338,16 +339,12 @@
 static void
 if_clone_free(struct if_clone *ifc)
 {
-	for (int bytoff = 0; bytoff < ifc->ifc_bmlen; bytoff++) {
-		KASSERT(ifc->ifc_units[bytoff] == 0x00,
-		("ifc_units[%d] is not empty", bytoff));
-	}
 
 	KASSERT(LIST_EMPTY(&ifc->ifc_iflist),
 	("%s: ifc_iflist not empty", __func__));
 
 	IF_CLONE_LOCK_DESTROY(ifc);
-	free(ifc->ifc_units, M_CLONE);
+	delete_unrhdr(ifc->ifc_unrhdr);
 }
 
 /*
@@ -441,73 +438,40 @@
 int
 ifc_alloc_unit(struct if_clone *ifc, int *unit)
 {
-	int wildcard, bytoff, bitoff;
-	int err = 0;
+	char name[IFNAMSIZ];
+	int wildcard;
 
-	IF_CLONE_LOCK(ifc);
-
-	bytoff = bitoff = 0;
 	wildcard = (*unit < 0);
-	/*
-	 * Find a free unit if none was given.
-	 */
+retry:
 	if (wildcard) {
-		while ((bytoff < ifc->ifc_bmlen)
-		&& (ifc->ifc_units[bytoff] == 0xff))
-			bytoff++;
-		if (bytoff >= ifc->ifc_bmlen) {
-			err = ENOSPC;
-			goto done;
-		}
-		while ((ifc->ifc_units[bytoff] & (1 << bitoff)) != 0)
-			bitoff++;
-		*unit = (bytoff << 3) + bitoff;
+		*unit = alloc_unr(ifc->ifc_unrhdr);
+		if (*unit == -1)
+			return (ENOSPC);
+	} else {
+		*unit = alloc_unr_specific(ifc->ifc_unrhdr, *unit);
+		if (*unit == -1)
+			return (EEXIST);
 	}
 
-	if (*unit > ifc->ifc_maxunit) {
-		err = ENOSPC;
-		goto done;
+	snprintf(name, IFNAMSIZ, "%s%d", ifc->ifc_name, *unit);
+	if (ifunit(name) != NULL) {
+		if (wildcard)
+			goto retry;	/* XXXGL: yep, it's a unit leak */
+		else
+			return (EEXIST);
 	}
 
-	if (!wildcard) {
-		bytoff = *unit >> 3;
-		bitoff = *unit - (bytoff << 3);
-	}
+	IF_CLONE_ADDREF(ifc);
 
-	if((ifc->ifc_units[bytoff] & (1 << bitoff)) != 0) {
-		err = EEXIST;
-		goto done;
-	}
-	/*
-	 * Allocate the unit in the bitmap.
-	 */
-	KASSERT((ifc->ifc_units[bytoff] & (1 << bitoff)) == 0,
-	("%s: bit is already set", __func__));
-	ifc->ifc_units[bytoff] |= (1 << bitoff);
-	IF_CLONE_ADDREF_LOCKED(ifc);
-
-done:
-	IF_CLONE_UNLOCK(ifc);
-	return (err);
+	return (0);
 }
 
 void
 ifc_free_unit(struct if_clone *ifc, int unit)
 {
-	int bytoff, bitoff;
 
-
-	/*
-	 * Compute offset in the bitmap and deallocate the unit.
-	 */
-	bytoff = unit >> 3;
-	bitoff = unit - (bytoff << 3);
-
-	IF_C

Re: if_clone.c allows creating multiple interfaces with the same name

2011-11-25 Thread Gleb Smirnoff
On Thu, Nov 24, 2011 at 09:28:51AM +0100, Daan Vreeken wrote:
D> Recently I've discovered a bug in if_clone.c and if.c where the code allows 
D> multiple interfaces to be created with exactly the same name (which leads to 
D> all sorts of other interesting problems).
D> I've submitted a PR about this with patches, which can be found here :
D> 
D>  http://www.freebsd.org/cgi/query-pr.cgi?pr=162789
D> 
D> Could anyone take a look at it?

I decided to simply if_clone code utilizing generic unit allocator. Patch
atteched. Now I'll try to merge it with your ideas.

-- 
Totus tuus, Glebius.
Index: if_clone.c
===
--- if_clone.c	(revision 227964)
+++ if_clone.c	(working copy)
@@ -282,33 +282,34 @@
 /*
  * Register a network interface cloner.
  */
-void
+int
 if_clone_attach(struct if_clone *ifc)
 {
-	int len, maxclone;
+	struct if_clone *ifc1;
 
-	/*
-	 * Compute bitmap size and allocate it.
-	 */
-	maxclone = ifc->ifc_maxunit + 1;
-	len = maxclone >> 3;
-	if ((len << 3) < maxclone)
-		len++;
-	ifc->ifc_units = malloc(len, M_CLONE, M_WAITOK | M_ZERO);
-	ifc->ifc_bmlen = len;
+	KASSERT(ifc->ifc_name != NULL, ("%s: no name\n", __func__));
+
 	IF_CLONE_LOCK_INIT(ifc);
 	IF_CLONE_ADDREF(ifc);
+	ifc->ifc_unrhdr = new_unrhdr(0, ifc->ifc_maxunit, &ifc->ifc_mtx);
+	LIST_INIT(&ifc->ifc_iflist);
 
 	IF_CLONERS_LOCK();
+	LIST_FOREACH(ifc1, &V_if_cloners, ifc_list)
+		if (strcmp(ifc->ifc_name, ifc1->ifc_name) == 0) {
+			IF_CLONERS_UNLOCK();
+			IF_CLONE_REMREF(ifc);
+			return (EEXIST);
+		}
 	LIST_INSERT_HEAD(&V_if_cloners, ifc, ifc_list);
 	V_if_cloners_count++;
 	IF_CLONERS_UNLOCK();
 
-	LIST_INIT(&ifc->ifc_iflist);
-
 	if (ifc->ifc_attach != NULL)
 		(*ifc->ifc_attach)(ifc);
 	EVENTHANDLER_INVOKE(if_clone_event, ifc);
+
+	return (0);
 }
 
 /*
@@ -338,16 +339,12 @@
 static void
 if_clone_free(struct if_clone *ifc)
 {
-	for (int bytoff = 0; bytoff < ifc->ifc_bmlen; bytoff++) {
-		KASSERT(ifc->ifc_units[bytoff] == 0x00,
-		("ifc_units[%d] is not empty", bytoff));
-	}
 
 	KASSERT(LIST_EMPTY(&ifc->ifc_iflist),
 	("%s: ifc_iflist not empty", __func__));
 
 	IF_CLONE_LOCK_DESTROY(ifc);
-	free(ifc->ifc_units, M_CLONE);
+	delete_unrhdr(ifc->ifc_unrhdr);
 }
 
 /*
@@ -441,73 +438,32 @@
 int
 ifc_alloc_unit(struct if_clone *ifc, int *unit)
 {
-	int wildcard, bytoff, bitoff;
-	int err = 0;
+	int error = 0;
 
-	IF_CLONE_LOCK(ifc);
-
-	bytoff = bitoff = 0;
-	wildcard = (*unit < 0);
-	/*
-	 * Find a free unit if none was given.
-	 */
-	if (wildcard) {
-		while ((bytoff < ifc->ifc_bmlen)
-		&& (ifc->ifc_units[bytoff] == 0xff))
-			bytoff++;
-		if (bytoff >= ifc->ifc_bmlen) {
-			err = ENOSPC;
-			goto done;
-		}
-		while ((ifc->ifc_units[bytoff] & (1 << bitoff)) != 0)
-			bitoff++;
-		*unit = (bytoff << 3) + bitoff;
+	if (*unit < 0) {	/* Wildcard. */
+		*unit = alloc_unr(ifc->ifc_unrhdr);
+		if (*unit == -1)
+			error = ENOSPC;
+	} else {
+		*unit = alloc_unr_specific(ifc->ifc_unrhdr, *unit);
+		if (*unit == -1)
+			error = EEXIST;
 	}
 
-	if (*unit > ifc->ifc_maxunit) {
-		err = ENOSPC;
-		goto done;
-	}
+	if (error)
+		return (error);
 
-	if (!wildcard) {
-		bytoff = *unit >> 3;
-		bitoff = *unit - (bytoff << 3);
-	}
+	IF_CLONE_ADDREF(ifc);
 
-	if((ifc->ifc_units[bytoff] & (1 << bitoff)) != 0) {
-		err = EEXIST;
-		goto done;
-	}
-	/*
-	 * Allocate the unit in the bitmap.
-	 */
-	KASSERT((ifc->ifc_units[bytoff] & (1 << bitoff)) == 0,
-	("%s: bit is already set", __func__));
-	ifc->ifc_units[bytoff] |= (1 << bitoff);
-	IF_CLONE_ADDREF_LOCKED(ifc);
-
-done:
-	IF_CLONE_UNLOCK(ifc);
-	return (err);
+	return (0);
 }
 
 void
 ifc_free_unit(struct if_clone *ifc, int unit)
 {
-	int bytoff, bitoff;
 
-
-	/*
-	 * Compute offset in the bitmap and deallocate the unit.
-	 */
-	bytoff = unit >> 3;
-	bitoff = unit - (bytoff << 3);
-
-	IF_CLONE_LOCK(ifc);
-	KASSERT((ifc->ifc_units[bytoff] & (1 << bitoff)) != 0,
-	("%s: bit is already cleared", __func__));
-	ifc->ifc_units[bytoff] &= ~(1 << bitoff);
-	IF_CLONE_REMREF_LOCKED(ifc);	/* releases lock */
+	free_unr(ifc->ifc_unrhdr, unit);
+	IF_CLONE_REMREF(ifc);
 }
 
 void
Index: if_clone.h
===
--- if_clone.h	(revision 227964)
+++ if_clone.h	(working copy)
@@ -37,7 +37,15 @@
 
 #define IFC_CLONE_INITIALIZER(name, data, maxunit,			\
 attach, match, create, destroy)	\
-{ { 0 }, name, maxunit, NULL, 0, data, attach, match, create, destroy }
+{	\
+	.ifc_name = name,		\
+	.ifc_maxunit = maxunit,		\
+	.ifc_data = data,		\
+	.ifc_attach = attach,		\
+	.ifc_match = match,		\
+	.ifc_create = create,		\
+	.ifc_destroy = destroy,		\
+}
 
 /*
  * Structure describing a `cloning' interface.
@@ -52,10 +60,7 @@
 	LIST_ENTRY(if_clone) ifc_list;	/* (e) On list of cloners */
 	const char *ifc_name;		/* (c) Name of device, e.g. `gif' */
 	int ifc_maxunit;		/* (c) Maximum unit number */
-	unsigned char *ifc_units;	/* (i) B

Re: if_clone.c allows creating multiple interfaces with the same name

2011-11-25 Thread Gleb Smirnoff
On Thu, Nov 24, 2011 at 09:28:51AM +0100, Daan Vreeken wrote:
D> Hi All,
D> 
D> Recently I've discovered a bug in if_clone.c and if.c where the code allows 
D> multiple interfaces to be created with exactly the same name (which leads to 
D> all sorts of other interesting problems).
D> I've submitted a PR about this with patches, which can be found here :
D> 
D>  http://www.freebsd.org/cgi/query-pr.cgi?pr=162789
D> 
D> Could anyone take a look at it?

I'll try to handle this.

-- 
Totus tuus, Glebius.
___
freebsd-current@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/freebsd-current
To unsubscribe, send any mail to "freebsd-current-unsubscr...@freebsd.org"


if_clone.c allows creating multiple interfaces with the same name

2011-11-24 Thread Daan Vreeken
Hi All,

Recently I've discovered a bug in if_clone.c and if.c where the code allows 
multiple interfaces to be created with exactly the same name (which leads to 
all sorts of other interesting problems).
I've submitted a PR about this with patches, which can be found here :

http://www.freebsd.org/cgi/query-pr.cgi?pr=162789

Could anyone take a look at it?


Regards,
-- 
Daan Vreeken
Vitsch Electronics
http://Vitsch.nl
tel: +31-(0)40-7113051 / +31-(0)6-46210825
KvK nr: 17174380
___
freebsd-current@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/freebsd-current
To unsubscribe, send any mail to "freebsd-current-unsubscr...@freebsd.org"