diff mbox

[v3] netfilter: ipset: Fix serious failure in CIDR tracking

Message ID 1378894613-37279-1-git-send-email-oliver@8.c.9.b.0.7.4.0.1.0.0.2.ip6.arpa
State Superseded
Headers show

Commit Message

Oliver Smith Sept. 11, 2013, 10:16 a.m. UTC
From: Oliver Smith <oliver@8.c.9.b.0.7.4.0.1.0.0.2.ip6.arpa>

This fixes a serious bug affecting all hash types with a net element -
specifically, if a CIDR value is deleted such that none of the same size
exist any more, all larger (less-specific) values will then fail to
match. Adding back any prefix with a CIDR equal to or more specific than
the one deleted will fix it.

Steps to reproduce:
ipset -N test hash:net
ipset -A test 1.1.0.0/16
ipset -A test 2.2.2.0/24
ipset -T test 1.1.1.1		#1.1.1.1 IS in set
ipset -D test 2.2.2.0/24
ipset -T test 1.1.1.1		#1.1.1.1 IS NOT in set

This is due to the fact that the nets counter was unconditionally
decremented prior to the iteration that shifts up the entries. Now, we
first check if there is a proceeding entry and if not, decrement it and
return. Otherwise, we proceed to iterate and then clean up the last
element afterwards.

Signed-off-by: Oliver Smith <oliver@8.c.9.b.0.7.4.0.1.0.0.2.ip6.arpa>
---
 kernel/net/netfilter/ipset/ip_set_hash_gen.h | 13 +++++++------
 1 file changed, 7 insertions(+), 6 deletions(-)

Comments

Jozsef Kadlecsik Sept. 11, 2013, 2:01 p.m. UTC | #1
Hi Oliver,

On Wed, 11 Sep 2013, Oliver wrote:

