Re: PATCH against 2.4.2: TTY hangup on PPP channel corrupts kernel memory

2001-03-23 Thread Paul Fulghum

From: "Andrew Morton" <[EMAIL PROTECTED]>

> Your analysis is correct.  It's a bug.
> 
> Furthermore, n_hdlc_tty_open() (for example) can sleep prior to
> incrementing the module refcount, which means the module can be
> unloaded while it's running.  I cut a patch ages ago which fixes
> this one for both ttys and ldiscs.  I never got around to sending
> it to anyone.
> 
> > Does this mean that all line discipline implementations must use a
> > spinlock around critical code in "open", "close", and every other line
> > discipline function?  It looks like they must, and it looks like most
> > don't right now.

I have experienced essentially the same problem:
A line discipline can be switched while a user mode program is blocked
inside of a line discipline call.

In my case the call was ioctl() (select) which went through the ldisc
(n_hdlc) and was being serviced by (and blocked in) the tty layer. 

Two processes had the underlying serial device open. One process
restored the ldisc to N_TTY, exited, and the script that started
the process unloaded the ldisc driver (which had
a zero ref count as a result of being switched out).
When the select call of the other process tried to return
(to the n_hdlc ldisc), the code was already unloaded and an
oops occurred.

I was not too worried about this because it was caused by
a series of wrong (buggy) moves by the user mode processes.

But it goes back to the problem of allowing the ldisc to
change when there are existing calls blocked in (or through)
the ldisc. 

Paul Fulghum [EMAIL PROTECTED]
Microgate Corporation www.microgate.com


-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to [EMAIL PROTECTED]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/



Re: PATCH against 2.4.2: TTY hangup on PPP channel corrupts kernel memory

2001-03-23 Thread Andrew Morton

Kevin Buhr wrote:
> 
> ...
> When changing line disciplines, "sys_ioctl" gets the big kernel lock
> for us, and the "tty_set_ldisc" function doesn't get any additional
> locks.  It just calls the line discipline "open" function.
> 
> Suppose, at this point, the modem hangs up.  From a hardware
> interrupt, "tty_hangup" is called which schedule_tasks the tq_hangup
> routine, "do_tty_hangup".
> 
> Now, suppose the line discipline "open" function doesn't do any
> special locking and has a harmless-looking "kmalloc" that isn't
> GPF_ATOMIC.  It falls asleep and gives up the big kernel lock!!
> 
> Now, the eventd kernel thread wakes up and runs "do_tty_hangup".
> "do_tty_hangup" has no trouble getting the big kernel lock and running
> the "flush_buffer", "write_wakeup", and "close" line discipline
> function on the half-initialized line discipline all with no further
> locking.  In a naive implementation, "close" would start freeing the
> same kernel structures that "open" hasn't had a chance to allocate!
> And, now, "open" is free to wake up and try allocating structures for
> a line discipline that has already been shutdown from the TTY.

Your analysis is correct.  It's a bug.

Furthermore, n_hdlc_tty_open() (for example) can sleep prior to
incrementing the module refcount, which means the module can be
unloaded while it's running.  I cut a patch ages ago which fixes
this one for both ttys and ldiscs.  I never got around to sending
it to anyone.

> Does this mean that all line discipline implementations must use a
> spinlock around critical code in "open", "close", and every other line
> discipline function?  It looks like they must, and it looks like most
> don't right now.
> 

Seems a semaphore would be adequate.  Adding locking to the
tty code can be tricky, because it likes to copy big structures
around by value, rather than by reference.  You can end up
accidentally overwriting your locks.  I'm not sure why the
code was done this way.

Here's the old tty+ldisc module safety patch.  I've added the
ldisc locking.  Does it look to you like it'll fix the race
you identify?  Note that only one ldisc (ppp_async) and one
tty driver (serial) have been edited to actually use the new
module handling. It's trivial to change the other ldiscs and
tty drivers.

I think the scenario you describe could happen with the 
tty_struct.driver handling, as well as with tty_struct.ldisc.

This patch isn't ready to roll yet, BTW.  It needs a few hours
staring, thinking and testing.  Mainly checking whether all
scenarios are covered.  Hacking the tty layer is not 
exactly a walk in the park


--- linux-2.4.3-pre6/include/linux/tty_driver.h Tue Jan 30 18:24:56 2001
+++ linux-akpm/include/linux/tty_driver.h   Fri Mar 23 21:20:36 2001
@@ -117,6 +117,14 @@
 
 #include 
 
