aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavid Miller <davem@davemloft.net>2008-11-09 15:52:12 +0100
committerWilly Tarreau <w@1wt.eu>2008-11-09 19:34:45 +0100
commitc90df95b42f3d52eaf581060be576843cba99c3e (patch)
tree897c00d03c8770d89a5b9e32cf7c2303b27be507
parent596d7aa090254d003aa5b14bab99913870c4c44d (diff)
downloadlinux-2.4-c90df95b42f3d52eaf581060be576843cba99c3e.tar.gz
net: Fix recursive descent in __scm_destroy().
[backport of 2.6 commit f8d570a4745835f2238a33b537218a1bb03fc671] __scm_destroy() walks the list of file descriptors in the scm_fp_list pointed to by the scm_cookie argument. Those, in turn, can close sockets and invoke __scm_destroy() again. There is nothing which limits how deeply this can occur. The idea for how to fix this is from Linus. Basically, we do all of the fput()s at the top level by collecting all of the scm_fp_list objects hit by an fput(). Inside of the initial __scm_destroy() we keep running the list until it is empty. Signed-off-by: David S. Miller <davem@davemloft.net> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org> Signed-off-by: Willy Tarreau <w@1wt.eu> (cherry picked from commit 57812a4772fa73f264b59ae31c5cc277630b1ff5)
-rw-r--r--include/linux/sched.h2
-rw-r--r--include/net/scm.h5
-rw-r--r--net/core/scm.c24
3 files changed, 26 insertions, 5 deletions
diff --git a/include/linux/sched.h b/include/linux/sched.h
index 7716c40c0afdf2..aee6f563d03cd8 100644
--- a/include/linux/sched.h
+++ b/include/linux/sched.h
@@ -415,6 +415,8 @@ struct task_struct {
/* journalling filesystem info */
void *journal_info;
+
+ struct list_head *scm_work_list;
};
/*
diff --git a/include/net/scm.h b/include/net/scm.h
index e26b43f5eb934e..6246dd3694721d 100644
--- a/include/net/scm.h
+++ b/include/net/scm.h
@@ -8,8 +8,9 @@
struct scm_fp_list
{
- int count;
- struct file *fp[SCM_MAX_FD];
+ struct list_head list;
+ int count;
+ struct file *fp[SCM_MAX_FD];
};
struct scm_cookie
diff --git a/net/core/scm.c b/net/core/scm.c
index 96e2fe046c09f5..b7bdc363246009 100644
--- a/net/core/scm.c
+++ b/net/core/scm.c
@@ -70,6 +70,7 @@ static int scm_fp_copy(struct cmsghdr *cmsg, struct scm_fp_list **fplp)
if (!fpl)
return -ENOMEM;
*fplp = fpl;
+ INIT_LIST_HEAD(&fpl->list);
fpl->count = 0;
}
fpp = &fpl->fp[fpl->count];
@@ -101,9 +102,25 @@ void __scm_destroy(struct scm_cookie *scm)
if (fpl) {
scm->fp = NULL;
- for (i=fpl->count-1; i>=0; i--)
- fput(fpl->fp[i]);
- kfree(fpl);
+ if (current->scm_work_list) {
+ list_add_tail(&fpl->list, current->scm_work_list);
+ } else {
+ LIST_HEAD(work_list);
+
+ current->scm_work_list = &work_list;
+
+ list_add(&fpl->list, &work_list);
+ while (!list_empty(&work_list)) {
+ fpl = list_entry(work_list.next, struct scm_fp_list, list);
+
+ list_del(&fpl->list);
+ for (i=fpl->count-1; i>=0; i--)
+ fput(fpl->fp[i]);
+ kfree(fpl);
+ }
+
+ current->scm_work_list = NULL;
+ }
}
}
@@ -263,6 +280,7 @@ struct scm_fp_list *scm_fp_dup(struct scm_fp_list *fpl)
new_fpl = kmalloc(sizeof(*fpl), GFP_KERNEL);
if (new_fpl) {
+ INIT_LIST_HEAD(&new_fpl->list);
for (i=fpl->count-1; i>=0; i--)
get_file(fpl->fp[i]);
memcpy(new_fpl, fpl, sizeof(*fpl));