> From: Oliver Smith <oliver@8.c.9.b.0.7.4.0.1.0.0.2.ip6.arpa>
> 
> This fixes a serious bug affecting all hash types with a net element -
> specifically, if a CIDR value is deleted such that none of the same size
> exist any more, all larger (less-specific) values will then fail to
> match. Adding back any prefix with a CIDR equal to or more specific than
> the one deleted will fix it.
> 
> Steps to reproduce:
> ipset -N test hash:net
> ipset -A test 1.1.0.0/16
> ipset -A test 2.2.2.0/24
> ipset -T test 1.1.1.1		#1.1.1.1 IS in set
> ipset -D test 2.2.2.0/24
> ipset -T test 1.1.1.1		#1.1.1.1 IS NOT in set
> 
> This is due to the fact that the nets counter was unconditionally
> decremented prior to the iteration that shifts up the entries. Now, we
> first check if there is a proceeding entry and if not, decrement it and
> return. Otherwise, we proceed to iterate and then clean up the last
> element afterwards.
> 
> Signed-off-by: Oliver Smith <oliver@8.c.9.b.0.7.4.0.1.0.0.2.ip6.arpa>
> ---
>  kernel/net/netfilter/ipset/ip_set_hash_gen.h | 13 +++++++------
>  1 file changed, 7 insertions(+), 6 deletions(-)
> 
> diff --git a/kernel/net/netfilter/ipset/ip_set_hash_gen.h b/kernel/net/netfilter/ipset/ip_set_hash_gen.h
> index 7a5b776..6599ea8 100644
> --- a/kernel/net/netfilter/ipset/ip_set_hash_gen.h
> +++ b/kernel/net/netfilter/ipset/ip_set_hash_gen.h
> @@ -307,19 +307,20 @@ mtype_add_cidr(struct htype *h, u8 cidr, u8 nets_length, u8 n)
>  static void
>  mtype_del_cidr(struct htype *h, u8 cidr, u8 nets_length, u8 n)
>  {
> -	u8 i, j;
> +	u8 i, j, net_end = nets_length - 1;
>  
> -	for (i = 0; i < nets_length - 1 && h->nets[i].cidr[n] != cidr; i++)
> +	for (i = 0; i < net_end && h->nets[i].cidr[n] != cidr; i++)
>  		;
> -	h->nets[i].nets[n]--;
> -
> -	if (h->nets[i].nets[n] != 0)
> +	if (h->nets[i].nets[n] > 1 || i == net_end || !h->nets[i + 1].nets[n]) {
> +		h->nets[i].nets[n]--;
>  		return;
> +	}

If you restored the original code in the part above, then the whole thing 
could be collapsed into starting the next loop from j = i + 1 (and 
changing the indices in the for body accordingly).
  
> -	for (j = i; j < nets_length - 1 && h->nets[j].nets[n]; j++) {
> +	for (j = i; j < net_end && h->nets[j].nets[n]; j++) {
>  		h->nets[j].cidr[n] = h->nets[j + 1].cidr[n];
>  		h->nets[j].nets[n] = h->nets[j + 1].nets[n];
>  	}
> +	h->nets[j].nets[n] = 0;

Here the checking of the value "j" is missing.

>  }
>  #endif

Best regards,
Jozsef
-
E-mail  : kadlec@blackhole.kfki.hu, kadlecsik.jozsef@wigner.mta.hu
PGP key : http://www.kfki.hu/~kadlec/pgp_public_key.txt
Address : Wigner Research Centre for Physics, Hungarian Academy of Sciences
          H-1525 Budapest 114, POB. 49, Hungary
--
To unsubscribe from this list: send the line "unsubscribe netfilter-devel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Oliver Smith Sept. 11, 2013, 3:32 p.m. UTC | #2
On Wednesday 11 September 2013 16:01:18 Jozsef Kadlecsik wrote:
> Hi Oliver,
> 
> On Wed, 11 Sep 2013, Oliver wrote:
> > From: Oliver Smith <oliver@8.c.9.b.0.7.4.0.1.0.0.2.ip6.arpa>
> > 
> > This fixes a serious bug affecting all hash types with a net element -
> > specifically, if a CIDR value is deleted such that none of the same size
> > exist any more, all larger (less-specific) values will then fail to
> > match. Adding back any prefix with a CIDR equal to or more specific than
> > the one deleted will fix it.
> > 
> > Steps to reproduce:
> > ipset -N test hash:net
> > ipset -A test 1.1.0.0/16
> > ipset -A test 2.2.2.0/24
> > ipset -T test 1.1.1.1		#1.1.1.1 IS in set
> > ipset -D test 2.2.2.0/24
> > ipset -T test 1.1.1.1		#1.1.1.1 IS NOT in set
> > 
> > This is due to the fact that the nets counter was unconditionally
> > decremented prior to the iteration that shifts up the entries. Now, we
> > first check if there is a proceeding entry and if not, decrement it and
> > return. Otherwise, we proceed to iterate and then clean up the last
> > element afterwards.
> > 
> > Signed-off-by: Oliver Smith <oliver@8.c.9.b.0.7.4.0.1.0.0.2.ip6.arpa>
> > ---
> > 
> >  kernel/net/netfilter/ipset/ip_set_hash_gen.h | 13 +++++++------
> >  1 file changed, 7 insertions(+), 6 deletions(-)
> > 
> > diff --git a/kernel/net/netfilter/ipset/ip_set_hash_gen.h
> > b/kernel/net/netfilter/ipset/ip_set_hash_gen.h index 7a5b776..6599ea8
> > 100644
> > --- a/kernel/net/netfilter/ipset/ip_set_hash_gen.h
> > +++ b/kernel/net/netfilter/ipset/ip_set_hash_gen.h
> > @@ -307,19 +307,20 @@ mtype_add_cidr(struct htype *h, u8 cidr, u8
> > nets_length, u8 n)> 
> >  static void
> >  mtype_del_cidr(struct htype *h, u8 cidr, u8 nets_length, u8 n)
> >  {
> > 
> > -	u8 i, j;
> > +	u8 i, j, net_end = nets_length - 1;
> > 
> > -	for (i = 0; i < nets_length - 1 && h->nets[i].cidr[n] != cidr; i++)
> > +	for (i = 0; i < net_end && h->nets[i].cidr[n] != cidr; i++)
> > 
> >  		;
> > 
> > -	h->nets[i].nets[n]--;
> > -
> > -	if (h->nets[i].nets[n] != 0)
> > +	if (h->nets[i].nets[n] > 1 || i == net_end || !h->nets[i + 1].nets[n]) 
{
> > +		h->nets[i].nets[n]--;
> > 
> >  		return;
> > 
> > +	}
> 
> If you restored the original code in the part above, then the whole thing
> could be collapsed into starting the next loop from j = i + 1 (and
> changing the indices in the for body accordingly).

I'm not sure I follow; the whole problem is that the proceeding loop fails 
because the element at j gets set to zero, causing it to be skipped when we do 
our shift-up below - why would we want to bother with looping when we can just 
decrement and return immediately?

> 
> > -	for (j = i; j < nets_length - 1 && h->nets[j].nets[n]; j++) {
> > +	for (j = i; j < net_end && h->nets[j].nets[n]; j++) {
> > 
> >  		h->nets[j].cidr[n] = h->nets[j + 1].cidr[n];
> >  		h->nets[j].nets[n] = h->nets[j + 1].nets[n];
> >  	
> >  	}
> > 
> > +	h->nets[j].nets[n] = 0;
> 
> Here the checking of the value "j" is missing.

I suppose we can check this, but what's the point really? j is always going to 
be nets_length - 1, so setting that element to zero is either necessary for 
cleanup, or won't do anything of use (nor harm) - why add in a memory read to 
see if we need to do a memory write when there's no harm in just doing it 
regardless?

Kind Regards,
Oliver.
--
To unsubscribe from this list: send the line "unsubscribe netfilter-devel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Jozsef Kadlecsik Sept. 11, 2013, 7:32 p.m. UTC | #3
On Wed, 11 Sep 2013, Oliver wrote:

> On Wednesday 11 September 2013 16:01:18 Jozsef Kadlecsik wrote:
> > 
> > On Wed, 11 Sep 2013, Oliver wrote:
> > > From: Oliver Smith <oliver@8.c.9.b.0.7.4.0.1.0.0.2.ip6.arpa>
> > > 
> > > This fixes a serious bug affecting all hash types with a net element -
> > > specifically, if a CIDR value is deleted such that none of the same size
> > > exist any more, all larger (less-specific) values will then fail to
> > > match. Adding back any prefix with a CIDR equal to or more specific than
> > > the one deleted will fix it.
> > > 
> > > Steps to reproduce:
> > > ipset -N test hash:net
> > > ipset -A test 1.1.0.0/16
> > > ipset -A test 2.2.2.0/24
> > > ipset -T test 1.1.1.1		#1.1.1.1 IS in set
> > > ipset -D test 2.2.2.0/24
> > > ipset -T test 1.1.1.1		#1.1.1.1 IS NOT in set
> > > 
> > > This is due to the fact that the nets counter was unconditionally
> > > decremented prior to the iteration that shifts up the entries. Now, we
> > > first check if there is a proceeding entry and if not, decrement it and
> > > return. Otherwise, we proceed to iterate and then clean up the last
> > > element afterwards.
> > > 
> > > Signed-off-by: Oliver Smith <oliver@8.c.9.b.0.7.4.0.1.0.0.2.ip6.arpa>
> > > ---
> > > 
> > >  kernel/net/netfilter/ipset/ip_set_hash_gen.h | 13 +++++++------
> > >  1 file changed, 7 insertions(+), 6 deletions(-)
> > > 
> > > diff --git a/kernel/net/netfilter/ipset/ip_set_hash_gen.h
> > > b/kernel/net/netfilter/ipset/ip_set_hash_gen.h index 7a5b776..6599ea8
> > > 100644
> > > --- a/kernel/net/netfilter/ipset/ip_set_hash_gen.h
> > > +++ b/kernel/net/netfilter/ipset/ip_set_hash_gen.h
> > > @@ -307,19 +307,20 @@ mtype_add_cidr(struct htype *h, u8 cidr, u8
> > > nets_length, u8 n)> 
> > >  static void
> > >  mtype_del_cidr(struct htype *h, u8 cidr, u8 nets_length, u8 n)
> > >  {
> > > 
> > > -	u8 i, j;
> > > +	u8 i, j, net_end = nets_length - 1;
> > > 
> > > -	for (i = 0; i < nets_length - 1 && h->nets[i].cidr[n] != cidr; i++)
> > > +	for (i = 0; i < net_end && h->nets[i].cidr[n] != cidr; i++)
> > > 
> > >  		;
> > > 
> > > -	h->nets[i].nets[n]--;
> > > -
> > > -	if (h->nets[i].nets[n] != 0)
> > > +	if (h->nets[i].nets[n] > 1 || i == net_end || !h->nets[i + 1].nets[n]) 
> {
> > > +		h->nets[i].nets[n]--;
> > > 
> > >  		return;
> > > 
> > > +	}
> > 
> > If you restored the original code in the part above, then the whole thing
> > could be collapsed into starting the next loop from j = i + 1 (and
> > changing the indices in the for body accordingly).
> 
> I'm not sure I follow; the whole problem is that the proceeding loop 
> fails because the element at j gets set to zero, causing it to be 
> skipped when we do our shift-up below - why would we want to bother with 
> looping when we can just decrement and return immediately?

The for loop below would not be started due to the conditions: actually, 
then conditions above ("i == net_end || !h->nets[i + 1].nets[n]") would be 
"moved" here. In another words, would not be duplicated:

	for (j = i + 1; j < nets_length - 1 && h->nets[j].nets[n]; j++)
		h->nets[j - 1].cidr[n] = h->nets[j + 1].cidr[n];
		..
	}
	if (j < nets_length)
		h->nets[j - 1].nets[n] = 0;

> > > -	for (j = i; j < nets_length - 1 && h->nets[j].nets[n]; j++) {
> > > +	for (j = i; j < net_end && h->nets[j].nets[n]; j++) {
> > > 
> > >  		h->nets[j].cidr[n] = h->nets[j + 1].cidr[n];
> > >  		h->nets[j].nets[n] = h->nets[j + 1].nets[n];
> > >  	
> > >  	}
> > > 
> > > +	h->nets[j].nets[n] = 0;
> > 
> > Here the checking of the value "j" is missing.
> 
> I suppose we can check this, but what's the point really? j is always 
> going to be nets_length - 1, so setting that element to zero is either 
> necessary for cleanup, or won't do anything of use (nor harm) - why add 
> in a memory read to see if we need to do a memory write when there's no 
> harm in just doing it regardless?

What is the difference between the first for loop in this function and 
this second one, from the loop variable point of view? Or, what is the 
value of "j" after the for loop, if all nets[j] slots are filled up?

Best regards,
Jozsef
-
E-mail  : kadlec@blackhole.kfki.hu, kadlecsik.jozsef@wigner.mta.hu
PGP key : http://www.kfki.hu/~kadlec/pgp_public_key.txt
Address : Wigner Research Centre for Physics, Hungarian Academy of Sciences
          H-1525 Budapest 114, POB. 49, Hungary
--
To unsubscribe from this list: send the line "unsubscribe netfilter-devel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Oliver Smith Sept. 12, 2013, 7:45 a.m. UTC | #4
On Wednesday 11 September 2013 21:32:26 Jozsef Kadlecsik wrote:
> On Wed, 11 Sep 2013, Oliver wrote:
> > On Wednesday 11 September 2013 16:01:18 Jozsef Kadlecsik wrote:
> > > On Wed, 11 Sep 2013, Oliver wrote:
<snip>
> > > 
> > > If you restored the original code in the part above, then the whole
> > > thing
> > > could be collapsed into starting the next loop from j = i + 1 (and
> > > changing the indices in the for body accordingly).
> > 
> > I'm not sure I follow; the whole problem is that the proceeding loop
> > fails because the element at j gets set to zero, causing it to be
> > skipped when we do our shift-up below - why would we want to bother with
> > looping when we can just decrement and return immediately?
> 
> The for loop below would not be started due to the conditions: actually,
> then conditions above ("i == net_end || !h->nets[i + 1].nets[n]") would be
> "moved" here. In another words, would not be duplicated:
> 
> 	for (j = i + 1; j < nets_length - 1 && h->nets[j].nets[n]; j++)
> 		h->nets[j - 1].cidr[n] = h->nets[j + 1].cidr[n];
> 		..
> 	}
> 	if (j < nets_length)
> 		h->nets[j - 1].nets[n] = 0;
> 

I don't really like this idea; i + 1 after the second iteration will point to 
whatever was shifted up, this obviously doesn't really present a problem since 
if the element is empty, we won't iterate any more, but I don't think it looks 
good for code clarity and it's just going to be a non-changing value that the 
processor must pointlessly compare on every iteration (first and second run 
notwithstanding).

Checking i against net_end is not a problem either, but again, I don't see why 
we would want to make the processor pointlessly recompare something that is 
guaranteed to be true on every iteration, especially since it's not going to 
make the code "simpler" or easier to follow.

> > > > -	for (j = i; j < nets_length - 1 && h->nets[j].nets[n]; j++) {
> > > > +	for (j = i; j < net_end && h->nets[j].nets[n]; j++) {
> > > > 
> > > >  		h->nets[j].cidr[n] = h->nets[j + 1].cidr[n];
> > > >  		h->nets[j].nets[n] = h->nets[j + 1].nets[n];
> > > >  	
> > > >  	}
> > > > 
> > > > +	h->nets[j].nets[n] = 0;
> > > 
> > > Here the checking of the value "j" is missing.
> > 
> > I suppose we can check this, but what's the point really? j is always
> > going to be nets_length - 1, so setting that element to zero is either
> > necessary for cleanup, or won't do anything of use (nor harm) - why add
> > in a memory read to see if we need to do a memory write when there's no
> > harm in just doing it regardless?
> 
> What is the difference between the first for loop in this function and
> this second one, from the loop variable point of view? Or, what is the
> value of "j" after the for loop, if all nets[j] slots are filled up?

Sorry, I explained this inaccurately; obviously if the array is not completely 
filled, the last element is going to be taken care of by virtue of the fact 
that j+1 would have had to be zero, thereby making the unconditional write 
after the loop basically a do-nothing operation. However, if it was completely 
filled, we *do* then need to zero it since net_end would be the reason for 
termination of loop iteration, leaving the old "copy" of the net element right 
at the end. So the question is, what is faster - reading and checking if we 
need to write the memory location before we do so, or just unconditionally 
writing it? Evidently in most cases the value is most likely going to be 0 
already, so is a mem write slower than a read and compare? Do we really care 
that much?

In any case, I do see scope for redoing the flow so that we just do everything 
we need under the very first for loop in the function, so I'll refactor it 
accordingly and mail it out. But, I would prefer not to be pontificating over 
syntactic sugar. Anything to do with errors I'll of course happily discuss 
endlessly, so if you believe one still exists, no problem.

My hope was to get a change in that fixes the issue with minimal reorganisation 
of the code flow so that anyone looking at it later on can see, relatively 
quickly, exactly what was done to resolve the issue - if you want to then 
clean up the semantics too, fine, throw that in as a separate commit - that 
also avoids undermining git bisect should the clean-up introduce its own 
errors, somehow.

Kind Regards,
Oliver.
--
To unsubscribe from this list: send the line "unsubscribe netfilter-devel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Jozsef Kadlecsik Sept. 13, 2013, 8:03 p.m. UTC | #5
On Thu, 12 Sep 2013, Oliver wrote:

> On Wednesday 11 September 2013 21:32:26 Jozsef Kadlecsik wrote:
> > On Wed, 11 Sep 2013, Oliver wrote:
> > > On Wednesday 11 September 2013 16:01:18 Jozsef Kadlecsik wrote:
> > > > On Wed, 11 Sep 2013, Oliver wrote:
> <snip>
> > > > 
> > > > If you restored the original code in the part above, then the whole
> > > > thing
> > > > could be collapsed into starting the next loop from j = i + 1 (and
> > > > changing the indices in the for body accordingly).
> > > 
> > > I'm not sure I follow; the whole problem is that the proceeding loop
> > > fails because the element at j gets set to zero, causing it to be
> > > skipped when we do our shift-up below - why would we want to bother with
> > > looping when we can just decrement and return immediately?
> > 
> > The for loop below would not be started due to the conditions: actually,
> > then conditions above ("i == net_end || !h->nets[i + 1].nets[n]") would be
> > "moved" here. In another words, would not be duplicated:
> > 
> > 	for (j = i + 1; j < nets_length - 1 && h->nets[j].nets[n]; j++)
> > 		h->nets[j - 1].cidr[n] = h->nets[j + 1].cidr[n];
> > 		..
> > 	}
> > 	if (j < nets_length)
> > 		h->nets[j - 1].nets[n] = 0;
> > 
> 
> I don't really like this idea; i + 1 after the second iteration will 
> point to whatever was shifted up, this obviously doesn't really present 
> a problem since if the element is empty, we won't iterate any more, but 
> I don't think it looks good for code clarity and it's just going to be a 
> non-changing value that the processor must pointlessly compare on every 
> iteration (first and second run notwithstanding).

I couldn't follow your reasoning above, maybe it's just too late here.

Anyway, I'd like to ask you two small modifications compared to v3 and 
that's all:

- Change the condition of the first "if" to an inequality, with a 
  continue statement in the if body. Thus the next lines can be shifted
  to the left, for better readability.
- Move the "i == net_end" test to the last position, because that is
  the less likely case.

Thanks! 

Best regards,
Jozsef
-
E-mail  : kadlec@blackhole.kfki.hu, kadlecsik.jozsef@wigner.mta.hu
PGP key : http://www.kfki.hu/~kadlec/pgp_public_key.txt
Address : Wigner Research Centre for Physics, Hungarian Academy of Sciences
          H-1525 Budapest 114, POB. 49, Hungary
--
To unsubscribe from this list: send the line "unsubscribe netfilter-devel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Oliver Smith Sept. 14, 2013, 8:38 a.m. UTC | #6
On Friday 13 September 2013 22:03:36 you wrote:
<snip>
> 
> I couldn't follow your reasoning above, maybe it's just too late here.
> 
> Anyway, I'd like to ask you two small modifications compared to v3 and
> that's all:
> 
> - Change the condition of the first "if" to an inequality, with a
>   continue statement in the if body. Thus the next lines can be shifted
>   to the left, for better readability.

OK, sounds good, I assume you mean v4, since a continue would not be valid in 
v3.

> - Move the "i == net_end" test to the last position, because that is
>   the less likely case.

This would create an error if we reached the very last possible iteration 
(i.e. *i* being 1 less than nets_length) - we need to check if it is equal to 
net_end (i.e. nets_length - 1) in the order it is right now so that we do not 
attempt a memory access beyond the boundary of the array (i.e. the final [i + 
1] check within the if() statement).

I'll land the new patch shortly.

Kind Regards,
Oliver.
--
To unsubscribe from this list: send the line "unsubscribe netfilter-devel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
diff mbox

Patch

diff --git a/kernel/net/netfilter/ipset/ip_set_hash_gen.h b/kernel/net/netfilter/ipset/ip_set_hash_gen.h
index 7a5b776..6599ea8 100644
--- a/kernel/net/netfilter/ipset/ip_set_hash_gen.h
+++ b/kernel/net/netfilter/ipset/ip_set_hash_gen.h
@@ -307,19 +307,20 @@  mtype_add_cidr(struct htype *h, u8 cidr, u8 nets_length, u8 n)
 static void
 mtype_del_cidr(struct htype *h, u8 cidr, u8 nets_length, u8 n)
 {
-	u8 i, j;
+	u8 i, j, net_end = nets_length - 1;
 
-	for (i = 0; i < nets_length - 1 && h->nets[i].cidr[n] != cidr; i++)
+	for (i = 0; i < net_end && h->nets[i].cidr[n] != cidr; i++)
 		;
-	h->nets[i].nets[n]--;
-
-	if (h->nets[i].nets[n] != 0)
+	if (h->nets[i].nets[n] > 1 || i == net_end || !h->nets[i + 1].nets[n]) {
+		h->nets[i].nets[n]--;
 		return;
+	}
 
-	for (j = i; j < nets_length - 1 && h->nets[j].nets[n]; j++) {
+	for (j = i; j < net_end && h->nets[j].nets[n]; j++) {
 		h->nets[j].cidr[n] = h->nets[j + 1].cidr[n];
 		h->nets[j].nets[n] = h->nets[j + 1].nets[n];
 	}
+	h->nets[j].nets[n] = 0;
 }
 #endif