+#ifdef CONFIG_MODULES
+#include 
+#define SET_TTY_OWNER(driver)  \
+   do { (driver)->owner = THIS_MODULE; } while (0)
+#define SET_LDISC_OWNER(ldisc) \
+   do { (ldisc)->owner = THIS_MODULE; } while (0)
+#endif
+
 struct tty_driver {
int magic;  /* magic number for this structure */
const char  *driver_name;
@@ -176,6 +184,9 @@
 */
struct tty_driver *next;
struct tty_driver *prev;
+#ifdef CONFIG_MODULES
+   struct module *owner;
+#endif
 };
 
 /* tty driver magic number */
--- linux-2.4.3-pre6/include/linux/tty_ldisc.h  Tue Jan 30 18:24:56 2001
+++ linux-akpm/include/linux/tty_ldisc.hFri Mar 23 21:20:36 2001
@@ -100,6 +100,8 @@
 #include 
 #include 
 
+struct module;
+
 struct tty_ldisc {
int magic;
char*name;
@@ -129,6 +131,9 @@
   char *fp, int count);
int (*receive_room)(struct tty_struct *);
void(*write_wakeup)(struct tty_struct *);
+#ifdef CONFIG_MODULES
+   struct module *owner;
+#endif
 };
 
 #define TTY_LDISC_MAGIC0x5403
--- linux-2.4.3-pre6/include/linux/tty.hTue Jan 30 18:24:56 2001
+++ linux-akpm/include/linux/tty.h  Fri Mar 23 21:29:32 2001
@@ -306,6 +306,7 @@
unsigned int canon_column;
struct semaphore atomic_read;
struct semaphore atomic_write;
+   struct semaphore ldisc_sem; /* Lock this while we're manipulating ldisc */
spinlock_t read_lock;
 };
 
