memory leak in net/core/neighbour.c

Bill Hawes (whawes@star.net)
Wed, 14 Jan 1998 17:11:15 -0500


This is a multi-part message in MIME format.
--------------A5377EC7E7B4B8B26E1528E1
Content-Type: text/plain; charset=us-ascii
Content-Transfer-Encoding: 7bit

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--