2010-11-07 18 views
11

Im unten angegebenen Beispiel versuche ich die Stackgröße auf 1kb zu setzen.Setze Stackgröße mit setrlimit() und provoziere einen Stacküberlauf/segfault

Warum ist es jetzt möglich, ein Array von Ints auf dem Stapel mit der Größe 8kb in foo() zu reservieren?

#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

Vielen Dank, ich jetzt um herauszufinden, süchtig bin, warum dies nicht funktioniert, wie in der Werbung im Menschen (2) setrlimit. Glücklicherweise können Sie in gcc die Stackgröße angeben :) –

+0

Eine Frage, die häufiger favorisiert wurde, als sie aktualisiert wurde - zu diesem Zeitpunkt. Interessant. –

Antwort

4

Das Limit wird sofort gesetzt, aber nur überprüft, wenn versucht wird, einen neuen Stack zuzuweisen oder den bestehenden Stack zu vergrößern. Ein grep für RLIMIT_STACK (or a LXR identifier search) über die Kernel-Quellen sollte sagen.

Offenbar ist die anfängliche Größe des Stapels ist, was zu den Dateinamen + env Strings + arg Strings plus einige zusätzliche Seiten auf setup_arg_pages (20 Seiten in 2.6.33 1, zugeordnet benötigt wird 2, 128 Kb auf 2.6.34 3).

Zusammengefasst:

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

wo

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

Zusätzlich Kerne mit Ingo Molnar exec-shield Patch (Fedora, Ubuntu, ...) haben eine zusätzliche EXEC_STACK_BIAS „(2MB mehr zur Deckung Randomisierungseffekte.) ", siehe Aufruf der neuen Funktion over_stack_limit() von acct_stack_growth() ([Ubuntu1], [Ubuntu2], [Ubuntu3]).

Ich habe das ursprüngliche Programm bearbeitet, dies zu zeigen:

#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); 
} 

was zur Folge hat:

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

Nein, eigentlich konnte ich einen bestehenden Stack erweitern. Ich bin jetzt wie ein Hund, der einen Knochen mit diesem Problem nicht loslässt. –

+0

@Tim Post: Bist du sicher, dass der Stack gewachsen ist? Siehe meine bearbeitete Antwort, es gibt etwas zusätzlichen Platz auf dem ursprünglichen Stapel. – ninjalj

+0

Ja, ich habe beide Fälle auf 16k erweitert, dasselbe Ergebnis. –

4

Ich denke setrlimit die „Ressourcenzeiger“ bewegt sich aber nicht gilt, die neuen Grenzen, bis Sie exec eine neue Kopie des Programms.

#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; 
} 

Und ein Testlauf:

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

Warum das Verfahren der Grenzen vor dem Drucken (und vor dem Aufruf von foo), ich weiß nicht getötet wird.

+0

Ich vermutete ähnlich und versuchte nur mit 'fork()', was keinen Unterschied machte. Ich kann nicht verstehen, warum setrlimit() nur Prozesse hervorruft, die über 'exec' und nicht die Eltern erzeugt werden, aber das scheint der Fall zu sein. –

+0

Mit GDB bekomme ich 'Programm normal beendet' nach der Zeile "foo 2: 42" - no abgetötet, kein segfault – tur1ng

+0

@ tur1ng: versuche 'nearg [0] = argv [0];' am Anfang von main. Ich vermute, dass Ihre Binärdatei nicht "a.out" heißt – pmg