--- linux-2.4.3-pre6/drivers/char/tty_io.c  Sun Feb 25 17:37:04 2001
+++ linux-akpm/drivers/char/tty_io.cFri Mar 23 21:39:11 2001
@@ -182,6 +182,67 @@
 }
 
 /*
+ * Lock a driver's module into core and increment its low-level refcount.
+ * Return 0 on success.  If we return non-zero then the driver module isn't there
+ * any more and action needs to be taken by the caller
+ */
+static int tty_dev_hold(struct tty_driver *driver)
+{
+#ifdef CONFIG_MODULES
+   if (driver->owner) {
+   if (try_inc_mod_count(driver->owner) == 0)
+   return -ENODEV; /* Module is deleted */
+   }
+#endif
+   (*driver->refcount)++;
+   return 0;
+}
+
+/*
+

Re: PATCH against 2.4.2: TTY hangup on PPP channel corrupts kernel memory

2001-03-22 Thread Kevin Buhr

Paul Mackerras <[EMAIL PROTECTED]> writes:
> 
> I didn't realize you were talking about linux 2.4.0 and pppd 2.3.11.

That was my stupid oversight.  I carefully tested and retested the
patch under 2.4.0-test5, then ported it to 2.4.2 and sent it off with
only a cursory check using the new pppd [smack forehead here].

> > In particular, the comment above "ppp_asynctty_close" is misleading.
> > It's true that the TTY layer won't call any further line discipline
> > entries while the "close" is executing; however, there may be
> > processes already sleeping in line discipline functions called before
> > the hangup.  For example, "ppp_asynctty_close" could be called while
> > we sleep in the "get_user" in "ppp_channel_ioctl" (called from
> > "ppp_asynctty_ioctl").  Therefore, calling "PPPIOCATTACH" on an
> > unattached PPP-disciplined TTY could, in unlikely circumstances
> > (argument swapped out), lead to a crash.
> 
> Yuck.  I don't see that we can protect against this without having
> some sort of lock in the tty structure, though.  We can't protect the
> existence of the channel structure with a lock inside that structure.
> Ideally the necessary protection would be provided at the tty level.

Well, the closer I look at line discipline locking the less I think I
understand it.  I can't even see what prevents an "ldisc.close"
function from being called when an "ldisc.open" is sleeping on a
memory allocation.

Can someone help me understand?

When changing line disciplines, "sys_ioctl" gets the big kernel lock
for us, and the "tty_set_ldisc" function doesn't get any additional
locks.  It just calls the line discipline "open" function.

Suppose, at this point, the modem hangs up.  From a hardware
interrupt, "tty_hangup" is called which schedule_tasks the tq_hangup
routine, "do_tty_hangup".

Now, suppose the line discipline "open" function doesn't do any
special locking and has a harmless-looking "kmalloc" that isn't
GPF_ATOMIC.  It falls asleep and gives up the big kernel lock!!

Now, the eventd kernel thread wakes up and runs "do_tty_hangup".
"do_tty_hangup" has no trouble getting the big kernel lock and running
the "flush_buffer", "write_wakeup", and "close" line discipline
function on the half-initialized line discipline all with no further
locking.  In a naive implementation, "close" would start freeing the
same kernel structures that "open" hasn't had a chance to allocate!
And, now, "open" is free to wake up and try allocating structures for
a line discipline that has already been shutdown from the TTY.

Does this mean that all line discipline implementations must use a
spinlock around critical code in "open", "close", and every other line
discipline function?  It looks like they must, and it looks like most
don't right now.

Maybe I'm just overlooking something obvious.

> >Can we
> > eliminate "ppp_channel_ioctl" from "ppp_async.c" entirely, as in the
> > patch below?  We're requiring people to upgrade to "pppd" 2.4.0
> > anyway, and it has no need for these calls.  This would give me a warm,
> > fuzzy feeling.
> 
> Sure, that would be fine.  I'll make up a patch and send it to Linus.

Thank you.

Kevin <[EMAIL PROTECTED]>
-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to [EMAIL PROTECTED]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/



Re: PATCH against 2.4.2: TTY hangup on PPP channel corrupts kernel memory

2001-03-22 Thread Paul Mackerras

Kevin Buhr writes:

> I didn't realize my specific hang was a peculiarity of the older
> attachment style.  The channel created by pushing the PPP line

I didn't realize you were talking about linux 2.4.0 and pppd 2.3.11.

> discipline onto a TTY was connected to a unit with a PPPIOCATTACH
> ioctl on the TTY---this didn't really "attach" the channel; it still
> had a refcnt of only one.  Through the old compatibility interface, it
> was possible to call ppp_asynctty_read -> ppp_channel_read -> ppp_read
> on the channel's "struct ppp_file" and wait on the channel's "rwait".
> If the modem hung up, "do_tty_hangup" would call "ppp_asynctty_close"
> (with a reader still in "ppp_asynctty_read") and the "struct channel"
> would be freed in "ppp_unregister_channel".

That's one of the main reasons why I removed the compatibility
stuff. :)

> I think your analysis of how things presently are with 2.4.2 and a
> modern "pppd" is correct...
> 
> Since the new "pppd" uses an explicit PPPIOCATTCHAN / PPPIOCCONNECT
> sequence, the refcnt gets bumped to 2 and stays there while the
> channel is attached.  So, this specific hang isn't a problem anymore
> for "ppp_async.c".  It's still a problem with "ppp_synctty.c", though
> (when used with "pppd" 2.3.11, say).  Is the compatibility stuff in
> there slated for removal, too?

Yep, and we should take out the stuff in ppp_generic.c that was called
by the compatibility stuff in the channels, too.

> In particular, the comment above "ppp_asynctty_close" is misleading.
> It's true that the TTY layer won't call any further line discipline
> entries while the "close" is executing; however, there may be
> processes already sleeping in line discipline functions called before
> the hangup.  For example, "ppp_asynctty_close" could be called while
> we sleep in the "get_user" in "ppp_channel_ioctl" (called from
> "ppp_asynctty_ioctl").  Therefore, calling "PPPIOCATTACH" on an
> unattached PPP-disciplined TTY could, in unlikely circumstances
> (argument swapped out), lead to a crash.

Yuck.  I don't see that we can protect against this without having
some sort of lock in the tty structure, though.  We can't protect the
existence of the channel structure with a lock inside that structure.
Ideally the necessary protection would be provided at the tty level.

> I assume PPPIOCATTACH (on the TTY) is deprecated in favor of
> PPPIOCATTCHAN / PPPIOCCONNECT (on the "/dev/ppp" handle).  Can we
> eliminate "ppp_channel_ioctl" from "ppp_async.c" entirely, as in the
> patch below?  We're requiring people to upgrade to "pppd" 2.4.0
> anyway, and it has no need for these calls.  This would give me a warm,
> fuzzy feeling.

Sure, that would be fine.  I'll make up a patch and send it to Linus.

Paul.
-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to [EMAIL PROTECTED]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/



Re: PATCH against 2.4.2: TTY hangup on PPP channel corrupts kernel memory

2001-03-17 Thread Kevin Buhr

Paul Mackerras <[EMAIL PROTECTED]> writes:
> 
> But the waiting process must have had an instance of /dev/ppp open and
> attached to the channel in order to be doing anything with rwait,
> within either ppp_file_read or ppp_poll.  The process of attaching to
> the channel increases its refcnt, meaning that the channel shouldn't
> be destroyed until the instance of /dev/ppp is closed and ppp_release
> is called.

Ugh...  I duplicated the hangs and verified my patch fixed them under
kernel 2.4.0 with "pppd" 2.3.11; and I also verified that kernel 2.4.2
with my patch and "pppd" 2.4.0f correctly deferred channel destruction
on modem hangup.  However, I forget to double-check that the hangs
were actually still possible with 2.4.2 and "pppd" 2.4.0f.

I didn't realize my specific hang was a peculiarity of the older
attachment style.  The channel created by pushing the PPP line
discipline onto a TTY was connected to a unit with a PPPIOCATTACH
ioctl on the TTY---this didn't really "attach" the channel; it still
had a refcnt of only one.  Through the old compatibility interface, it
was possible to call ppp_asynctty_read -> ppp_channel_read -> ppp_read
on the channel's "struct ppp_file" and wait on the channel's "rwait".
If the modem hung up, "do_tty_hangup" would call "ppp_asynctty_close"
(with a reader still in "ppp_asynctty_read") and the "struct channel"
would be freed in "ppp_unregister_channel".

I think your analysis of how things presently are with 2.4.2 and a
modern "pppd" is correct...

Since the new "pppd" uses an explicit PPPIOCATTCHAN / PPPIOCCONNECT
sequence, the refcnt gets bumped to 2 and stays there while the
channel is attached.  So, this specific hang isn't a problem anymore
for "ppp_async.c".  It's still a problem with "ppp_synctty.c", though
(when used with "pppd" 2.3.11, say).  Is the compatibility stuff in
there slated for removal, too?

> I presume that the generic file descriptor code ensures that the file
> release function doesn't get called while any task is inside the read
> or write function for that file, or while the file descriptor is in
> use in a select or poll.

It's not the file "release" function that's the problem.  It's the
line discipline's "close" call.  On a real hardware hangup, The kernel
thread "eventd" calls "do_tty_hangup" which grabs the big kernel lock
and calls ppp_asynctty_close.  I believe any line discipline or
"/dev/ppp" file function that sleeps (and so gives up its big kernel
lock) with refcnt == 1 is susceptible to having the "struct channel"
pulled out from under it.

In particular, the comment above "ppp_asynctty_close" is misleading.
It's true that the TTY layer won't call any further line discipline
entries while the "close" is executing; however, there may be
processes already sleeping in line discipline functions called before
the hangup.  For example, "ppp_asynctty_close" could be called while
we sleep in the "get_user" in "ppp_channel_ioctl" (called from
"ppp_asynctty_ioctl").  Therefore, calling "PPPIOCATTACH" on an
unattached PPP-disciplined TTY could, in unlikely circumstances
(argument swapped out), lead to a crash.

In fact, I think the only remaining PPP locking problem in
"ppp_async.c" still in 2.4.2 has to do with those PPPIOCATTACH/DETACH
ioctls for the TTY.  They seem to be the only way someone can
reference the "struct channel" of an unattached channel.

I assume PPPIOCATTACH (on the TTY) is deprecated in favor of
PPPIOCATTCHAN / PPPIOCCONNECT (on the "/dev/ppp" handle).  Can we
eliminate "ppp_channel_ioctl" from "ppp_async.c" entirely, as in the
patch below?  We're requiring people to upgrade to "pppd" 2.4.0
anyway, and it has no need for these calls.  This would give me a warm,
fuzzy feeling.

Kevin <[EMAIL PROTECTED]>

*   *   *

--- linux-2.4.2/drivers/net/ppp_async.c Sun Mar  4 20:09:19 2001
+++ linux-2.4.2-local/drivers/net/ppp_async.c   Sat Mar 17 20:11:45 2001
@@ -244,11 +244,6 @@
err = 0;
break;
 
-   case PPPIOCATTACH:
-   case PPPIOCDETACH:
-   err = ppp_channel_ioctl(&ap->chan, cmd, arg);
-   break;
-
default:
err = -ENOIOCTLCMD;
}
-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to [EMAIL PROTECTED]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/



Re: PATCH against 2.4.2: TTY hangup on PPP channel corrupts kernel memory

2001-03-17 Thread Paul Mackerras

Kevin Buhr writes:

> If there's a hangup in the TTY layer on an async PPP channel,
> do_tty_hangup shuts down the PPP line discipline, and, in ppp_async.c,
> the function ppp_asynctty_close unregisteres the channel.  In
> ppp_generic.c, ppp_unregister_channel merrily wakes up the rwait
> queue, then proceeds to destroy the channel, freeing the "struct
> channel" which contains the "struct ppp_file" that contains the
> "wait_queue_head_t rwait".  When the waiting process wakes up, it
> removes itself from the wait queue, modifying freed memory.

But the waiting process must have had an instance of /dev/ppp open and
attached to the channel in order to be doing anything with rwait,
within either ppp_file_read or ppp_poll.  The process of attaching to
the channel increases its refcnt, meaning that the channel shouldn't
be destroyed until the instance of /dev/ppp is closed and ppp_release
is called.

Note that pppd will not be blocking inside ppp_file_read since it sets
the file descriptor non-blocking.  Most of the time pppd would be
inside a select, so rwait would be in use by the poll/select code.

I presume that the generic file descriptor code ensures that the file
release function doesn't get called while any task is inside the read
or write function for that file, or while the file descriptor is in
use in a select or poll.  If that assumption is wrong then it would
indeed be possible for the channel to be destroyed while some process
is waiting on rwait.  But in any case it shouldn't be a problem in
practice since it would only be pppd that would have the channel open
and pppd is single-threaded, i.e. it couldn't be closing the file
descriptor while it is blocked inside read or select.

So, to put it in other words, this is the sequence (simplified):

fd = open("/dev/ppp", O_RDWR);
ioctl(fd, PPPIOCATTCHAN, &channel_number);
fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK);

select(...);/* fd_sets including fd */
read(fd, ...);
...
close(fd);

