Subject: kern/28683: NULL deref in ipw(4)
To: None <kern-bug-people@netbsd.org, gnats-admin@netbsd.org,>
From: None <heas@roome.shrubbery.net>
List: netbsd-bugs
Date: 12/16/2004 19:51:00
>Number:         28683
>Category:       kern
>Synopsis:       NULL deref in ipw(4)
>Confidential:   no
>Severity:       serious
>Priority:       low
>Responsible:    kern-bug-people
>State:          open
>Class:          sw-bug
>Submitter-Id:   net
>Arrival-Date:   Thu Dec 16 19:51:00 +0000 2004
>Originator:     heas
>Release:        NetBSD 2.99.10
>Organization:
	
>Environment:
	
	
System: NetBSD roome 2.99.10 NetBSD 2.99.10 (roome) #2: Thu Dec 16 18:32:11 UTC 2004 root@roome:/sys/arch/i386/compile/roome i386
Architecture: i386
Machine: i386
>Description:
Frequent traps in ipw(4) due to NULL deref in ipw_tx_start().

This is on an ibm t41p w/ a 2100 3B mini-pci (no WEP) running -current of ~12/4.
>How-To-Repeat:
Boot, login from a remote machine, run dmesg.  About 99% reliable.
>Fix:
It seems that the driver runs out of ipw_soft_hdrs from sc_free_shdr.  It uses
the head of the sc_free_shdr & sc_free_sbuf lists without checking their values
in ipw_tx_start().

ipw_tx_start @0x41 (c2159038, c24b7e00, c210d500
(gdb) list *(ipw_tx_start+0x41)
0xc02bade5 is in ipw_tx_start (../../../../dev/pci/if_ipw.c:722).
717             wh = mtod(m, struct ieee80211_frame *);
718
719             shdr = TAILQ_FIRST(&sc->sc_free_shdr);
720             sbuf = TAILQ_FIRST(&sc->sc_free_sbuf);
721
722             shdr->hdr.type = htole32(IPW_HDR_TYPE_SEND);
723             shdr->hdr.subtype = htole32(0);
724             shdr->hdr.encrypted = (wh->i_fc[1] & IEEE80211_FC1_WEP) ? 1 : 0;
725             shdr->hdr.encrypt = 0;
726             shdr->hdr.keyidx = 0; 

This works around the problem for me:
Index: if_ipw.c
===================================================================
RCS file: /cvsroot/src/sys/dev/pci/if_ipw.c,v
retrieving revision 1.8
diff -d -u -r1.8 if_ipw.c
--- if_ipw.c	14 Sep 2004 00:38:37 -0000	1.8
+++ if_ipw.c	16 Dec 2004 19:36:17 -0000
@@ -719,6 +719,12 @@
 	shdr = TAILQ_FIRST(&sc->sc_free_shdr);
 	sbuf = TAILQ_FIRST(&sc->sc_free_sbuf);
 
+	if (!shdr || !sbuf) {
+		aprint_debug("%s: no free soft hdrs/bufs\n",
+			     sc->sc_dev.dv_xname);
+		m_freem(m);
+		return 1;
+	}
 	shdr->hdr.type = htole32(IPW_HDR_TYPE_SEND);
 	shdr->hdr.subtype = htole32(0);
 	shdr->hdr.encrypted = (wh->i_fc[1] & IEEE80211_FC1_WEP) ? 1 : 0;

I'm not sure how it depletes pool of shdrs since they appear to only be used
for tx and for every shdr there is a sbuf, which would fill the sc->stdb_list.
There are half as many shdrs and sbuf as stdb_list entries.
	#define IPW_NDATA       (IPW_NTBD / 2)

However, there does not appear to be any checking for whether the stdb_list
entries are in use within ipw_cmd() or ipw_tx_start().  so, if the list wraps,
it could overwrite and entry that is in use.  That appears as if it could
also leak mbufs.

With the hack above, moderate abuse produces the log message several hundred
times within a few minutes.

>Unformatted: