книги хакеры / журнал хакер / 140_Optimized
.pdf
|
|
|
|
hang |
e |
|
|
|
|
|
|
|
|
C |
|
E |
|
|
|||
|
|
X |
|
|
|
|
|
|||
|
- |
|
|
|
|
|
d |
|
||
|
F |
|
|
|
|
|
|
t |
|
|
|
D |
|
|
|
|
|
|
|
i |
|
|
|
|
|
|
|
|
|
r |
||
P |
|
|
|
|
|
NOW! |
o |
|||
|
|
|
|
|
|
|
||||
|
|
|
|
|
BUY |
|
|
|||
|
|
|
|
to |
|
|
|
|
|
|
w Click |
|
|
|
|
|
m |
||||
|
|
|
|
|
|
|||||
w |
|
|
|
|
|
|
|
|
|
|
|
w |
|
|
|
|
|
|
|
o |
|
|
. |
|
|
|
|
|
.c |
|
||
|
|
p |
|
|
|
|
g |
|
|
|
|
|
|
df |
|
|
n |
e |
|
||
|
|
|
|
-xcha |
|
|
|
|
|
|
|
|
hang |
e |
|
|
|
|
|
|
|
|
C |
|
E |
|
|
|||
|
|
X |
|
|
|
|
|
|||
|
- |
|
|
|
|
|
d |
|
||
|
F |
|
|
|
|
|
|
t |
|
|
|
D |
|
|
|
|
|
|
|
i |
|
|
|
|
|
|
|
|
|
r |
||
P |
|
|
|
|
|
NOW! |
o |
|||
|
|
|
|
|
|
|
||||
|
|
|
|
|
BUY |
|
|
|||
|
|
|
|
to |
|
|
|
|
|
|
w Click |
|
|
|
|
|
m |
||||
|
|
|
|
|
|
|||||
w |
|
|
|
|
|
|
|
|
|
|
|
w |
|
|
|
|
|
|
|
o |
|
|
. |
|
|
|
|
|
.c |
|
||
|
|
p |
|
|
|
|
g |
|
|
|
|
|
|
df |
|
|
n |
e |
|
||
|
|
|
|
-x cha |
|
|
|
|
позволяющие определить, что нужно предпринять для гарантированной защиты кода.
Для защиты управляемого кода используются две технологии:
• защита на основе признаков (evidence-based security)
позволяет определять, какие разрешения следует предоставлять коду;
• защита по правам доступа кода (code access security) позволяет проверять, весь ли код в стеке имеет необходимые разрешения на выполнение каких-либо действий. Эти две технологии связаны между собой концепцией разрешений.
По признакам и политике безопасности, устанавливаемой администратором, система защиты определяет, какие разрешения могут быть выданы коду. Программа сама может запрашивать какое-либо разрешение, влияя на состав окончательного набора разрешений. Запрос разрешения выражается в виде объявления на уровне сборки с синтаксисом пользовательских (custom) атрибутов. Однако, в любом случае, код не может получить более широкие или ограниченные разрешения, чем это предписано политикой безопасности. Разрешение предоставляется только раз и определяет права всего кода в сборке. Для просмотра и изменения политики безопасности используется инструмент настройки .NET Framework (Mscorcfg.msc).
ПЛАНИРУЕМ БОЕВЫЕ ДЕЙСТВИЯ
Есть такая японская пословица: «Выходи из дома так, как будто он окружен тысячей врагов». Понятно, что во времена феодальной Японии, когда туда-сюда бегали самураи, воевали между собой и искали других приключений,
это пословица была актуальной. Сегодня, позволю себе |
|
заметить, эта пословица будет справедливой и для твоего |
|
кода — если ты думаешь, что твой код никому нафиг не |
|
сдался, ты глубоко ошибаешься. |
Mscorcfg.msc |
Если твой код — часть приложения, которая не вызывает- |
|
ся другим кодом, то его защита проста и специального |
|
программирования не требует. Но учти, что он может быть |
архитектуре безопасности Windows, что приводит к появ- |
вызван злонамеренным кодом. Хотя защита по правам |
лению таких извратов, как UAC, DEP, рандомизация стека, |
доступа кода препятствует доступу злонамеренного кода к |
сандбоксов и пр. |
ресурсам, он все равно способен считать значения полей |
• Зона интернета (например, http://www.microsoft.com). |
или свойств, которые, возможно, содержат ценную инфор- |
Коду из этой зоны предоставляется очень ограниченный |
мацию. |
набор разрешений, который не опасно предоставить даже |
IHRE AUSWEISS, BITTE! |
злонамеренному коду. Обычно этому коду нельзя доверять, |
поэтому его можно безопасно выполнять только с очень |
|
Выдача разрешений |
узкими разрешениями, с которыми он не сумеет нанести |
Защита на основе признаков базируется на предположе- |
ущерб: |
нии, что высокий уровень доверия (с широкими полномо- |
• WebPermission — доступ к серверу сайта, с которого |
чиями) присваивается лишь коду, заслуживающему этого |
получен код; |
самого доверия, а злонамеренный код является «мало |
• FileDialogPermission — доступ только к файлам, спе- |
доверяемым» или вообще не имеет разрешений. В соот- |
циально указанным пользователем; |
ветствии с политикой по умолчанию в .NET Framework |
• IsolatedStorageFilePermission — постоянное хранилище, |
разрешения выдаются на основе зон (так, как они опреде- |
ограниченное пределами веб-сайта; |
лены в Microsoft Internet Explorer). Ниже приведено упро- |
• UlPermission — возможность записи информации в окно |
щенное описание этой политики «по умолчанию»: |
пользовательского интерфейса. |
• Зона локального компьютера (например, C:\app.exe) |
• Зона интрасети (например \\UNC\share). Код из этой |
является полностью доверяемой. Предполагается, что |
зоны выполняется с чуть большими разрешениями, чем |
пользователи помещают на свой компьютер только код, |
код из интернета, но среди них все равно нет таких, кото- |
которому они доверяют, и что большинство пользова- |
рые предоставляли бы широкие полномочия: |
телей не собираются разбивать свой жесткий диск на |
• FilelOPermission — доступ только для чтения к файлам |
области с разной степенью доверия. По существу, этот |
каталога, из которого загружен код; |
код может делать все что угодно, поэтому от злонамерен- |
• WebPermission — доступ к серверу, с которого загру- |
ного кода, находящегося в этой зоне, никакой защиты |
жен код; |
нет. Честно говоря, по моему скромному мнению, именно |
• DNSPermission — допускается разрешение DNS-имен в |
это предположение является одной из огромных дыр в |
IP-адреса; |
DVD |
dvd
Надискетынайдешь некоторыепрограммныепримеры, реализующиенаC# принципы, описанныевстатье.
HTTP://WWW
links
• blogs.msdn.com, www.eggheadcafe. com, bytes.com — на этихсайтахтысможешьнайтироссыпи интереснойинформацииотехнологиях
.NET ипополярных языках
XÀÊÅÐ 09 /140/ 10 |
109 |
|
|
|
|
hang |
e |
|
|
|
|
|
|
|
|
C |
|
E |
|
|
|||
|
|
X |
|
|
|
|
|
|||
|
- |
|
|
|
|
|
d |
|
||
|
F |
|
|
|
|
|
|
t |
|
|
|
D |
|
|
|
|
|
|
|
i |
|
|
|
|
|
|
|
|
|
r |
||
P |
|
|
|
|
|
NOW! |
o |
|||
|
|
|
|
|
|
|
||||
|
|
|
|
|
BUY |
|
|
|||
|
|
|
|
to |
|
|
|
|
|
|
w Click |
|
|
|
|
|
m |
||||
|
|
|
|
|
|
|||||
w |
|
|
|
|
|
|
|
|
|
|
|
w |
|
|
|
|
|
|
|
o |
|
|
. |
|
|
|
|
|
.c |
|
||
|
|
p |
|
|
|
|
g |
|
|
|
|
|
|
df |
|
|
n |
e |
|
||
|
|
|
|
-xcha |
|
|
|
|
CODING
|
|
|
|
hang |
e |
|
|
|
|
|
|
|
|
C |
|
E |
|
|
|||
|
|
X |
|
|
|
|
|
|||
|
- |
|
|
|
|
|
d |
|
||
|
F |
|
|
|
|
|
|
t |
|
|
|
D |
|
|
|
|
|
|
|
i |
|
|
|
|
|
|
|
|
|
r |
||
P |
|
|
|
|
|
NOW! |
o |
|||
|
|
|
|
|
|
|
||||
|
|
|
|
|
BUY |
|
|
|||
|
|
|
|
to |
|
|
|
|
|
|
w Click |
|
|
|
|
|
m |
||||
|
|
|
|
|
|
|||||
w |
|
|
|
|
|
|
|
|
|
|
|
w |
|
|
|
|
|
|
|
o |
|
|
. |
|
|
|
|
|
.c |
|
||
|
|
p |
|
|
|
|
g |
|
|
|
|
|
|
df |
|
|
n |
e |
|
||
|
|
|
|
-x cha |
|
|
|
|
Утилитаграфической настройки прав доступак коду GuiCaspol
•FileDialogPermission — доступ только к файлам, специально указанным пользователем;
•Isolated StorageFilePermission — постоянное хранилище (с меньши-
ми ограничениями);
•UlPermission — код может свободно использовать собственные окна верхнего уровня.
•Зона ограниченных сайтов, код из которой выполняется только с минимальными разрешениями.
Продумай свои требования к защите и соответственно измени политику безопасности. Правда, никакая конфигурация защиты не решит всех проблем: политика безопасности по умолчанию, в общем-то, рассчитана на запрет потенциально опасных операций.
В зависимости от способа развертывания, твой код может получать различные разрешения. Перед выпуском кода в мир убедись, что ему предоставляются разрешения, достаточные для нормальной работы. Продумывая защиту кода от атак, посмотри, откуда может быть загружен атакующий код, и как он может получить доступ к твоему коду.
ЗАЩИТА ДОСТУПА К МЕТОДАМ
Некоторые методы не стоит открывать для вызова произвольным недоверенным кодом. Вызов такого метода связан с различными рисками: он может предоставлять информацию, доступ к которой ограничен, принимать любую передаваемую ему информацию, не проверять ошибки в параметрах или, получив некорректные параметры, неправильно работать или даже нанести вред. Учитывай все эти случаи и предпринимай меры для защиты таких методов.
Иногда приходится ограничивать доступ к методам, которые не предназначены для открытого использования, но все равно должны быть объявлены как открытые. Например, у тебя есть некий интерфейс, вызываемый вашими же DLL, и поэтому он должен быть открытым, но ты не хочешь, чтобы этот интерфейс был общедоступным, так как нежелательно, чтобы клиенты могли с ним работать, или чтобы злонамеренный код воспользовался им как точкой входа в твой компонент. Еще одна типичная причина ограничения доступа к методу, который не предназначен для общего использования (но, тем не менее, должен быть открытым) — стремление избежать документирования и поддержки интерфейса, применяемого исключительно на внутреннем уровне. Поэтому вот тебе несколько советов, как можно ограничить доступ к методам в управляемом коде:
•Ограничь область доступности классом, сборкой или производными классами (если им можно доверять). Это простейший способ ограничения доступа к методу. Замечу, что вообще-то производные классы могут быть менее доверяемыми, чем класс-предок, но в некоторых случаях они используют ту же идентификацию, что и надкласс. В частности, ключевое слово protected не подразумевает доверия, и его необязательно нужно использовать в контексте защиты.
•Разрешай вызов метода только вызывающим с определенной идентификацией (обладающим заданными вами признаками).
•Разрешай вызов метода только тем, у кого есть требуемые разрешения.
Аналогичным образом декларативная защита позволяет контролировать наследование классов. С помощью InheritanceDemand можно потребовать наличия определенной идентификации или разрешения от:
•всех производных классов;
•производных классов, переопределяющих те или иные методы.
НЕ ВЛЕЗАЙ — УБЬЕТ!
Какие разрешения несут в себе потенциальную опасность?
Для выполнения некоторых защищенных операций .NET Framework предоставляет разрешения, потенциально позволяющие обойти систему защиты. Эти опасные разрешения следует предоставлять только коду, заслуживающему доверия, и только при абсолютной необходимости. Обычно, если злонамеренный код получает такие разрешения, то защититься нельзя. К опасным разрешениям относятся:
[Security Permission]:
•Unmanaged Code — позволяет управляемому коду вызывать неуправляемый код, что зачастую весьма опасно;
•Skip Verification — код может делать что угодно без всякой верификации;
•ControlEvidence — управление признаками позволяет обмануть систему защиты;
•ControlPolicy — возможность изменять политику безопасности позволяет отключить защиту;
•SerializationFormatter — за счет сериализации можно обойти управление доступом;
•ControlPrincipal — возможность указывать текущего пользователя позволяет обходить защиту на основе ролей;
•ControlThread — возможность манипуляций с потоками опасна, так как с ними связано состояние защиты;
[ReflectionPermission]:
•MemberAccess — позволяет отключать механизм управления доступом (становится возможным использование закрытых членов).
ЗАЩИЩАЕМ ДОСТУП К МЕТОДУ ИЛИ КЛАССУ
Следующий пример показывает, как обезопасить открытый метод, ограничив доступ к нему.
1. Команда sn -k создает пару из закрытого и открытого ключа. Закрытая часть нужна, чтобы подписать код строгим именем (strong name), и хранится в безопасном месте издателем кода. Если она станет известной, указать твою подпись в своем коде сможет кто угодно.
sn -k keypair.dat
csc/r:App1.dll /a. keyfile: keypair.dat App1.cs sn -p keypair.dat public.dat
sn -tp public.dat >publichex.txt
[StrongNameldentityPermissionAttribute ( SecurityAction
. LinkDemand , PublicKey="...hex...",Name="App1", Version="0. 0.0.0")]
public class MyClass
2.Команда esc компилирует и подписывает Appl, предоставляя ему доступ к защищенному методу.
3.Следующие две команды sn извлекают из пары открытый ключ и преобразуют его в шестнадцатеричную форму.
4.Во второй половине показанного исходного кода содержится фрагмент защищаемого метода. Пользовательский атрибут (custom attribute) определяет строгое имя и в шестнадцатеричном формате вставляет открытый ключ, полученный командой sn, в атрибут PublicKey.
110 |
XÀÊÅÐ 09 /140/ 10 |
|
|
|
|
hang |
e |
|
|
|
|
|
|
|
|
C |
|
E |
|
|
|||
|
|
X |
|
|
|
|
|
|||
|
- |
|
|
|
|
|
d |
|
||
|
F |
|
|
|
|
|
|
t |
|
|
|
D |
|
|
|
|
|
|
|
i |
|
|
|
|
|
|
|
|
|
r |
||
P |
|
|
|
|
|
NOW! |
o |
|||
|
|
|
|
|
|
|
||||
|
|
|
|
|
BUY |
|
|
|||
|
|
|
|
to |
|
|
|
|
|
|
w Click |
|
|
|
|
|
m |
||||
|
|
|
|
|
|
|||||
w |
|
|
|
|
|
|
|
|
|
|
|
w |
|
|
|
|
|
|
|
o |
|
|
. |
|
|
|
|
|
.c |
|
||
|
|
p |
|
|
|
|
g |
|
|
|
|
|
|
df |
|
|
n |
e |
|
||
|
|
|
|
-xcha |
|
|
|
|
||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
hang |
e |
|
|
|
|
|
|
|
|
C |
|
E |
|
|
|||
|
|
X |
|
|
|
|
|
|||
|
- |
|
|
|
|
|
d |
|
||
|
F |
|
|
|
|
|
|
t |
|
|
|
D |
|
|
|
|
|
|
|
i |
|
|
|
|
|
|
|
|
|
r |
||
P |
|
|
|
|
|
NOW! |
o |
|||
|
|
|
|
|
|
|
||||
|
|
|
|
|
BUY |
|
|
|||
|
|
|
|
to |
|
|
|
|
|
|
w Click |
|
|
|
|
|
m |
||||
|
|
|
|
|
|
|||||
w |
|
|
|
|
|
|
|
|
|
|
|
w |
|
|
|
|
|
|
|
o |
|
|
. |
|
|
|
|
|
.c |
|
||
|
|
p |
|
|
|
|
g |
|
|
|
|
|
|
df |
|
|
n |
e |
|
||
|
|
|
|
-x cha |
|
|
|
|
Утилитаручнойнастройки прав доступак коду
5. В период выполнения Appl имеет необходимую подпись со строгим именем и может использовать MyClass.
Вэтом примере для защиты API-элемента применяется атрибут
LinkDemand.
Впримере, показанном ниже, частично доверенному коду запрещается обращаться к классам и методам (а также к свойствам и событиям). Когда такие объявления применяются к классу, защищаются все методы, свойства и события этого класса. Однако декларативная защита не влияет на доступ к полям. Кроме того, учти, что требования
ксвязи (link demands) защищают только от непосредственно вызывающего кода — возможность атак с подменой сохраняется.
[System.Security.Permissions.
PermissionSetAttribute(System.Security.
Permissions.SecurityAction.InheritanceDemand,
Name="FullTrust")]
[System.Security.Permissions.PermissionSetAttribute
(System.Security.Permissions.SecurityAction.
LinkDemand,
Name="FullTrust")]
public class YourClass{...}
ПРИЕМЫ БЕЗОПАСНОГО КОДИНГА
Запрос разрешений — отличный способ обеспечить поддержку защиты в разрабатываемом коде. Он позволяет запрашивать минимальные разрешения, необходимые для выполнения кода, и гарантировать, что код не получит разрешений больше, чем нужно. Например:
[assemblyiFilelOPermissionAttribute
(SecurityAction.RequestMinimum,
Wrlte="C:\\test.tmp")]
[assembly:ÐårmissionSet (SecurityAction.RequestOptional. Unrestricted=false)]
... SecurityAction.RequestRefused ...
В этом примере системе сообщается, что код не должен запускаться, пока не получит разрешение на запись в C:\test.tmp. Если одна из политик безопасности не предоставляет такое разрешение, генерируется исключение PolicyException, и код не запускается. Ты должен убедиться в том, что коду выдается нужное разрешение, и тогда тебе не придется беспокоиться об ошибках из-за нехватки разрешений. Кроме того, здесь система уведомляется о том, что дополнительные разрешения нежелательны. Иначе код получит все разрешения, предусмотренные политикой безопасности. Лишние разрешения не принесут вреда, но, если в системе безопасности есть какая-то ошибка, уменьшение числа разрешений, выдаваемых коду, может прикрыть брешь в защите. Таким образом, если код обладает разрешениями, которые ему не нужны, возможны проблемы с безопас-
ностью. Еще один способ ограничить количество привилегий, предоставляемых коду — явно перечислить разрешения, от которых следует отказаться. Отказ от разрешений осуществляется объявлением необязательности разрешений и исключением конкретных разрешений из запроса.
ЗАКЛЮЧЕНИЕ
Уфф! На тему обеспечения безопасности твоего кода можно говорить бесконечно. В рамках этой статьи я постарался упомянуть только самое важное и, на мой взгляд, интересное. Иными словами, то, что должно помочь тебе сделать свои приложения непробиваемыми. В общем, да пребудет с тобой Сила!z
XÀÊÅÐ 09 /140/ 10 |
111 |
|
|
|
|
hang |
e |
|
|
|
|
|
|
|
|
|
|
|
C |
|
E |
|
|
|
|
|
|||
|
|
X |
|
|
|
|
|
|
|
|
|||
|
- |
|
|
|
|
|
d |
|
|
|
|||
|
F |
|
|
|
|
|
|
t |
|
|
|
||
|
D |
|
|
|
|
|
|
|
i |
|
|
|
|
|
|
|
|
|
|
|
|
|
r |
|
|
||
P |
|
|
|
|
|
NOW! |
|
o |
|
|
|||
|
|
|
|
|
|
|
|
|
|
||||
|
|
|
|
|
BUY |
|
|
|
|
|
|||
w Click |
to |
|
|
|
|
|
|
CODING |
|
||||
|
|
|
|
|
|
m |
deeonis deeonis@gmail.com |
||||||
|
|
|
|
|
|
|
|||||||
w |
|
|
|
|
|
|
|
|
|
|
|||
|
w |
|
|
|
|
|
|
|
|
o |
|
|
|
|
. |
|
|
|
|
|
.c |
|
|
|
|||
|
|
p |
|
|
|
|
g |
|
|
|
|
|
|
|
|
|
df |
|
|
n |
e |
|
|
|
|||
|
|
|
|
-xcha |
|
|
|
|
|
|
|
|
|
|
|
hang |
e |
|
|
|
|
|
|
|
|
|
C |
|
E |
|
|
|
|||
|
|
X |
|
|
|
|
|
|
|||
|
- |
|
|
|
|
|
d |
|
|
||
|
F |
|
|
|
|
|
|
t |
|
|
|
|
D |
|
|
|
|
|
|
|
i |
|
|
|
|
|
|
|
|
|
|
r |
|
||
P |
|
|
|
|
|
NOW! |
o |
|
|||
|
|
|
|
BUY |
|
|
|
||||
|
|
|
|
|
|
|
|
||||
|
|
|
|
to |
|
|
|
|
|
|
|
w Click |
|
|
|
|
|
m |
|
||||
|
|
|
|
|
|
|
|||||
w |
|
|
|
|
|
|
|
|
|
|
|
|
w |
|
|
|
|
|
|
|
o |
|
|
|
. |
|
|
|
|
|
.c |
|
|
||
|
|
p |
|
|
|
|
g |
|
|
|
|
|
|
|
df |
|
|
n |
e |
|
|
||
|
|
|
|
-x cha |
|
|
|
|
|
КОДЕРСКИЕ
ТИПСЫИТРИКСЫ
ПравилакодинганаC++длянастоящихспецов
Продолжаем изучать тонкости управления памятью в C++. Следующие пара страниц будут посвящены углубленному изучению операторов new и delete. Из них ты узнаешь, какие требования предъявляет стандарт C++ к пользовательским реализациям этих операторов.
Впрошлойстатьемыуспелиразобраться, длячеговообщенужно заменятьоператорыnew иdelete своимиверсиями, ивкакихслучаях безэтогоможнообойтись. Такжемынемногозатронулитемуфункцииобработчикаnew иобсудилипроблемувыравниваниявозвращаемых указателей.
Еслитынечиталпрошлыетрюки, крайнесоветуюпрочитатьавгустовскийномер, посколькувэтойстатьемыбудемсчитать, чтопрошлые трюкитывсе-такиосилил.
Итак, креализациипользовательскихверсийоператоровnew иdelete C++ предъявляетопределенныетребования. Одноизэтихтребований мырассмотреливпрошломномере— этофункция-обработчикnew.
Онеймыещепоговоримчутьниже. Крометого, самописныйnew долженвозвращатьправильноезначениеикорректнообрабатывать запросынавыделениенулябайтов. Такжеприреализациисобственныхфункцийуправленияпамятьюмыдолжныпозаботитьсяотом, чтобынескрытьих«нормальные» формы. Ну, атеперьобовсемэтом подробнее.
Соглашения при написании оператора new
Первымделомпользовательскийnew долженвозвращатьправильноезначение. Вслучаеуспешноговыделенияпамятиоператордолженвернутьуказательнанее. Еслижечто-топошлонетак, следует возбудитьисключениетипаbad_alloc.
Ноневсетакпросто, каккажется. Передисключениемnew долженв циклевызыватьфункцию-обработчик, котораяпопытаетсяразрешить проблемнуюситуацию. Чтодолжнаделатьэтафункция, мыподробно рассмотреливпрошлойстатье. Сейчасялишьнапомню, чтоонаможетвысвободитьзаранеезаготовленныйрезервпамятивслучаеее нехватки, самавозбудитьисключениеиливовсезавершитьпрограмму. Крайневажно, чтобыфункция-обработчиккорректноотработала, посколькуциклеевызовабудетвыполнятьсядотехпор, поканебудет разрешенаконфликтнаяситуация. Следующийважныймомент, которыймыдолжныучитывать— этообработказапросовнавыделение
нулябайтпамяти. Какнистранноэтозвучит, ностандартC++ требуетв этомслучаекорректнойработыоператора. Такоеповедениеупрощает реализациюнекоторыхвещейвдругихместахязыка. Принимаяво вниманиевсеэто, можнопопробоватьнакидатьпсевдокодпользовательскогоnew:
Псевдокодпользовательскойреализацииоператораnew
void *operator new(std::size_t size) throw(std::bad_alloc)
{
using namespace std;
//обработать запрос на 0 байтов,
//считая, что нужно выделить 1 байт if (size == 0)
size = 1;
while(true)
{
// попытка выделить size байтов;
if (выделить удалось)
return (указатель на память);
//выделить память не удалось
//проверить, установлена ли функция-обработчик new_handler globalHandler = set_new_handler(0); set_new_handler(globalHandler);
if (globalHandler) (*globalHandler) ();
else
throw std::bad_alloc();
}
}
112 |
XÀÊÅÐ 09 /140/ 10 |
|
|
|
|
hang |
e |
|
|
|
|
|
|
|
|
C |
|
E |
|
|
|||
|
|
X |
|
|
|
|
|
|||
|
- |
|
|
|
|
|
d |
|
||
|
F |
|
|
|
|
|
|
t |
|
|
|
D |
|
|
|
|
|
|
|
i |
|
|
|
|
|
|
|
|
|
r |
||
P |
|
|
|
|
|
NOW! |
o |
|||
|
|
|
|
BUY |
|
|
||||
|
|
|
|
|
|
|
||||
|
|
|
|
to |
|
|
|
|
|
|
w Click |
|
|
|
|
|
m |
||||
|
|
|
|
|
|
|||||
w |
|
|
|
|
|
|
|
|
|
|
|
w |
|
|
|
|
|
|
|
o |
|
|
. |
|
|
|
|
|
.c |
|
||
|
|
p |
|
|
|
|
g |
|
|
|
|
|
|
df |
|
|
n |
e |
|
||
|
|
|
|
-xcha |
|
|
|
|
|
|
|
|
hang |
e |
|
|
|
|
|
|
|
|
C |
|
E |
|
|
|||
|
|
X |
|
|
|
|
|
|||
|
- |
|
|
|
|
|
d |
|
||
|
F |
|
|
|
|
|
|
t |
|
|
|
D |
|
|
|
|
|
|
|
i |
|
|
|
|
|
|
|
|
|
r |
||
P |
|
|
|
|
|
NOW! |
o |
|||
|
|
|
|
|
|
|
||||
|
|
|
|
|
BUY |
|
|
|||
|
|
|
|
to |
|
|
|
|
|
|
w Click |
|
|
|
|
|
m |
||||
|
|
|
|
|
|
|||||
w |
|
|
|
|
|
|
|
|
|
|
|
w |
|
|
|
|
|
|
|
o |
|
|
. |
|
|
|
|
|
.c |
|
||
|
|
p |
|
|
|
|
g |
|
|
|
|
|
|
df |
|
|
n |
e |
|
||
|
|
|
|
-x cha |
|
|
|
|
Особочувствительныхможетсмутитьвыделениеодногобайтапамяти тогда, когдаунасзапрашиваютноль. Да, этогрубо, нозатоработает. Деловтом, чтомыдолжнывернутькорректныйуказательдажетогда, когдаунаспросят0 байтов, поэтомуприходитсяизобретать. Ичем прощебудетизобретение, темононадежней.
Такжесомнительнойможетпоказатьсяустановкауказателянаобработчикnew внулевоезначениеспоследующимеговосстановлением. Ксожалению, унаснетдругогоспособаполучитьадрестекущей функции-обработчика. Намнужнопроверитьэтотадрес, и, еслион нулевой, возбудитьисключениетипаbad_alloc.
Ещеразповторюсь, чтооператорnew вслучаепроблемсвыделением памятивбесконечномциклевызываетфункцию-обработчик. Очень важно, чтобыкодэтойфункциикорректноразрешалпроблему: сделал доступнойпамятибольше, возбудилисключениетипа, производного отbad_alloc, установилдругойобработчик, убралтекущийобработчик илиневозвращалуправлениевовсе. Впротивномслучаепрограмма, вызвавшаянашуверсиюnew, зависнет.
Отдельноследуетрассмотретьслучай, когдаnew являетсяфункцией- членомкакого-либокласса. Обычнопользовательскиеверсии операторовработыспамятьюпишутсядляболееоптимизированного распределенияпамяти. Например, new дляклассаBase заточенпод выделениепамятиобъемомsizeof(Base) — нибольше, нименьше. Ночтобудет, еслимысоздадимкласс, которыйнаследуетсяотBase? Вдочернемклассетакжебудетиспользоватьсяверсияоператора new, определенноговBase. Норазмернаследуемогокласса(назовем егоDerived), скореевсего, будетотличатьсяотразмерабазового: sizeof(Derived) != sizeof(Base). Из-заэтоговсяпользаотсобственной реализацииnew можетсойтинанет. Очем, кстати, многиезабываюти испытываютпотомнечеловеческиестрадания.
Проблеманаследованияоператораnew
class Base { public:
static void *operator new (std::size_t size) throw(std::bad_alloc);
...
};
// в подклассе не объявлен оператор new class Derived: public Base
{...};
//вызывается Base::operator new Derived *p = new Derived;
Решитьпроблемудостаточнопросто, ноделатьэтонадозаранее. Достаточновбазовомклассе, втелеоператораnew, выполнятьпроверкуразмеравыделяемойпамяти. Есликоличествозапрашиваемых байтовнесовпадаетсколичествомбайтоввобъектеклассаBase, то работуповыделениюпамятилучшевсегопередатьстандартнойреализацииnew. Ктомуже, такмысразужерешаемвопроссобработкой запросанавыделениенулябайтовпамяти— этимужебудетзаниматьсяштатнаяверсияоператора.
Решениепроблемынаследованияоператораnew
void *operator new (std::size_t size) throw(std::bad_alloc)
{
// если size неправильный, вызвать стандартный new
if(size != sizeof(Base)) return ::operator new(size);
// в противном случае обработать запрос
...
}
Науровнеклассаможнотакжеопределитьnew длямассивов (operator new[]). Этотоператорнедолженничегоделать, кромекак выделятьблокнеформатированнойпамяти. Мынеможемсовер- шатькакие-либооперациисещенесозданнымиобъектами. Даи, к томуже, намнеизвестенразмерэтихобъектов, ведьонимогутбыть наследникамикласса, вкоторомопределенnew[]. Тоестьколичествообъектоввмассивенеобязательноравно(запрошенноечисло байтов)/sizeof(Base). Болеетого, длядинамическихмассивовможет выделятьсябольшееколичествопамяти, чемзаймутсамиобъекты, дляобеспечениярезерва.
Соглашения при написании оператора delete
Чтокасаетсяоператораdelete, тотутвсегораздопроще. Основная гарантия, которуюмыдолжныпредоставить— этобезопасностьосвобожденияпамятипонулевомуадресу. Сучетомэтогопсевдокодdelete будетвыглядетьтак:
Псевдокодпользовательской реализацииоператораdelete
void *operator delete (void *rawMemory) throw()
{
//если нулевой указатель, ничего не делать if(rawMemory == 0) return;
//освободить память, на которую указывает rawMemory;
}
Еслиоператорdelete являетсяфункцией-членомкласса, то, какив случаесnew, следуетпозаботитьсяопроверкеразмераудаляемой памяти. Еслипользовательскаяреализацияnew дляклассаBase выделилаsizeof(Base) байтовпамяти, тоисамописныйdelete должен освободитьровностолькожебайтов. Впротивномслучае, еслиразмерудаляемойпамятинесовпадаетсразмеромкласса, вкотором определеноператор, следуетпередатьвсюработустандартному delete.
Псевдокодфункции-членаdelete
class Base { public:
static void *operator new (std::size_t size) throw(std::bad_alloc);
static void *operator delete
(void *rawMemory, std::size_t size) throw();
...
};
void* Base::operator delete (void *rawMemory, std::size_t size) throw()
{
XÀÊÅÐ 09 /140/ 10 |
113 |