Не получайте доступ к объекту за пределами его жизни
Не получайте доступ к объекту за пределами его жизни.[1]
Эта проверка проверяет на наличие следующих проблем:
Неинициализированный указатель.
Неинициализированная переменная.
Использование ранее освобожденного указателя.
Указатель или ссылка на переменную стека, покидающую возможности.
Доступ к объекту с временным сроком службы.
Неинициализированный указатель возникает, когда указателю не назначается адрес перед отсрочкой.
Если указателю явно не назначен адрес, он указывает на непредсказуемое местоположение.
Исправление зависит от первопричины дефекта. Например, вы назначили адрес указателю, но назначение недоступно.
Часто детали результата показывают последовательность событий, которые привели к дефекту. Вы можете реализовать исправление на любом событии в последовательности. Если сведения о результате не отображают историю событий, можно отследить их с помощью опций правого щелчка в исходном коде и просмотреть предыдущие связанные события. Смотрите также Результаты интерпретации Bug Finder в интерфейсе пользователя Polyspace Desktop.
См. примеры исправлений ниже. Рекомендуется инициализировать указатель на NULL при объявлении указателя.
Если вы не хотите устранять проблему, добавьте комментарии к своему результату или коду, чтобы избежать другой проверки. Смотрите Адрес Результаты Polyspace через исправления ошибок или обоснования.
#include <stdlib.h>
int* assign_pointer(int* prev)
{
int j = 42;
int* pi;
if (prev == nullptr)
{
pi = new int;
if (pi == nullptr) return NULL;
}
*pi = j;
/* Defect: Writing to uninitialized pointer */
return pi;
}
Если prev
не nullptr
, указатель pi
не присваивается адрес. Однако pi
определяется на каждом пути выполнения независимо от того, prev
является nullptr
или нет.
Одной из возможных коррекций является назначение адреса pi
когда prev
не nullptr
. Также инициализируйте pi
как nullptr
во время его объявления.
#include <cstdlib> int* assign_pointer(int* prev) { int j = 42; /*Fix: Initialize pointers by using nullptr during declaration*/ int* pi = nullptr; if (prev == NULL) { pi = new int; if (pi == nullptr) return NULL; } /* Fix: Initialize pi in branches of if statement */ else pi = prev; *pi = j; return pi; }
Неинициализированная переменная возникает, когда переменная не инициализируется до чтения ее значения.
Если переменная не инициализирована явным образом, значение переменных непредсказуемо. Вы не можете полагаться на переменную, имеющую определенное значение.
Исправление зависит от первопричины дефекта. Например, вы присвоили значение переменной, но присвоение недоступно, или вы присвоили значение переменной в одной из двух ветвей условного оператора. Исправьте недостижимый код или отсутствующее назначение.
Часто детали результата показывают последовательность событий, которые привели к дефекту. Вы можете реализовать исправление на любом событии в последовательности. Если сведения о результате не отображают историю событий, можно отследить их с помощью опций правого щелчка в исходном коде и просмотреть предыдущие связанные события. Смотрите также Результаты интерпретации Bug Finder в интерфейсе пользователя Polyspace Desktop.
См. примеры исправлений ниже. Рекомендуется инициализировать переменную при объявлении.
Если вы не хотите устранять проблему, добавьте комментарии к своему результату или коду, чтобы избежать другой проверки. Смотрите Адрес Результаты Polyspace через исправления ошибок или обоснования.
int get_sensor_value(void)
{
extern int getsensor(void);
int command;
int val;
command = getsensor();
if (command == 2)
{
val = getsensor();
}
return val;
/* Defect: val does not have a value if command is not 2 */
}
Если command
не 2, переменная val
не назначен. В этом случае возврат значение функции get_sensor_value
не определено.
Одной из возможных коррекций является инициализация val
во время объявления так, чтобы инициализация не обходилась по некоторым путям выполнения.
int get_sensor_value(void) { extern int getsensor(void); int command; /* Fix: Initialize val */ int val=0; command = getsensor(); if (command == 2) { val = getsensor(); } return val; }
val
присваивается начальное значение 0. Когда command
не равно 2, функция get_sensor_value
возвращает это значение.
Использование ранее освобожденного указателя происходит, когда вы получаете доступ к блоку памяти после удаления блока, например, при помощи free
функцию или delete
оператор.
Когда указателю выделяется динамическая память при помощи функций malloc
, calloc
, realloc
или оператор new
, это указывает на место памяти на куче. Когда вы используете free
функцию или delete
оператор на этом указателе, связанный блок памяти отменяется. Попытка получить доступ к этому блоку памяти может привести к непредсказуемому поведению или даже отказу сегментации.
Исправление зависит от первопричины дефекта. Посмотрите, намеревались ли вы позже отменить выделение памяти или выделить указателю другой блок памяти перед доступом.
Как хорошая практика, после удаления блока памяти назначьте соответствующий указатель на nullptr
. Перед снятием указателей проверьте, nullptr
ли они и обработайте ошибку. Таким образом, вы защищены от доступа к освобожденному блоку.
#include <cstdlib>
int increment_content_of_address(int base_val, int shift)
{
int j;
int* pi = new int;
if (pi == NULL) return 0;
*pi = base_val;
delete pi;
j = *pi + shift;
/* Defect: Reading a deallocated pointer */
return j;
}
The delete
оператор освобождает блок памяти, который pi
относится к. Поэтому разыменование pi
после delete pi;
недопустимый оператор.
Одной из возможных коррекций является отмена выделения указателя pi
только после последнего образца, к которому осуществляется доступ.
#include <cstdlib> int increment_content_of_address(int base_val, int shift) { int j; int* pi = new int; if (pi == NULL) return 0; *pi = base_val; j = *pi + shift; *pi = 0; /* Fix: The pointer is deallocated after its last use */ delete pi; return j; }
std::unique_ptr
Другой возможной коррекцией является использование std::unique_ptr
вместо необработанного указателя. Умные указатели, такие как std::unique_ptr
управляет собственными ресурсами. поскольку вы не должны явным образом отменять выделение смарт-указателей, доступ к ним не осуществляется непреднамеренно после удаления.
#include <cstdlib> #include <memory> int increment_content_of_address(int base_val, int shift) { int j; /* Fix: A smart pointer is used*/ std::unique_ptr<int> pi(new int(3)); if (pi == nullptr) return 0; *pi = base_val; j = *pi + shift; *pi = 0; return j; }
Указатель или ссылка на переменную стека, покидающую возможности, происходит, когда указатель или ссылка на локальную переменную покидает возможности переменной. Для образца:
Функция возвращает указатель на локальную переменную.
Функция выполняет назначение globPtr = &locVar
. globPtr
является переменной глобального указателя и locVar
является локальной переменной.
Функция выполняет назначение *paramPtr = &locVar
. paramPtr
является параметром функции, который, для образца, является int**
указатель и locVar
является локальным int
переменная.
Метод C++ выполняет назначение memPtr = &locVar
. memPtr
является представителем данных указателя класса, которому принадлежит метод. locVar
является локальной переменной метода.
Дефект также относится к памяти, выделенной с помощью alloca
функция. Дефект не применяется к статическим локальным переменным.
Локальным переменным выделяется адрес в стеке. Когда возможности локальной переменной заканчиваются, этот адрес доступен для повторного использования. Использование этого адреса для доступа к локальному значению переменных вне переменных возможностей может вызвать неожиданное поведение.
Если указатель на локальную переменную покидает возможности переменной, Polyspace® Bug Finder™ подсвечивает дефект. Дефект появляется, даже если вы не используете адрес, сохраненный в указателе. Для поддерживаемого кода рекомендуется не позволять указателю покидать возможности переменной. Даже если вы не используете адрес в указателе сейчас, кто-то другой, использующий вашу функцию, может использовать адрес, вызывая неопределенное поведение.
Не разрешайте указателю или ссылке на локальную переменную покидать возможности переменной.
void func2(int *ptr) {
*ptr = 0;
}
int* func1(void) {
int ret = 0;
return &ret ;
}
void main(void) {
int* ptr = func1() ;
func2(ptr) ;
}
В этом примере func1
возвращает указатель на локальную переменную ret
.
В main
, ptr
указывает на адрес локальной переменной. Когда ptr
доступ осуществляется в func2
, доступ запрещен, потому что возможности ret
ограничивается func1
,
auto createAdder(int amountToAdd) {
int addThis = amountToAdd;
auto adder = [&] (int initialAmount) {
return (initialAmount + addThis);
};
return adder;
}
void func() {
auto AddByTwo = createAdder(2);
int res = AddByTwo(10);
}
В этом примере createAdder
функция задает лямбда-выражение adder
который захватывает локальную переменную addThis
по ссылке. Область возможностей addThis
ограничивается createAdder
функция. Когда объект вернулся createAdder
вызывается, ссылка на переменную addThis
доступ к нему за пределами его возможностей. При доступе таким способом значение addThis
не определено.
Если функция возвращает объект выражения лямбды, избегайте захвата локальных переменных путем ссылки в объекте лямбды. Вместо этого соберите переменные путем копирования.
Переменные, захваченные копией, имеют то же время жизни, что и лямбда- объект, но переменные, захваченные ссылкой, часто имеют меньшее время жизни, чем сама лямбда объекта. Когда используется объект lambda, эти переменные, доступные вне возможностей, имеют неопределенные значения.
auto createAdder(int amountToAdd) { int addThis = amountToAdd; auto adder = [=] (int initialAmount) { return (initialAmount + addThis); }; return adder; } void func() { auto AddByTwo = createAdder(2); int res = AddByTwo(10); }
Доступ к объекту с временным временем жизни происходит при попытке считать или записать в объект с временным временем жизни, который возвращается вызовом функции. В структуре или объединении, возвращенном функцией и содержащем массив, представители массива являются временными объектами. Срок жизни временных объектов заканчивается:
Когда полное выражение или полный декларатор, содержащий вызов, заканчивается, как определено в C11 Standard.
После следующей точки последовательности, как определено в C90 и C99 стандартах. Точка последовательности является точкой выполнения программы, где все предыдущие вычисления завершены, и последующая оценка еще не началась.
Для кода С++ объект Accessing с временным временем жизни поднимает дефект только тогда, когда вы записываете в объект с временным временем жизни.
Если временный объект жизни возвращается по адресу, дефект не возникает.
Изменение объектов с временным временем жизни является неопределенным поведением и может вызвать ненормальное завершение программы и проблемы переносимости.
Присвойте объект, возвращенный из вызова функции, локальной переменной. Содержимое объекта временного срока службы копируется в переменную. Теперь вы можете изменять его безопасно.
#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#define SIZE6 6
struct S_Array
{
int t;
int a[SIZE6];
};
struct S_Array func_temp(void);
/* func_temp() returns a struct value containing
* an array with a temporary lifetime.
*/
int func(void) {
/*Writing to temporary lifetime object is
undefined behavior
*/
return ++(func_temp().a[0]);
}
void main(void) {
(void)func();
}
В этом примере func_temp()
возвращает по значению структуру с представителем массива a
. У этот представитель есть временное время жизни. Его увеличение является неопределенным поведением.
Одной из возможных коррекций является назначение возврата вызова func_temp()
в локальную переменную. Содержимое временного объекта a
копируется в переменную, которую можно безопасно увеличить.
#include <stdio.h> #include <assert.h> #include <stdlib.h> #include <string.h> #define SIZE6 6 struct S_Array { int t; int a[SIZE6]; }; struct S_Array func_temp(void); int func(void) { /* Assign object returned by function call to *local variable */ struct S_Array s = func_temp(); /* Local variable can safely be *incremented */ ++(s.a[0]); return s.a[0]; } void main(void) { (void)func(); }
Группа: 02. Выражения (EXP) |
[1] Это программное обеспечение было создано MathWorks, включающее фрагменты: «Сайт SEI CERT-C», © 2017 Университет Карнеги Меллон, Веб-сайт SEI CERT-C + + © 2017 Университет Карнеги Меллон, "Стандарт кодирования SEI CERT C - Правила разработки безопасных, Надежные и безопасные системы - 2016 Edition ", © 2016 Университет Карнеги Меллон, и "Стандарт кодирования SEI CERT C++ - Правила разработки безопасных, Надежные и безопасные системы в C++ - 2016 Edition "© 2016 Университет Карнеги Меллон, с специального разрешения от его Института программной инженерии.
ЛЮБОЙ МАТЕРИАЛ УНИВЕРСИТЕТА КАРНЕГИ МЕЛЛОН И/ИЛИ ЕГО ИНЖЕНЕРНОГО ИНСТИТУТА ПРОГРАММНОГО ОБЕСПЕЧЕНИЯ, СОДЕРЖАЩИЙСЯ В НАСТОЯЩЕМ ДОКУМЕНТЕ, ПОСТАВЛЯЕТСЯ НА БАЗИСЕ «КАК ЕСТЬ». УНИВЕРСИТЕТ КАРНЕГИ МЕЛЛОН НЕ ДАЕТ НИКАКИХ ГАРАНТИЙ, ВЫРАЖЕННЫХ ИЛИ ПОДРАЗУМЕВАЕМЫХ, В ОТНОШЕНИИ ЛЮБОГО ВОПРОСА, ВКЛЮЧАЯ, НО НЕ ОГРАНИЧИВАЯСЬ, ГАРАНТИЮ ПРИГОДНОСТИ ДЛЯ ЦЕЛЕЙ ИЛИ КОММЕРЧЕСКОЙ ВЫГОДЫ, ИСКЛЮЧИТЕЛЬНОСТИ, ИЛИ УНИВЕРСИТЕТ КАРНЕГИ МЕЛЛОН НЕ ДАЕТ НИКАКИХ ГАРАНТИЙ В ОТНОШЕНИИ СВОБОДЫ ОТ ПАТЕНТА, ТОВАРНОГО ЗНАКА ИЛИ НАРУШЕНИЯ АВТОРСКИХ ПРАВ.
Это программное обеспечение и связанная с ним документация не были рассмотрены и не одобрены Университетом Карнеги-Меллон или его Институтом программной инженерии.
1. Если смысл перевода понятен, то лучше оставьте как есть и не придирайтесь к словам, синонимам и тому подобному. О вкусах не спорим.
2. Не дополняйте перевод комментариями “от себя”. В исправлении не должно появляться дополнительных смыслов и комментариев, отсутствующих в оригинале. Такие правки не получится интегрировать в алгоритме автоматического перевода.
3. Сохраняйте структуру оригинального текста - например, не разбивайте одно предложение на два.
4. Не имеет смысла однотипное исправление перевода какого-то термина во всех предложениях. Исправляйте только в одном месте. Когда Вашу правку одобрят, это исправление будет алгоритмически распространено и на другие части документации.
5. По иным вопросам, например если надо исправить заблокированное для перевода слово, обратитесь к редакторам через форму технической поддержки.