2010-11-07 15 views
11

En el ejemplo dado a continuación, intento establecer el tamaño de la pila en 1kb.Establezca el tamaño de la pila con setrlimit() y provoque un desbordamiento de la pila/segfault

¿Por qué ahora es posible asignar una matriz de entradas en la pila con el tamaño 8kb en foo()?

#include <stdio.h> 
#include <sys/resource.h> 

void foo(void); 

int main() { 
struct rlimit lim = {1024, 1024}; 

if (setrlimit(RLIMIT_STACK, &lim) == -1) 
    return 1; 

foo(); 

return 0; 
} 

void foo() { 
unsigned ints[2048]; 

printf("foo: %u\n", ints[2047]=42); 
} 
+0

Gracias, ahora soy adicto a averiguar por qué esto no funciona como se anuncia en el hombre (2) setrlimit. Afortunadamente, gcc le permite especificar el tamaño de la pila :) –

+0

Una pregunta preferida más a menudo de lo que fue actualizada, en este momento. Interesante. –

Respuesta

4

El límite se establece de inmediato, pero solo se comprueba cuando se intenta asignar una nueva pila o intentar aumentar la pila existente. Un grep para RLIMIT_STACK (or a LXR identifier search) en las fuentes del kernel debería indicarlo.

Al parecer, el tamaño inicial de la pila es lo que se necesita para las cadenas de nombre de archivo + env + Cuerdas arg más algunas páginas adicionales asignados en setup_arg_pages (20 páginas en 2.6.33 1, 2, 128 Kb en 2.6.34 3)

En resumen:

initial stack size = MIN(size for filename + arg strings + env strings + extra pages, MAX(size for filename + arg strings + env strings, RLIMIT_STACK)) 

donde

size for filename + arg strings + env strings <= MAX(ARG_MAX(32 pages), RLIMIT_STACK/4) 

Además, los núcleos con exec-shield parche de Ingo Molnar (Fedora, Ubuntu, ...) tienen un EXEC_STACK_BIAS adicional "(2 MB más para cubrir efectos de aleatorización). ", consulte la llamada a la nueva función over_stack_limit() de acct_stack_growth() ([Ubuntu1], [Ubuntu2], [Ubuntu3]).

He editado el programa original para mostrar esto:

#include <stdio.h> 
#include <sys/resource.h> 

void foo(void); 

int main(int argc, char *argv[]) { 
     struct rlimit lim = {1, 1}; 


     if (argc > 1 && argv[1][0] == '-' && argv[1][8]=='l') { 
       printf("limiting stack size\n"); 
       if (setrlimit(RLIMIT_STACK, &lim) == -1) { 
         printf("rlimit failed\n"); 
         return 1; 
       } 
     } 

     foo(); 

     return 0; 
} 

void foo() { 
     unsigned ints[32768]; 

     printf("foo: %u\n", ints[2047]=42); 
} 

que se traduce en:

$./rl 
foo: 42 
$./rl -l 
limiting stack size 
Segmentation fault 
$ 
+1

No, en realidad, pude hacer crecer una pila existente. Ahora soy como un perro que no va a soltar un hueso con este problema. –

+0

@Tim Post: ¿estás seguro de que la pila creció? Ver mi respuesta editada, hay un espacio extra en la pila inicial. – ninjalj

+0

Sí, amplié ambos casos a 16k, el mismo resultado. –

4

creo setrlimit mueve los "punteros de recursos", pero no se aplica a los nuevos límites hasta que exec una nueva copia del programa.

#include <stdio.h> 
#include <stdlib.h> 
#include <unistd.h> 
#include <sys/resource.h> 

void foo(int chk) { 
    unsigned ints[2048]; 
    ints[2047] = 42; 
    printf("foo %d: %u\n", chk, ints[2047]); 
} 

int main(int argc, char **argv) { 
    char *newarg[] = { "argv[0]", "one", "two" }; 
    char *newenv[] = { NULL }; 
    struct rlimit lim; 

    newarg[0] = argv[0]; 
    getrlimit(RLIMIT_STACK, &lim); 
    printf("lim: %d/%d\n", (int)lim.rlim_cur, (int)lim.rlim_max); 
    switch (argc) { 
    case 1: /* first call from command line */ 
     lim.rlim_cur = 65536; 
     lim.rlim_max = 65536; 
     if (setrlimit(RLIMIT_STACK, &lim) == -1) return EXIT_FAILURE; 
     newarg[2] = NULL; 
     foo(1); 
     execve(argv[0], newarg, newenv); 
     break; 
    case 2: /* second call */ 
     lim.rlim_cur = 1024; 
     lim.rlim_max = 1024; 
     if (setrlimit(RLIMIT_STACK, &lim) == -1) return EXIT_FAILURE; 
     foo(2); 
     execve(argv[0], newarg, newenv); 
     break; 
    default: /* third call */ 
     foo(3); 
     break; 
    } 
    return 0; 
} 

Y una prueba de funcionamiento:

 
$ ./a.out 
lim: 8388608/-1 
foo 1: 42 
lim: 65536/65536 
foo 2: 42 
Killed 

Por qué el proceso muere antes de imprimir los límites (y antes de llamar a foo), no sé.

+0

Sospeché que era similar y lo intenté con 'fork()', lo que no hizo ninguna diferencia. No puedo entender por qué setrlimit() solo genera procesos de efectos a través de 'exec' y no a los padres, pero ese parece ser el caso. –

+0

Con GDB obtengo el 'Programa salido normalmente' después de la línea "foo 2: 42" - sin eliminar, sin segfault – tur1ng

+0

@ tur1ng: intente agregar 'newarg [0] = argv [0];' al principio de main. Sospecho que su binario no se llama "a.out" – pmg

Cuestiones relacionadas