K&R:C - Обнаружено разрушение стека
Мой код такой:
int find_test(int argc, char *argv[])
{
char line[MAX_LINES];
int c, except = 0, found = 0, number = 0;
long lineno = 0;
int i = 0;
while(--argc > 0 && (*++argv)[0] == '-'){
while(c = *++argv[0]){
switch(c){
case 'x':
except = 1;
break;
case 'n':
number = 1;
break;
default:
printf("find:illegal option %c\n", c);
argc = 0;
found = -1;
break;
}
}
}
if(argc != 1){
printf("Usage:find -x -n pattern\n");
}else{
while(getline(line, MAX_LENGTH) > 0){
lineno++;
if((strstr(line, *argv) != NULL) != except){
if(number)
printf("%ld:", lineno);
printf("%s\n", line);
found++;
}
}
}
return found;
}
getline
как это:
int getline(char *line, int maxline)
{
char *p = line;
int c;
while(maxline-- && (c = getchar()) != EOF && c != '\n'){
*line++ = c;
}
if(maxline > 0)
*line = '\0';
return line - p;
}
Когда я использую gcc -Wall -O2 -g a.c -o a.out
и выполнить a.out -x -n 111<find_test
, где find_test
это просто мои тестовые данные:
line1:111111111111111
line2:222222222222222
line3:222222222222222
line4:444444444444444
line2:kasdskskdk
Я получил сообщение об ошибке:
*** stack smashing detected ***: ./a.out terminated
======= Backtrace: =========
/lib/tls/i686/cmov/libc.so.6(__fortify_fail+0x48)[0xb7ead138]
/lib/tls/i686/cmov/libc.so.6(__fortify_fail+0x0)[0xb7ead0f0]
./a.out[0x8048e95]
./a.out[0x8048ec2]
/lib/tls/i686/cmov/libc.so.6(__libc_start_main+0xe0)[0xb7dd6450]
./a.out[0x8048641]
======= Memory map: ========
08048000-0804a000 r-xp 00000000 08:08 17488 /home/jyc/prgm/the_c_p_l/a.out
0804a000-0804b000 rw-p 00001000 08:08 17488 /home/jyc/prgm/the_c_p_l/a.out
0804b000-0806c000 rw-p 0804b000 00:00 0 [heap]
b7dbf000-b7dc0000 rw-p b7dbf000 00:00 0
b7dc0000-b7f09000 r-xp 00000000 08:08 694644 /lib/tls/i686/cmov/libc-2.7.so
b7f09000-b7f0a000 r--p 00149000 08:08 694644 /lib/tls/i686/cmov/libc-2.7.so
b7f0a000-b7f0c000 rw-p 0014a000 08:08 694644 /lib/tls/i686/cmov/libc-2.7.so
b7f0c000-b7f0f000 rw-p b7f0c000 00:00 0
b7f0f000-b7f32000 r-xp 00000000 08:08 694648 /lib/tls/i686/cmov/libm-2.7.so
b7f32000-b7f34000 rw-p 00023000 08:08 694648 /lib/tls/i686/cmov/libm-2.7.so
b7f3a000-b7f44000 r-xp 00000000 08:08 677855 /lib/libgcc_s.so.1
b7f44000-b7f45000 rw-p 0000a000 08:08 677855 /lib/libgcc_s.so.1
b7f45000-b7f49000 rw-p b7f45000 00:00 0
b7f49000-b7f4a000 r-xp b7f49000 00:00 0 [vdso]
b7f4a000-b7f64000 r-xp 00000000 08:08 678556 /lib/ld-2.7.so
b7f64000-b7f66000 rw-p 00019000 08:08 678556 /lib/ld-2.7.so
bfa26000-bfa3b000 rw-p bffeb000 00:00 0 [stack]
но если я использую gcc -Wall -O2 -g -fno-stack-protector a.c -o a.out
и выполнить a.out -x -n 111<find_test
все отлично. Я не мог найти причину. Кто-нибудь может помочь?
2 ответа
Вы, кажется, перепутали MAX_LINES
а также MAX_LENGTH
, Похоже, вы выделяете место для первого, но то, что вы читаете, это второе.
int find_test(int argc, char *argv[])
{
char line[MAX_LINES]; <-------------
int c...
....
while(getline(line, MAX_LENGTH) > 0){ <-------
Кстати, почему вы не используете fgets()
вместо getline()
?
Обновить
но если я использую gcc -Wall -O2 -g -fno-stack-protector ac -o a.out и выполняю a.out -x -n 111
Нет. Определенно, все не в порядке. Вы все еще перезаписываете область памяти; перезапись может быть "в основном безвредной" сейчас и на этой конкретной платформе, но она все еще потенциально смертельна. В другом контексте та же самая ошибка, если только не была установлена какая-либо защита (к счастью, в настоящее время это происходит очень часто - но вы не можете рассчитывать на удачу!), Может позволить удаленному злоумышленнику получить контроль над вашей машиной. Если вы попытаетесь использовать более длинные строки, есть вероятность, что ваша программа "ОК" без стекового протектора снова вызовет segfault (или, для более сложных программ, выдаст неверные результаты или даже нанесет ущерб системе).
Когда ваша линия достигает MAX_LENGTH
ваш strstr
может сканировать за пределами буфера, потому что буфер не имеет символа '\0'.
РЕДАКТИРОВАТЬ: И есть еще один момент, который я хотел бы сделать. Это один спорный, потому что есть аргументы в пользу обеих возможностей. Было бы лучше использовать unsigned
буферные индексы на мой взгляд. Это заставило бы обращаться с циклом по-другому, что имхо легче сделать правильно. Кроме того, при индексации массива с unsigned
, в случае недосадки вы получите скорее всего segmentation fault
с этим, чем с подписанным интегралом. У нас был случай в нашем проекте недавно (система, которая работает с 1998 года), и я изменил одну переменную в коде на size_t
и поймал 2 ошибки недостаточного количества, которые были пропущены с самого начала.
EDIT2: еще один стилистический момент, который я хочу сделать. Избегайте пост-(in|de) создания в выражениях цикла, они очень часто приводят к незаметным ошибкам (как показано выше). Предварительное (в | де) создание не является проблемой. Причина, по которой после (в | де) создания проблемы, заключается в том, что когда вы мысленно анализируете выражение для его условий (продолжить или прервать), оно сразу становится недействительным, то есть в конце выражения, значения, которые вы использовали в своей голове чтобы увидеть результат больше не существует. Может быть, это только я, но за 25 лет, которые я программировал на C, было огромное количество раз, когда я получал неправильные циклы после (в | де) обработки, и это всегда исправлялось либо предварительным (в | де) обработкой или снятие приращения из логического выражения.