Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

net: Fix recursive descent in __scm_destroy().

__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>

authored by

David Miller and committed by
Linus Torvalds
f8d570a4 75fa6770

+26 -5
+2
include/linux/sched.h
··· 1349 1349 */ 1350 1350 unsigned long timer_slack_ns; 1351 1351 unsigned long default_timer_slack_ns; 1352 + 1353 + struct list_head *scm_work_list; 1352 1354 }; 1353 1355 1354 1356 /*
+3 -2
include/net/scm.h
··· 14 14 15 15 struct scm_fp_list 16 16 { 17 - int count; 18 - struct file *fp[SCM_MAX_FD]; 17 + struct list_head list; 18 + int count; 19 + struct file *fp[SCM_MAX_FD]; 19 20 }; 20 21 21 22 struct scm_cookie
+21 -3
net/core/scm.c
··· 75 75 if (!fpl) 76 76 return -ENOMEM; 77 77 *fplp = fpl; 78 + INIT_LIST_HEAD(&fpl->list); 78 79 fpl->count = 0; 79 80 } 80 81 fpp = &fpl->fp[fpl->count]; ··· 107 106 108 107 if (fpl) { 109 108 scm->fp = NULL; 110 - for (i=fpl->count-1; i>=0; i--) 111 - fput(fpl->fp[i]); 112 - kfree(fpl); 109 + if (current->scm_work_list) { 110 + list_add_tail(&fpl->list, current->scm_work_list); 111 + } else { 112 + LIST_HEAD(work_list); 113 + 114 + current->scm_work_list = &work_list; 115 + 116 + list_add(&fpl->list, &work_list); 117 + while (!list_empty(&work_list)) { 118 + fpl = list_first_entry(&work_list, struct scm_fp_list, list); 119 + 120 + list_del(&fpl->list); 121 + for (i=fpl->count-1; i>=0; i--) 122 + fput(fpl->fp[i]); 123 + kfree(fpl); 124 + } 125 + 126 + current->scm_work_list = NULL; 127 + } 113 128 } 114 129 } 115 130 ··· 301 284 302 285 new_fpl = kmalloc(sizeof(*fpl), GFP_KERNEL); 303 286 if (new_fpl) { 287 + INIT_LIST_HEAD(&new_fpl->list); 304 288 for (i=fpl->count-1; i>=0; i--) 305 289 get_file(fpl->fp[i]); 306 290 memcpy(new_fpl, fpl, sizeof(*fpl));