I believe the channel structure is guaranteed to exist from the ioctl
to the close, and all the selects and reads (i.e. all the uses of
rwait) have to happen within that time interval.

> A patch against 2.4.2 follows.  I've overloaded the "refcnt" in
> "struct ppp_file" to also keep track of rwaiters.  The last refcnt
> user destroys the channel and decreases the module use count.  I've
> tested this with printks in all the right places, and it seems to fix
> the problem correctly.

I'm not sure this is the right fix, this sounds to me like the
refcounts are going awry somehow or there is an SMP race that I
haven't considered, and I am concerned that this patch will just cover
over the real problem.  Actually, given that you've seen it 4 times in
6 months it's more likely that it is an SMP race IMHO.

In any case I don't think your patch does the right thing with
ppp_poll, because poll_wait doesn't actually wait, it just adds rwait
to a list of things to watch for wakeups.  In other words, rwait will
be in use from the time poll_wait is called until the time that the
poll/select logic (in fs/select.c) decides that it's time to return to
the user.  So increasing the refcount around just the poll_wait call
won't help much.

Do you have a way to reproduce the problem at will?  Have you seen it
happen on a UP box (i.e. could it be an SMP race)?  How sure are you
that your patch really fixes the problem?

Regards,
Paul.

-- 
Paul Mackerras, Open Source Research Fellow, Linuxcare, Inc.
+61 2 6262 8990 tel, +61 2 6262 8991 fax
[EMAIL PROTECTED], http://www.linuxcare.com.au/
Linuxcare.  Putting Open Source to work.
-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to [EMAIL PROTECTED]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/



