From owner-freebsd-net@FreeBSD.ORG Mon Jun 30 20:14:17 2003 Return-Path: Delivered-To: freebsd-net@freebsd.org Received: from mx1.FreeBSD.org (mx1.freebsd.org [216.136.204.125]) by hub.freebsd.org (Postfix) with ESMTP id B250B37B401 for ; Mon, 30 Jun 2003 20:14:17 -0700 (PDT) Received: from mail.sandvine.com (sandvine.com [199.243.201.138]) by mx1.FreeBSD.org (Postfix) with ESMTP id F288043FBD for ; Mon, 30 Jun 2003 20:14:15 -0700 (PDT) (envelope-from don@sandvine.com) Received: by mail.sandvine.com with Internet Mail Service (5.5.2653.19) id ; Mon, 30 Jun 2003 23:14:03 -0400 Message-ID: From: Don Bowman To: "'freebsd-net@freebsd.org'" Date: Mon, 30 Jun 2003 23:13:57 -0400 MIME-Version: 1.0 X-Mailer: Internet Mail Service (5.5.2653.19) Content-Type: text/plain; charset="iso-8859-1" Subject: RE: using memory after freed in tcp_syncache (syncache_timer()) w ith ipfw: patch attached X-BeenThere: freebsd-net@freebsd.org X-Mailman-Version: 2.1.1 Precedence: list List-Id: Networking and TCP/IP with FreeBSD List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Tue, 01 Jul 2003 03:14:18 -0000 Synopsis: under some ipfw conditions, tcp_syncache has syncache_respond() call ip_output call ip_input call syncache_drop(), which drops the 'syncache' that is being worked on, or corrupts the list, etc. This is typically seen from syncache_timer or syncache_add. I've attached a patch that I believe corrects this problem. I'm observing it on 4.7, but I believe it equally affects RELENG_4 and CURRENT. This seems to make the problem I was seeing go away. I'm currently running with 2K syn/second through the original condition, will let it go overnight like that. I think that will flush out if i've introduced a leak or other crash. Can someone who knows this code perhaps critique what I've done? Essentially I have made syncache_drop() instead defer the delete onto a different list. In the timer, I delete the syncache entries from the delete list. This costs some performance and memory, but was the best way I could come up with. --don Index: tcp_syncache.c =================================================================== RCS file: /usr/cvs/src/sys/netinet/tcp_syncache.c,v retrieving revision 1.5.2.8.1000.3 diff -U3 -r1.5.2.8.1000.3 tcp_syncache.c --- tcp_syncache.c 4 Feb 2003 01:52:03 -0000 1.5.2.8.1000.3 +++ tcp_syncache.c 1 Jul 2003 03:05:22 -0000 @@ -85,6 +85,7 @@ #include #include +static int syncache_delete; static int tcp_syncookies = 1; SYSCTL_INT(_net_inet_tcp, OID_AUTO, syncookies, CTLFLAG_RW, &tcp_syncookies, 0, @@ -127,6 +128,7 @@ struct callout tt_timerq[SYNCACHE_MAXREXMTS + 1]; }; static struct tcp_syncache tcp_syncache; +static TAILQ_HEAD(syncache_delete_list, syncache) sc_delete_list; SYSCTL_NODE(_net_inet_tcp, OID_AUTO, syncache, CTLFLAG_RW, 0, "TCP SYN cache"); @@ -204,6 +206,9 @@ rt->rt_flags, NULL); RTFREE(rt); } +#if defined(DIAGNOSTIC) + memset(sc, 0xee, sizeof(struct syncache)); +#endif zfree(tcp_syncache.zone, sc); } @@ -258,6 +263,8 @@ tcp_syncache.cache_limit -= 1; tcp_syncache.zone = zinit("syncache", sizeof(struct syncache), tcp_syncache.cache_limit, ZONE_INTERRUPT, 0); + + TAILQ_INIT(&sc_delete_list); } static void @@ -331,6 +338,18 @@ s = splnet(); + if ((sc->sc_flags & SCF_DELETE) == 0) { + sc->sc_flags |= SCF_DELETE; + syncache_delete = 1; + TAILQ_INSERT_TAIL(&sc_delete_list, sc, sc_delete); + + splx(s); + return; + } + if (sc->sc_delete.tqe_next || sc->sc_delete.tqe_prev) { + TAILQ_REMOVE(&sc_delete_list, sc, sc_delete); + } + TAILQ_REMOVE(&sch->sch_bucket, sc, sc_hash); sch->sch_length--; tcp_syncache.cache_count--; @@ -359,6 +378,8 @@ s = splnet(); if (callout_pending(&tcp_syncache.tt_timerq[slot]) || !callout_active(&tcp_syncache.tt_timerq[slot])) { + if (syncache_delete) + goto delete_cleanup; splx(s); return; } @@ -392,6 +413,17 @@ if (nsc != NULL) callout_reset(&tcp_syncache.tt_timerq[slot], nsc->sc_rxttime - ticks, syncache_timer, (void *)(slot)); + +delete_cleanup: + sc = TAILQ_FIRST(&sc_delete_list); + while (sc != NULL) { + nsc = TAILQ_NEXT(sc, sc_delete); + syncache_drop(sc, NULL); + sc = nsc; + } + TAILQ_INIT(&sc_delete_list); + syncache_delete = 0; + splx(s); } @@ -1335,6 +1367,7 @@ sc = zalloc(tcp_syncache.zone); if (sc == NULL) return (NULL); + bzero(sc, sizeof(*sc)); /* * Fill in the syncache values. * XXX duplicate code from syncache_add Index: tcp_var.h =================================================================== RCS file: /usr/cvs/src/sys/netinet/tcp_var.h,v retrieving revision 1.56.2.12 diff -U3 -r1.56.2.12 tcp_var.h --- tcp_var.h 24 Aug 2002 18:40:26 -0000 1.56.2.12 +++ tcp_var.h 1 Jul 2003 02:33:57 -0000 @@ -224,8 +224,10 @@ #define SCF_CC 0x08 /* negotiated CC */ #define SCF_UNREACH 0x10 /* icmp unreachable received */ #define SCF_KEEPROUTE 0x20 /* keep cloned route */ +#define SCF_DELETE 0x40 /* I'm being deleted */ TAILQ_ENTRY(syncache) sc_hash; TAILQ_ENTRY(syncache) sc_timerq; + TAILQ_ENTRY(syncache) sc_delete; }; struct syncache_head {