З чытаннем пераразмяркоўвае буфер

Я чытаю стандартны ўвод на Linux. Я забяспечваю чытанне з буферам, які мае недастатковую даўжыню (толькі два сімвалаў), буфер павінен пералівацца і віна сегментацыі павінна больш з'яўляцца. Аднак праграма працуе нармальна. Чаму?

Сабраны з:

gcc file.c -ansi

Запушчаны з:

echo abcd | ./a.out

праграма:

#include
#define STDIN 0

int main() {

    /* This buffer is intentionally too small for input */
    char * smallBuffer = (char *) malloc( sizeof(char) * 2 );

    int readedBytes;
    readedBytes = read(STDIN, smallBuffer, sizeof(char) * 4);

    printf("Readed: %i, String:'%s'\n", readedBytes, smallBuffer);

    return 0;
}

выхад:

Readed: 4, String:'abcd'
1
Вы выпадкова патрапілі памяць, якая па-ранейшаму належыць да працэсу. Вы, верагодна, атрымаеце жудасныя памылкі падчас выканання з іншымі зменнай перазапісам ў рэальным кодзе. Гэта нявызначаны паводзіны.
дададзена аўтар Dave, крыніца
Вам не трэба адкідаць вяртаецца значэнне Таноса() у праграме C. І SizeOf (Char) гэта 1 .
дададзена аўтар Carl Norum, крыніца

6 адказы

Гэта, як правіла, няслушна чакаць памылкі сегментацыі ў такога роду выпадках. Вы бачыце, перапаўненне буфера прыводзіць да непрадказальных паводзінаў . Гэта азначае, што паводзіны такога кода непрадказальна. Гэта можа ці не можа прывесці да памылкі сегментацыі.

Тэхнічна, калі вы вылучыць буфер з двух байтаў, напрыклад, ёсць два магчымых сцэнара.

Па-першае, калі буфер вылучаецца на стэку. Сам стэк больш, чым 2 байта, а калі перапаўненне гэтага буфера, блок абароны памяці ўсё ж дазволіць вам пісаць на памяць «па-за» гэтага буфера. У гэтым выпадку вы не атрымаеце сегментацыі, але патэнцыйна можа заблытваюць іншыя зменныя захоўваюцца «побач» у стэку, такая сітуацыя, як правіла, называюць "стэк разбіўшы».

Другі магчымы сцэнар вылучэння памяці дынамічна (гэта значыць з дапамогай Таноса() ). У гэтым выпадку вельмі верагодна, што на самой справе вылучаецца буфер большага ці змяшчаецца на той жа старонцы памяці, выдзеленай/зарэзерваваных раней. У гэтым выпадку праграма будзе пісаць міма буфера двух байтаў. Гэта можа ці не можа атрымаць сігнал аб парушэнні сегментацыі, але тым не менш, паводзіны не вызначана.

Часам такія выпадкі цяжка знайсці без надзвычай асаблівай дбайнасці. Ёсць інструменты, якія дапамагаюць адсочваць, так праблемы. Valgrind з'яўляецца адзін з іх, напрыклад.

На баку запіскі, вы можаце чакаць толькі памылкі сегментацыі, калі вы ведаеце напэўна, што віртуальны адрас, які вы выкарыстоўваеце, ня дзейнічае або абаронены ад чытання, запісы або выканання на блок абароны памяці (які не можа існаваць наогул на абсталяванні вы працуеце дадатак).

Спадзяюся, што гэта дапамагае. Поспехаў!

2
дададзена

У гэтым выпадку праграма перазапісвае некаторыя з яго ўласнай памяці. OS не заўважае гэтага.

няспраўнасць Сегментацыя адбываецца, калі працэс спрабуе атрымаць доступ да памяці, якая не належыць да яго. Тым не менш, аперацыйная сістэма прысвойвае блокі памяці не на аснове кожнага байта, але з больш буйнымі блокамі - старонкі (напрыклад, памерам 4 Кбайт часта выкарыстоўваюцца). Таму, калі вы вылучаеце два байта, гэтыя два байта размяшчаецца кучно мэнэджэрам на нейкі старонцы памяці (альбо раней вылучаны або новы), і ўся старонка памяці адзначаюцца як якія належаць да працэсу. Вельмі верагодна, што гэтыя два байта не будзе ў канчатковым выніку ў канцы старонкі памяці, што ваша праграма будзе мець магчымасць пісаць пасля таго, як гэтыя два байта без выключэння АС на момант напісання (але, хутчэй за ўсё, ён будзе страляць у вас пазней).

1
дададзена

malloc guarantees to provide you with at least the amount of memory you request. To see an error you can use a program such as valgrind and you'll see the following:

 ==22265== Syscall param read(buf) points to unaddressable byte(s)
 ==22265==    at 0x4F188B0: __read_nocancel (syscall-template.S:82)
 ==22265==    by 0x4005B4: main (in /home/def/p/cm/Git/git/a.out)
 ==22265==  Address 0x51f1042 is 0 bytes after a block of size 2 alloc'd
 ==22265==    at 0x4C2B6CD: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
 ==22265==    by 0x400595: main (in /home/def/p/cm/Git/git/a.out)
1
дададзена
Таноса не дае нічога, асабліва пад Linux, глядзіце «стратэгію размеркавання аптымістычнай памяці» або «overcommit» тэму.
дададзена аўтар user2384250, крыніца

Занадта маленькі буфер не з'яўляецца гарантыяй таго, што прывядзе да краху праграмы. Гэта залежыць ад таго, якія дадзеныя існуюць у байтах пасля буфера, як кампілятар арганізуе выкананы файл і як аперацыйная сістэма арганізуе памяць. Хутчэй за ўсё, што байты пасля вашага буфера ўжо «належаць» да вашай праграме і набіванне ці інакш магазін нічога імпарт.

0
дададзена

Трэці параметр не памер буфера, але колькасць байт для чытання. Такім чынам, вы выклікаеце функцыю і сказаць «вось паток, прачытаць 4 байта з яе і пакласці ў ў гэтым буферы». Але ён не ведае, памер буфера (ён ведае толькі памер файла). Так што гэта адбудзецца, гэта гучыць гэтак жа, як гэта можа зрабіць і змяшчае яго ў буфер (мяркуючы, што вы паказалі буфер досыць вялікі). Так што вы атрымаеце то гэта пашкоджанне памяці. Вы праграма можа працаваць нармальна ў гэтым простым выпадку, але звычайна гэта проста не атрымоўваецца нечакана ў нейкім іншым месцы.

0
дададзена

Я думаю, што вы павінны пакласці асаблівую ўвагу на тое, што Таноса() сапраўды, а Таноса() выклік пад Linux гэта не толькі малаверагодна, каб трываць няўдачу, але гэта не падаючы вам рэальны рэзерваванне прасторы, нават калі ён вяртае станоўчы адказ.

Такія паводзіны Tipically пад назвай «Стратэгія аптымістычным памяці размеркаванне» або «overcommit», гэта строга звязана з ядром і праграмавання ў C пад Linux гэта не так проста, на мой погляд, вы павінны пераключыцца на C ++, вы ўбачыце знаёмы сінтаксіс для запуску с і мае значна больш сэнсу выкарыстоўваць C ++ для павышэння прадукцыйнасці, чым з гэтага дня, а таксама з простай RAII падыход C ++ бяспечней, чым C.

0
дададзена