Как работает аннотация _Pre_defensive_?
-
21-12-2019 - |
Вопрос
Итак, я достаточно хорошо знаком с использованием языка аннотаций исходного кода Microsoft (вариант VS 2012-2013) для описания контрактов функций с указателями.
Однако меня интересует одна вещь: я ожидал получить другой результат при _In_ _Pre_defensive_
чем без него для случаев, когда аннотированный вызываемый объект сначала не проверяет указатель.[Многие из наших устаревших функций ожидают допустимых входных данных для этих параметров, но политика заключается в двойной проверке.] Существует ли ошибка статического анализа для описания функции, помеченной как защитная, но не защищающей себя?
Из документы,
Если функция появляется на границе доверия, мы рекомендуем использовать аннотацию _Pre_defensive_.Модификатор «защитный» изменяет некоторые аннотации, указывая, что в момент вызова интерфейс должен строго проверяться, но в теле реализации следует предполагать, что могут быть переданы неправильные параметры.В таком случае, В _Pre_defensive_ предпочтительнее на границе доверия, чтобы указать, что, хотя вызывающая сторона получит ошибку, если попытается передать NULL, тело функции будет проанализировано, как если бы параметр мог быть NULL, и любые попытки отменить ссылку на указатель без предварительной проверки оно будет отмечено как NULL.
Вот небольшая демонстрационная программа для анализа кода.Все 4 мои функции показывают C6387 при статическом анализе, но я надеялся увидеть дополнительное указание на то, что моя «защитная» функция на самом деле не проверяла входные данные, как в fun0()
.Есть ли какая-либо польза от добавления этого квалификатора?Это действительно загромождает декларацию, поэтому, если это не помогает, трудно оправдать ее размещение.
// SAL2013.cpp : Defines the entry point for the console application.
#include "stdafx.h"
// pre-defensive annotation, but no check
void fun0(_In_ _Pre_defensive_ int* pInt)
{
auto oops = *pInt;
}
// not defensive, no check
void fun1(_In_ int* pInt)
{
auto oops = *pInt;
}
// defensive check, but not annotated defensively
void fun2(_In_ int* pInt)
{
if (pInt != nullptr)
{
auto oops = *pInt;
}
}
// defensive, with annotation
void fun3(_In_ _Pre_defensive_ int* pInt)
{
if (pInt != nullptr)
{
auto oops = *pInt;
}
}
int _tmain(int argc, _TCHAR* argv[])
{
int* p = nullptr;
fun0(p); // C6387 Invalid Parameter Value
fun1(p); // C6387 Invalid Parameter Value
fun2(p); // C6387 Invalid Parameter Value
fun3(p); // C6387 Invalid Parameter Value
return 0;
}
Бонусный вопрос:Я вижу в sal.h есть _In_defensive_(annotes)
аннотация тоже, но я не понимаю, как ее использовать.
Спасибо.
Решение
AFAICT в настоящее время нет (публичной) разницы при добавлении _defensive_
аннотации.Однако у Microsoft есть дополнительные и расширенные анализаторы, которые они используют внутри себя.Они могут использовать аннотации, а также могут быть опубликованы в будущем.
Так что это компромисс.Если вы считаете, что они слишком загромождают декларацию, нет никакого вреда в их удалении (хотя учтите, что через _Use_decl_annotations_
вам нужно только поместить аннотацию в шапку).С другой стороны, в будущем это может быть использовано, а также может быть использовано как документация по целевому использованию.
РЕДАКТИРОВАТЬ:Что касается _In_defensive_(annotes)
, это позволяет применять _Pre_defensive_
аннотация ко всем аннотациям (приведена в annotes
).Также это позволяет разместить аннотацию в другом месте, т.е.
_In_defensive(_Pre_satisfies_(pInt != nullptr))
void fun3(int* pInt)
{
}