Hi Alan,
There seems to be a small memory leak related to allocating
neigh_sysctl_table structures. A structure is allocated for every call
to neigh_sysctl_register and returned as a void *, but in a couple of
places the returned value is discarded (in devinet.c and
ipv6/addrconf.c). There would then be no way to free the table.
I've corrected this by saving the structure in the neigh_parms
sysctl_table field, and have added a test to make sure the field isn't
already filled. When allocating a neigh_parms structure the sysctl_table
field is initialized to NULL.
The attached patch also corrects a race condition in
neigh_parms_release, where I think you want the start_bh_atomic
exclusion before walking the parms list.
If you could check the patch to see that everything looks reasonable,
I'll send it on to Linus.
Regards,
Bill
--------------A5377EC7E7B4B8B26E1528E1
Content-Type: text/plain; charset=us-ascii; name="neigh_79-patch"
Content-Transfer-Encoding: 7bit
Content-Disposition: inline; filename="neigh_79-patch"
--- net/core/neighbour.c.old Tue Jan 13 10:38:37 1998
+++ net/core/neighbour.c Wed Jan 14 17:47:15 1998
@@ -58,7 +58,7 @@
/*
* It is random distribution in the interval (1/2)*base...(3/2)*base.
* It corresponds to default IPv6 settings and is not overridable,
- * because it is really reasonbale choice.
+ * because it is really reasonable choice.
*/
unsigned long neigh_rand_reach_time(unsigned long base)
@@ -368,7 +368,7 @@
/*
Transitions NUD_STALE <-> NUD_REACHABLE do not occur
- when fast path is built: we have no timers assotiated with
+ when fast path is built: we have no timers associated with
these states, we do not have time to check state when sending.
neigh_periodic_timer check periodically neigh->confirmed
time and moves NUD_REACHABLE -> NUD_STALE.
@@ -845,6 +845,7 @@
p = kmalloc(sizeof(*p), GFP_KERNEL);
if (p) {
memcpy(p, &tbl->parms, sizeof(*p));
+ p->sysctl_table = NULL;
p->reachable_time = neigh_rand_reach_time(p->base_reachable_time);
start_bh_atomic();
p->next = tbl->parms.next;
@@ -860,9 +861,9 @@
if (parms == NULL || parms == &tbl->parms)
return;
+ start_bh_atomic();
for (p = &tbl->parms.next; *p; p = &(*p)->next) {
if (*p == parms) {
- start_bh_atomic();
*p = parms->next;
end_bh_atomic();
#ifdef CONFIG_SYSCTL
@@ -872,6 +873,7 @@
return;
}
}
+ end_bh_atomic();
NEIGH_PRINTK1("neigh_release_parms: not found\n");
}
@@ -926,7 +928,6 @@
#ifdef CONFIG_RTNETLINK
-
int neigh_delete(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
{
struct ndmsg *ndm = NLMSG_DATA(nlh);
@@ -1154,13 +1155,9 @@
NETLINK_CB(skb).dst_groups = RTMGRP_NEIGH;
netlink_broadcast(rtnl, skb, 0, RTMGRP_NEIGH, GFP_ATOMIC);
}
+#endif /* CONFIG_ARPD */
-
-
-#endif
-
-
-#endif
+#endif /* CONFIG_RTNETLINK */
#ifdef CONFIG_SYSCTL
@@ -1230,14 +1227,21 @@
{{CTL_NET, "net", NULL, 0, 0555, NULL},{0}}
};
+/*
+ * Allocate a neigh_sysctl_table structure and save it in neigh_parms.
+ */
void * neigh_sysctl_register(struct device *dev, struct neigh_parms *p,
int p_id, int pdev_id, char *p_name)
{
struct neigh_sysctl_table *t;
+ if (p->sysctl_table)
+ printk("neigh_sysctl_register: parms %p has table for %s!\n",
+ p, p_name);
+
t = kmalloc(sizeof(*t), GFP_KERNEL);
if (t == NULL)
- return NULL;
+ goto out;
memcpy(t, &neigh_sysctl_template, sizeof(*t));
t->neigh_vars[0].data = &p->mcast_probes;
t->neigh_vars[1].data = &p->ucast_probes;
@@ -1272,11 +1276,17 @@
t->neigh_root_dir[0].child = t->neigh_proto_dir;
t->sysctl_header = register_sysctl_table(t->neigh_root_dir, 0);
- if (t->sysctl_header == NULL) {
- kfree(t);
- return NULL;
- }
+ if (t->sysctl_header == NULL)
+ goto out_free;
+ p->sysctl_table = t;
+
+out:
return t;
+
+out_free:
+ kfree(t);
+ t = NULL;
+ goto out;
}
void neigh_sysctl_unregister(struct neigh_parms *p)
--------------A5377EC7E7B4B8B26E1528E1--