утечка мозга КРИС КАСПЕРСКИ АКА МЫЩЪХ Спецвыпуск: Хакер, номер #071, стр. 071-020-1 БОРЬБА С УТЕЧКАМИ РЕСУРСОВ И ПЕРЕПОЛНЯЮЩИМИСЯ БУФЕРАМИ НА ЯЗЫКОВОМ И ВНЕЯЗЫКОВОМ УРОВНЕ С УТЕЧКАМИ РЕСУРСОВ И ПЕРЕПОЛНЯЮЩИМИСЯ БУФЕРАМИ ПРОГРАММИСТСКОЕ СООБЩЕСТВО БОРЕТСЯ УЖЕ ЛЕТ ТРИДЦАТЬ, НО ВСЕ БЕЗУСПЕШНО. В ЭТОЙ СТАТЬЕ МЫЩЪХ ДЕЛИТСЯ СВОИМИ СОБСТВЕННЫМИ ТРЮКАМИ И НАХОДКАМИ. НА «СЕРЕБРЯНУЮ ПУЛЮ» ОНИ НЕ ПРЕТЕНДУЮТ, НО ЛИКВИДИРУЮТ ЗНАЧИТЕЛЬНОЕ КОЛИЧЕСТВО ОШИБОК С МИНИМАЛЬНЫМИ НАКЛАДНЫМИ РАСХОДАМИ [введение.] Проблемы утечки ресурсов и переполнения буферов главным образом относятся к Си (и в меньшей степени к его преемнику — Си++). Это не покажется удивительным, если вспомнить, что Си — самый низкоуровневый из всех высокоуровневых языков, всего лишь на одну ступень отстоящий от ассемблера. Си делает только то, о чем его просят, вынуждая программиста самостоятельно выделять и освобождать память, контролировать границы буферов и заботиться о массе других вещей, которые на Java, С# и многих других языках автоматически выполняются компилятором (интерпретатором). Однако ни один из них не завоевал такой популярности, как Си, и навряд ли завоюет ее в дальнейшем. Почему? На Си написано огромное количество программ, которые необходимо развивать и поддерживать, Си обеспечивает максимальную производительность и переносимость, в то время как его конкуренты предлагают либо производительность, либо переносимость. Что же касается Си++, то это гибрид, вобравший в себя множество концепций и парадигм программирования, позаимствованных из таких языков как, например, ADA или Smalltalk, но все равно оставшийся «ручным». В нем нет ни сборщика мусора, ни динамических стековых массивов, ни автоматического контроля границ, ни многих других вещей, которые по-прежнему приходится делать руками. А нет их там потому, что вся эта «автоматизация» заметно снижает производительность и превращает Си++ в пародию на Visual Basic, ярчайшим примером которой является C#. Ходят устойчивые слухи, что значительная часть Longhorn'а была написана на C#, но, несмотря на все усилия разработчиков, достичь приемлемой производительности и стабильности им так и не удалось (а что еще можно ожидать от Бейсика, пускай и продвигаемого под видом Си?). В конечном счете, компания была вынуждена похоронить миллионы строк, и начать разработку заново. Если не ошибаюсь, текущая версия Windows Vista базируется на коде Server 2003, написанном на смеси Си с Си++, а это значит, что отказ от Си/Си++ (по крайней мере, в крупных проектах) невозможен, и вместо того, чтобы жаловаться на судьбу, лучше придумать пару-тройку новых методов борьбы с утечками и переполняющимися буферами, о чем мы сейчас и поговорим. [переполняющиеся буферы.] Ошибок переполнения не избежала практически ни одна чуть более сложная, чем «hello, world!», программа, а все потому, что ошибки переполнения в Си носят фундаментальный характер, и язык не предоставляет никаких механизмов для их преодоления. Контроль границ буфера приходится осуществлять вручную. К тому же, если говорить по существу, в Си вообще нет никаких буферов. Получив указатель на «буфер», функция не может определить его длину. Оператор sizeof возвращает размер указателя (как правило, равный DWORD), но отнюдь не размер блока, на который он указывает. Небольшую лазейку оставляет нестандартная функция _msize, сообщающая размер динамического блока, но она требует указателя на его начало, отказываясь работать с серединой. |