PATCH against 2.4.2: TTY hangup on PPP channel corrupts kernel memory

2001-03-16 Thread Kevin Buhr

Linus:

This one was tough to find.  I think I triggered it maybe four times
over the space of six months and almost chalked it up to an electrical
problem with the modem.

If there's a hangup in the TTY layer on an async PPP channel,
do_tty_hangup shuts down the PPP line discipline, and, in ppp_async.c,
the function ppp_asynctty_close unregisteres the channel.  In
ppp_generic.c, ppp_unregister_channel merrily wakes up the rwait
queue, then proceeds to destroy the channel, freeing the "struct
channel" which contains the "struct ppp_file" that contains the
"wait_queue_head_t rwait".  When the waiting process wakes up, it
removes itself from the wait queue, modifying freed memory.

(In my case, it turns out that the "struct channel" in ppp_generic.c
and "struct shmid_kernel" are both in the size-128 slab cache.  My
desktop pager uses the X SHM extension to snapshot the desktop and
does many SHM allocations per second.  In the occasional case that the
allocation beats the rwaiting process, a modem hangup sends the X
server spinning on a random piece of memory way down in
"valid_swaphandles", and the other CPU locks up in some unrelated
place waiting on the kernel lock.)

A patch against 2.4.2 follows.  I've overloaded the "refcnt" in
"struct ppp_file" to also keep track of rwaiters.  The last refcnt
user destroys the channel and decreases the module use count.  I've
tested this with printks in all the right places, and it seems to fix
the problem correctly.

