Skip to content

Commit

Permalink
net: Fix recursive descent in __scm_destroy().
Browse files Browse the repository at this point in the history
__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>
  • Loading branch information
David Miller authored and Linus Torvalds committed Nov 6, 2008
1 parent 75fa677 commit f8d570a
Show file tree
Hide file tree
Showing 3 changed files with 26 additions and 5 deletions.
2 changes: 2 additions & 0 deletions include/linux/sched.h
Original file line number Diff line number Diff line change
Expand Up @@ -1349,6 +1349,8 @@ struct task_struct {
*/
unsigned long timer_slack_ns;
unsigned long default_timer_slack_ns;

struct list_head *scm_work_list;
};

/*
Expand Down
5 changes: 3 additions & 2 deletions include/net/scm.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,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
Expand Down
24 changes: 21 additions & 3 deletions net/core/scm.c
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,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];
Expand Down Expand Up @@ -106,9 +107,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_first_entry(&work_list, 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;
}
}
}

Expand Down Expand Up @@ -284,6 +301,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));
Expand Down

0 comments on commit f8d570a

Please sign in to comment.