cvs commit: src/sys/kern kern_proc.c

Poul-Henning Kamp phk at phk.freebsd.dk
Wed Jun 9 16:20:09 GMT 2004


In message <20040609.100413.118633043.imp at bsdimp.com>, "M. Warner Losh" writes:

>Can you provide a couple of lines about why BAD is BAD and why GOOD
>fixes that flaw?  That should help others from making this mistake in
>the future.

 	LOCK(foo->lock)
 	foo->refcount--;
 	UNLOCK(foo->lock)
 	if (foo->refcount == 0)
 		destroy(foo);

The problem is that there is the risk that another thread will modify
the refcount between our modification and our test:

Assume foo->refcount = 2;

	thread1 (low priority)	thread2 (high priority)
	----------------------	-----------------------

	...			...
	LOCK(foo->lock)		...
	foo->refcount--;	...
	# refcount now == 1	LOCK(foo->lock) 

At this point, thread2 sleeps, spins or whatever until it can get
the lock it wants.

	UNLOCK(foo->lock)	

Now thread2 is runnable and since it has a higher priority it will
be run:

				foo->refcount--;
				# refcount now == 0
				UNLOCK(foo->lock);
				if(foo->refount == 0)
					destroy(foo);
				...

At some point thread1 gets to continue:
	if (foo->refcount == 0) 
		destroy(foo);

But at this time foo may be gone or recycled and a panic is our best
hope and random memory corruption is our worst fear.

The way to fix this is to make sure that the test for zero-ness
is done on the result of our own decrement operation:

	LOCK(foo->lock)
	i = --foo->refcount;
	UNLOCK(foo->lock)
	if (i == 0)
		destroy(foo);

Assume foo->refcount = 2;

	thread1 (low priority)	thread2 (high priority)
	----------------------	-----------------------

	...			...
	LOCK(foo->lock)		...
	i = --foo->refcount;	LOCK(foo->lock)
	# i == 1, refcount == 1
	UNLOCK(foo->lock)
				i = --foo->refcount;
				# i == 0, refcount == 0
				UNLOCK(foo->lock)
				if (i == 0) # true
					destroy(foo)
				...

	if (i == 0) # false
		destroy(foo)	

I'm not very good at explaining this am I ?				

-- 
Poul-Henning Kamp       | UNIX since Zilog Zeus 3.20
phk at FreeBSD.ORG         | TCP/IP since RFC 956
FreeBSD committer       | BSD since 4.3-tahoe    
Never attribute to malice what can adequately be explained by incompetence.


More information about the cvs-src mailing list