I don't use any other PPP drivers besides async, but it looks like the
same bug exists (and will be fixed by this patch) in ppp_synctty.c.

The FILEVERSION at the top of ppp_generic may need to be fixed, but 

Thanks.

Kevin <[EMAIL PROTECTED]>

*   *   *

--- linux-2.4.2/drivers/net/ppp_generic.c   Sun Mar  4 20:09:19 2001
+++ linux-2.4.2-local/drivers/net/ppp_generic.c Fri Mar 16 14:50:28 2001
@@ -72,7 +72,7 @@
struct sk_buff_head xq; /* pppd transmit queue */
struct sk_buff_head rq; /* receive queue for pppd */
wait_queue_head_t rwait;/* for poll on reading /dev/ppp */
-   atomic_trefcnt; /* # refs (incl /dev/ppp attached) */
+   atomic_trefcnt; /* # refs (attaches and rwaiters) */
int hdrlen; /* space to leave for headers */
struct list_head list;  /* link in all_* list */
int index;  /* interface unit / channel number */
@@ -316,6 +316,28 @@
return 0;
 }
 
+/*
+ * For ppp_async, a (true) hangup in the TTY layer will shut down the
+ * current line discipline, unregistering the channel, so...
+ *
+ * We must ppp_file_acquire before waiting on rwait to keep the
+ * channel around, then ppp_file_release after waiting to release
+ * resources if we're the last user.
+ */
+#define ppp_file_acquire(pf) atomic_inc(&(pf)->refcnt)
+static void ppp_file_release(struct ppp_file *pf) {
+   if (atomic_dec_and_test(&pf->refcnt)) {
+   switch (pf->kind) {
+   case INTERFACE:
+   ppp_destroy_interface(PF_TO_PPP(pf));
+   break;
+   case CHANNEL:
+   ppp_destroy_channel(PF_TO_CHANNEL(pf));
+   break;
+   }
+   }
+}
+
 static int ppp_release(struct inode *inode, struct file *file)
 {
struct ppp_file *pf = (struct ppp_file *) file->private_data;
@@ -323,16 +345,7 @@
lock_kernel();
if (pf != 0) {
file->private_data = 0;
-   if (atomic_dec_and_test(&pf->refcnt)) {
-   switch (pf->kind) {
-   case INTERFACE:
-   ppp_destroy_interface(PF_TO_PPP(pf));
-   break;
-   case CHANNEL:
-   ppp_destroy_channel(PF_TO_CHANNEL(pf));
-   break;
-   }
-   }
+   ppp_file_release(pf);
}
unlock_kernel();
return 0;
@@ -357,6 +370,7 @@
if (pf == 0)
goto out;   /* not currently attached */
 
+   ppp_file_acquire(pf);
add_wait_queue(&pf->rwait, &wait);
current->state = TASK_INTERRUPTIBLE;
for (;;) {
@@ -376,6 +390,7 @@
}
current->state = TASK_RUNNING;
remove_wait_queue(&pf->rwait, &wait);
+   ppp_file_release(pf);
 
if (skb == 0)
goto out;
@@ -448,6 +463,7 @@
 
if (pf == 0)
return 0;
+   ppp_file_acquire(pf);
poll_wait(file, &pf->rwait, wait);
mask = POLLOUT | POLLWRNORM;
if (skb_peek(&pf->rq) != 0)
@@ -457,6 +473,7 @@
if (pch->chan == 0)
mask |= POLLHUP;
}
+   ppp_file_release(pf);
return mask;
 }
 
@@ -1788,9 +1805,12 @@
spin_lock_bh(&all_