Skip site navigation (1)Skip section navigation (2)
Date:      Wed, 18 Apr 2001 22:07:25 +0000 (GMT)
From:      Terry Lambert <tlambert@primenet.com>
To:        arch@freebsd.org
Subject:   Found BAD BUG: squashed
Message-ID:  <200104182207.PAA11716@usr08.primenet.com>

Next in thread | Raw E-Mail | Index | Archive | Help
I have identified the bug.

It turns out that the bug causes memory corruption by freeing
but continuing to use a credential, and it only occurs in big
resource usage cases, and then seemingly at random.


It is the fact that there are two credentials per socket, one
for the socket, and one for the descriptor itself.

The cr_ref is an unsigned short, and ...

There is no test for overflow.

Thus if I open 65537 files, and close 2, my cred structure is
freed.

If invariants are on, "0xdeadc0de" is spammed over top of the
reference count, and then each time the cread is released, it
is decremented: "0xdeadc0dd", "0xdeadc0dc", "0xdeadc0db", ...

...until another "0xc0de" (49374) closes, at which point it
frees the already freed credential _again_.


The way FreeBSD code is currently structured, this means that
the quick-and-ugly fix is simply to change this from u_short
to u_long.  Further, I moved the cr_ref below the cr_groups
declartion -- well out of stomping range of the INVARIANTS
code.  I have successfully opened 60,000 socket connections
(240,000 credential references between the client and server
processes combined, and 120,000 each) with this change.

--

The real fix is to do as I suggested on -arch earlier today
(and then some), and change:

	crhold(curproc->p_ucred);
	sigio->sio_ucred = curproc->p_ucred;

style things into:

	sigio->sio_ucred = crhold(curproc->p_ucred);

style things.

But that is only part of it.  Additionally, it needs to be
further changed to something like:

	sigio->sio_ucred = crhold(&curproc->p_ucred);

-- passing the address of the credential pointer -- and then
(pseudocode), the credential code in crhold() needs to check
for an imminent overflow, and when it sees it:

	struct ucred *
	crhold(struct ucred **cpp)
	{
		struct ucred *ret;

		if( about_to_overflow) {
			ret = *cpp;
			*cpp = crdup(save);
		} else {
			ret = *cpp;
			ret ->cr_ref++;
		}

		return ret;
	}

The point of doing this would be to give the FD the overflowing
cred, so that new files opened by the process can start counting
at 1 again.

NOTE:	I also suggest chagning free() in kern/kern_malloc.c in
free() in the INVARIANTS case, from:

---
#ifdef INVARIANTS
	/*
	 * Check for multiple frees. Use a quick check to see if
	 * it looks free before laboriously searching the freelist.
	 */
	if (freep->spare0 == WEIRD_ADDR) {
		fp = (struct freelist *)kbp->kb_next;
		while (fp) {
			if (fp->spare0 != WEIRD_ADDR)
				panic("free: free item %p modified", fp);
			else if (addr == (caddr_t)fp)
				panic("free: multiple freed item %p", addr);
			fp = (struct freelist *)fp->next;
		} 
	}
---
#ifdef INVARIANTS
	/*
	 * Check for multiple frees. We can't use a quick check, since
	 * that will give us a false negative for something like a
	* stomped reference counter or mutex, if it is the first item.
	 */
	fp = (struct freelist *)kbp->kb_next;
	while (fp) {
		if (fp->spare0 != WEIRD_ADDR)
			panic("free: free item %p modified", fp);
		else if (addr == (caddr_t)fp)
			panic("free: multiple freed item %p", addr);
		fp = (struct freelist *)fp->next;
	} 
---


					Terry Lambert
					terry@lambert.org
---
Any opinions in this posting are my own and not those of my present
or previous employers.

To Unsubscribe: send mail to majordomo@FreeBSD.org
with "unsubscribe freebsd-arch" in the body of the message




Want to link to this message? Use this URL: <http://docs.FreeBSD.org/cgi/mid.cgi?200104182207.PAA11716>