Date: Tue, 29 Nov 2011 01:07:13 +0100 From: Daan Vreeken <Daan@vitsch.nl> To: Gleb Smirnoff <glebius@freebsd.org> Cc: FreeBSD Current <freebsd-current@freebsd.org> Subject: Re: if_clone.c allows creating multiple interfaces with the same name Message-ID: <201111290107.13631.Daan@vitsch.nl> In-Reply-To: <20111125143257.GR96616@FreeBSD.org> References: <201111240928.51162.Daan@vitsch.nl> <20111125131935.GP96616@FreeBSD.org> <20111125143257.GR96616@FreeBSD.org>
next in thread | previous in thread | raw e-mail | index | archive | help
--Boundary-00=_xIC1OndPncenpNF Content-Type: text/plain; charset="iso-8859-1" Content-Transfer-Encoding: 7bit Content-Disposition: inline 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 --Boundary-00=_xIC1OndPncenpNF Content-Type: text/x-diff; charset="iso-8859-1"; name="if_clone-ENOSPC-and-unr-leak-fix-2011-11-29.diff" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="if_clone-ENOSPC-and-unr-leak-fix-2011-11-29.diff" 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 == -1) - return (EEXIST); + ret = alloc_unr_specific(ifc->ifc_unrhdr, unit); + if (ret == -1) { + if (wildcard) { + /* unit number already claimed. try next */ + unit++; + goto retry; + } else { + /* specified unit number already claimed */ + return (EEXIST); + } + } } - snprintf(name, IFNAMSIZ, "%s%d", ifc->ifc_name, *unit); + /* if we reach this point, a unit number has been allocated */ + 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); + /* name is already taken */ + free_unr(ifc->ifc_unrhdr, unit); + if (wildcard) { + /* increment unit number and try again */ + unit++; + goto retry; + } + return (EEXIST); } + *unit_nr = unit; IF_CLONE_ADDREF(ifc); --Boundary-00=_xIC1OndPncenpNF--
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201111290107.13631.Daan>