다음을 통해 공유


SAL 이해

Microsoft 소스 코드 주석 언어 (SAL)는 함수 매개 변수, 알아 있도록 가정 및 종료 될 때에 게 보장 사용 하는 방법에 대해 설명 하는 데 사용할 수 있는 주석을 제공 합니다.주석을 헤더 파일에 정의 된 <sal.h>.SAL 주석을 c + +에 코드 분석을 Visual Studio를 사용 하 여 분석 기능을 수정 합니다.Windows 드라이버 개발에 대 한 SAL 2.0에 대 한 자세한 내용은 참조 하십시오 SAL 2.0 주석에 대 한 Windows 드라이버.

기본적으로, C 및 c + + 개발자에 게 일관 되 게 도와 불변 익스프레스 제한적인된 방법 으로만 제공 합니다.SAL 주석을 사용 하 여 개발자가 사용 하는 사용 방법을 더 잘 이해할 수 있도록 함수에 자세히 설명할 수 있습니다.

SAL 무엇 이며 왜 사용 해야?

간단히 말해, SAL 컴파일러가 코드를 검사할 수 있도록 하는 저렴 한 방법입니다.

Hh916383.collapse_all(ko-kr,VS.110).gifSAL 코드 보다 중요 한 게

SAL은 인간이 대 한 코드 분석 도구에 대 한 코드 디자인을 좀 더 이해 하기 쉽게 만들기 하는 데 도움이 됩니다.C 런타임 함수를 보여 줍니다.이 예제에서는 memcpy.

void * memcpy(
   void *dest, 
   const void *src, 
   size_t count
);

이 함수의 용도 알 수 있습니다?함수 구현 되거나 호출 되 면 특정 속성 프로그램 올바른지 확인 하기 위해 유지 되어야 합니다.방금 예제에서와 같은 선언을 살펴보면 들은 알 수 없습니다.SAL 주석을 사용 하지 않고 문서 또는 주석을 코드에 의존 해야 합니다.다음은 MSDN 설명서에 대 한 memcpy 라는:

"복사 대상으로 src의 바이트 수를 계산 하는 데 사용 됩니다.원본과 대상이 겹칠 경우 memcpy의 동작이 정의 되지 않습니다.Memmove를 사용 하 여 겹치는 영역을 처리 합니다.보안 참고: 크기 또는 소스 버퍼 보다 큰 있는지 대상 버퍼에 일치 하는지 확인 합니다.참조에 대 한 자세한 내용은 버퍼 오버런을 방지 하십시오. "

설명서에는 몇을 개의 비트 정보 코드 프로그램 올바른지 확인 하기 위해 특정 속성을 유지 해야 하는 것이 좋습니다 포함 되어 있습니다.

  • memcpy복사본은 count 대상 버퍼를 소스 버퍼에서 바이트 수입니다.

  • 대상 버퍼를 소스 버퍼 적어도 큰 있어야 합니다.

그러나 컴파일러는 설명서를 참조 하거나 비공식적인 의견을 읽을 수 없습니다.두 버퍼 간의 관계 임을 알 수 없는 및 count, 고 것도 효과적으로 관계에 대 한 추측 수 없습니다.SAL 속성 및 함수를 구현 하는 방법에 대 한 더욱 명료 하 게 다음과 같이 제공할 수 있습니다.

void * memcpy(
   _Out_writes_bytes_all_(count) void *dest, 
   _In_reads_bytes_(count) const void *src, 
   size_t count
);

정보는 MSDN 설명서에 이러한 주석을 유사 하지만 보다 간결 하 게 하는 의미 하는 패턴에 따라 표시 됩니다.이 코드를 읽을 때이 함수의 속성 및 버퍼 오버런 보안 문제를 방지 하는 방법을 신속 하 게 이해할 수 있습니다.더욱 우수한 SAL 제공 의미 패턴 자동화 된 코드 분석 도구에서 잠재적인 버그의 초기 검색의 효율성을 향상 시킬 수 있습니다.누군가가이 버그가 있는 구현을 작성 하는 경우를 가정해 봅니다 wmemcpy.

wchar_t * wmemcpy(
   _Out_writes_all_(count) wchar_t *dest, 
   _In_reads_(count) const wchar_t *src, 
   size_t count)
{
   size_t i;
   for (i = 0; i <= count; i++) { // BUG: off-by-one error
      dest[i] = src[i];
   }
   return dest;
}

이 구현은 일반적인 해제-하나씩 오류가 포함 되어 있습니다.다행히 SAL 버퍼 크기 주석 코드 작성자를 포함, 코드 분석 도구에만이 함수를 분석 하 여 버그를 처리 하지 못했습니다.

Hh916383.collapse_all(ko-kr,VS.110).gifSAL의 기초

SAL 네 가지 기본 유형의 사용 패턴으로 분류 되는 매개 변수를 정의 합니다.

범주

매개 변수 주석

설명

입력으로 함수를 호출합니다.

_In_

데이터 호출된 함수에 전달 되 고 읽기 전용으로 취급 됩니다.

입력으로 함수를 호출 하 고 호출자에 게 출력

_Inout_

사용할 수 있는 데이터 함수에 전달 된 및 잠재적으로 수정 됩니다.

호출자에 게 출력

_Out_

호출자만 호출된 되는 함수를 작성할 수 있습니다.호출된 된 함수가 해당 공간에 데이터를 씁니다.

호출자에 게 포인터를 출력

_Outptr_

다음과 같이 호출자에 게 출력.호출된 된 함수에서 반환 되는 값에 대 한 포인터입니다.

이러한 네 가지 기본 주석은 다양 한 방식으로 명시적 더 만들 수 있습니다.기본적으로 주석이 추가 된 포인터 매개 변수가 필요한 것으로 간주 됩니다-는 NULL 이어야 합니다 성공 하는 함수에 대 한.가장 일반적으로 사용 되는 변형을 기본 주석의 포인터 매개 변수가 선택적 임을 나타냅니다-NULL 이면 함수 여전히 해당 작업 성공할 수 있습니다.

이 테이블 간의 필수 및 선택적 매개 변수를 구분 하는 방법을 보여 줍니다.

매개 변수는 필수입니다.

매개 변수는 선택 사항입니다.

입력으로 함수를 호출합니다.

_In_

_In_opt_

입력으로 함수를 호출 하 고 호출자에 게 출력

_Inout_

_Inout_opt_

호출자에 게 출력

_Out_

_Out_opt_

호출자에 게 포인터를 출력

_Outptr_

_Outptr_opt_

이러한 주석을 사용할 수 있는 초기화 되지 않은 값을 확인할 하 고 공식적이 고 정확한 방식으로 잘못 된 null 포인터를 사용 합니다.필수 매개 변수에 NULL을 전달에 충돌이 발생할 수 있습니다 또는 "실패" 오류 반환 코드를 발생할 수 있습니다.어느 쪽이 든 해당 작업 과정에서 함수를 계속할 수 없습니다.

SAL 예제

이 단원의 코드 예제를 기본 SAL 주석 보여 줍니다.

Hh916383.collapse_all(ko-kr,VS.110).gifVisual Studio 코드 분석 도구를 사용 하 여 오류를 찾으려면

예제에서는 Visual Studio 코드 분석 도구가 코드 오류를 찾기 위해 SAL 주석은 함께 사용 됩니다.다음은 이렇게 하는 방법입니다.

Visual Studio 코드 분석 도구와 SAL를 사용 합니다.

  1. Visual Studio SAL 주석이 포함 된 c + + 프로젝트를 엽니다.

  2. 메뉴 모음에서 선택 빌드, 솔루션에 대해 코드 분석 실행.

    이 섹션에서는 _In_ 예제를 살펴보십시오.코드 분석을 실행 하는 경우이 경고가 표시 됩니다.

    C6387 잘못 된 매개 변수 값'pInt' '0' 될 수 있습니다:이 함수 'InCallee'에 대 한 사양을 따르지 않습니다.

Hh916383.collapse_all(ko-kr,VS.110).gif예: _In_ 주석

_In_ 주석을 나타냅니다.

  • 매개 변수가 유효 해야 하며 수정 되지 않습니다.

  • 만 함수는 단일 요소 버퍼에서 읽습니다.

  • 호출자는 버퍼를 제공 하 고 초기화 해야 합니다.

  • _In_"읽기 전용"으로 지정합니다.적용 하는 것이 일반적인 실수로 _In_ 에 있어야 하는 매개 변수는 _Inout_ 주석 대신.

  • _In_하지만 아닌 스칼라에서 분석기를 무시 하는 것이 가능 합니다.

void InCallee(_In_ int *pInt)
{
   int i = *pInt;
}

void GoodInCaller()
{
   int *pInt = new int;
   *pInt = 5;

   InCallee(pInt);
   delete pInt;   
}

void BadInCaller()
{
   int *pInt = NULL;
   InCallee(pInt); // pInt should not be NULL
}

이 예제에서 Visual Studio 코드 분석을 사용 하는 경우 호출자는 Null이 아닌 포인터를 초기화 버퍼에 대 한 전달 확인 pInt.이 경우 pInt 포인터는 NULL 일 수 없습니다.

Hh916383.collapse_all(ko-kr,VS.110).gif예: _In_opt_ 주석

_In_opt_같은 _In_따라서 함수가이를 확인 해야 하 고 입력된 매개 변수는 NULL이 될 수 있는 점을 제외 하 고는 합니다.

void GoodInOptCallee(_In_opt_ int *pInt)
{
   if(pInt != NULL) {
      int i = *pInt;
   }
}

void BadInOptCallee(_In_opt_ int *pInt)
{
   int i = *pInt; // Dereferencing NULL pointer ‘pInt’
}

void InOptCaller()
{
   int *pInt = NULL;
   GoodInOptCallee(pInt);
   BadInOptCallee(pInt);
} 

Visual Studio 코드 분석 함수에 대해 NULL 버퍼에 액세스 하기 전에 확인을 확인 합니다.

Hh916383.collapse_all(ko-kr,VS.110).gif예: _Out_ 주석

_Out_요소는 버퍼를 가리키는 NULL 포인터에 전달 되 고 함수는 요소를 초기화 하는 일반적인 시나리오를 지원 합니다.호출자가 호출 하기 전에 버퍼를 초기화 하지. 호출된 된 함수가 반환 되기 전에 초기화를 해줍니다.

void GoodOutCallee(_Out_ int *pInt)
{
   *pInt = 5;
}

void BadOutCallee(_Out_ int *pInt)
{
   // Did not initialize pInt buffer before returning!
}

void OutCaller()
{
   int *pInt = new int;
   GoodOutCallee(pInt);
   BadOutCallee(pInt);
   delete pInt;
} 

Visual Studio 코드 분석 도구 검사는 호출자가 NULL이 아닌 포인터가 버퍼를 전달 하는 pInt 및 반환 하기 전에 해당 함수에 의해 버퍼를 초기화 합니다.

Hh916383.collapse_all(ko-kr,VS.110).gif예: _Out_opt_ 주석

_Out_opt_같은 _Out_매개 변수는 NULL이 될 수 및 따라서 함수가이를 확인 해야 한다는 점을 제외 하면.

void GoodOutOptCallee(_Out_opt_ int *pInt)
{
   if (pInt != NULL) {
      *pInt = 5;
   }
}

void BadOutOptCallee(_Out_opt_ int *pInt)
{
   *pInt = 5; // Dereferencing NULL pointer ‘pInt’
}

void OutOptCaller()
{
   int *pInt = NULL;
   GoodOutOptCallee(pInt);
   BadOutOptCallee(pInt);
} 

Visual Studio 코드 분석의 유효성을 검사 하기 전에 null이 함수를 확인 합니다 pInt 역참조 될 경우 pInt NULL 반환 하기 전에 해당 함수에 의해 버퍼 초기화 된다는 아닙니다.

Hh916383.collapse_all(ko-kr,VS.110).gif예: _Inout_ 주석

_Inout_해당 함수에 의해 변경 될 수 있습니다는 포인터 매개 변수에 주석을 지정 하는 데 사용 됩니다.포인터가 잘못 초기화 된 데이터를 호출 하기 전에 가리켜야 하 고 변경 하는 경우에 여전히 유효한 값 반환이 있어야 합니다.주석 함수에서 읽을 수 있습니다 자유롭고 요소가 하나인 버퍼에 쓰기를 지정 합니다.호출자는 버퍼를 제공 하 고 초기화 해야 합니다.

[!참고]

다음과 같이 _Out_, _Inout_ 에 수정할 수 있는 값을 적용 해야 합니다.

void InOutCallee(_Inout_ int *pInt)
{
   int i = *pInt;
   *pInt = 6;
}

void InOutCaller()
{
   int *pInt = new int;
   *pInt = 5;
   InOutCallee(pInt);
   delete pInt;
}

void BadInOutCaller()
{
   int *pInt = NULL;
   InOutCallee(pInt); // ‘pInt’ should not be NULL
} 

Visual Studio 코드 분석 검사 호출자가 NULL이 아닌 포인터를 초기화 버퍼를 전달 pInt, 및 반품을 하기 전에 pInt 여전히 NULL이 버퍼를 초기화 합니다.

Hh916383.collapse_all(ko-kr,VS.110).gif예: _Inout_opt_ 주석

_Inout_opt_같은 _Inout_따라서 함수가이를 확인 해야 하 고 입력된 매개 변수는 NULL이 될 수 있는 점을 제외 하 고는 합니다.

void GoodInOutOptCallee(_Inout_opt_ int *pInt)
{
   if(pInt != NULL) {
      int i = *pInt;
      *pInt = 6;
   }
}

void BadInOutOptCallee(_Inout_opt_ int *pInt)
{
   int i = *pInt; // Dereferencing NULL pointer ‘pInt’
   *pInt = 6;
}

void InOutOptCaller()
{
   int *pInt = NULL;
   GoodInOutOptCallee(pInt);
   BadInOutOptCallee(pInt);
} 

Visual Studio 코드 분석 확인이 함수에 대해 NULL 버퍼에 액세스 하기 전에 확인 하 고는 pInt NULL 반환 하기 전에 해당 함수에 의해 버퍼 초기화 된다는 아닙니다.

Hh916383.collapse_all(ko-kr,VS.110).gif예: _Outptr_ 주석

_Outptr_에 대 한 포인터를 반환 하도록 의도 된 매개 변수 주석을 지정 하는 데 사용 됩니다.NULL 매개 변수 자체 수 없습니다 및 NULL이 아닌 포인터는 호출된 된 함수에서 반환 해당 초기화 된 데이터를 가리킵니다.

void GoodOutPtrCallee(_Outptr_ int **pInt)
{
   int *pInt2 = new int;
   *pInt2 = 5;

   *pInt = pInt2;
}

void BadOutPtrCallee(_Outptr_ int **pInt)
{
   int *pInt2 = new int;
   // Did not initialize pInt buffer before returning!
   *pInt = pInt2;
}

void OutPtrCaller()
{
   int *pInt = NULL;
   GoodOutPtrCallee(&pInt);
   BadOutPtrCallee(&pInt);
} 

Visual Studio 코드 분석 검사는 호출자가 NULL이 아닌 포인터가 전달에 대 한 *pInt, 및 반환 하기 전에 해당 함수에 의해 버퍼를 초기화 합니다.

Hh916383.collapse_all(ko-kr,VS.110).gif예: _Outptr_opt_ 주석

_Outptr_opt_같은 _Outptr_매개 변수는 선택 사항입니다만 제외 하 고, 호출자가 NULL 포인터의 매개 변수를 전달할 수 있습니다.

void GoodOutPtrOptCallee(_Outptr_opt_ int **pInt)
{
   int *pInt2 = new int;
   *pInt2 = 6;

   if(pInt != NULL) {
      *pInt = pInt2;
   }
}

void BadOutPtrOptCallee(_Outptr_opt_ int **pInt)
{
   int *pInt2 = new int;
   *pInt2 = 6;
   *pInt = pInt2; // Dereferencing NULL pointer ‘pInt’
}

void OutPtrOptCaller()
{
   int **ppInt = NULL;
   GoodOutPtrOptCallee(ppInt);
   BadOutPtrOptCallee(ppInt);
} 

Visual Studio 코드 분석의 유효성을 검사 하기 전에 null이 함수를 확인 합니다 *pInt 역참조 되 고 반환 하기 전에 해당 함수에 의해 버퍼를 초기화 합니다.

Hh916383.collapse_all(ko-kr,VS.110).gif예:에서 _Success_ 주석 조합 하 여 _out_와

주석은 대부분의 개체에 적용할 수 있습니다.특히 전체 함수에 주석을 달 수 있습니다.함수의 가장 두드러진 특징 중 하나는 성공 또는 실패 수 있는지입니다.그러나 버퍼 크기와 같은 연결, C/c + + 함수 성공 또는 실패를 표현할 수 없습니다.사용 하는 _Success_ 주석, 어떤 함수에 대 한 성공을 말할 수 있습니다.매개 변수는 _Success_ 주석입니다만 식 true 일 때 함수 성공 했음을 나타냅니다.식 주석 파서가 처리할 수 있는 아무 것도 될 수 있습니다.함수가 반환 된 후 주석의 효과 함수가 성공 하는 경우에 적용 됩니다.이 예제를 보여 줍니다 어떻게 _Success_ 상호 작용 _Out_ 적절 하.키워드를 사용할 수 있습니다 return 의 반환 값을 나타내는 데.

_Success_(return != false) // Can also be stated as _Success_(return)
bool GetValue(_Out_ int *pInt, bool flag)
{
   if(flag) {
      *pInt = 5;
      return true;
   } else {
      return false;
   }
}

_Out_ 호출자가 NULL이 아닌 포인터가 버퍼를 전달 하 여 유효성을 검사 하려면 Visual Studio 코드 분석 주석을 사용 하면 pInt, 및 반환 하기 전에 해당 함수에 의해 버퍼를 초기화 합니다.

가장 좋은 방법은 SAL

Hh916383.collapse_all(ko-kr,VS.110).gif기존 코드에 주석 추가

SAL 보안 및 코드의 안정성을 향상 시킬 수 있도록 하는 강력한 기술입니다.SAL 알아본 다음 일상 업무에 새로운 스킬을 적용할 수 있습니다.새 코드에서 전체 설계 사양 SAL 기반을 사용할 수 있습니다. 이전 코드에서 주석을 단계적으로 추가 하 고 업데이트할 때마다 이점이 있으므로 증가 수 있습니다.

Microsoft 공용 헤더가 이미 주석 처리 됩니다.따라서 주석을 프로젝트에 먼저 리프 노드 기능 및 대부분의 혜택을 얻으려면 Win32 Api를 호출 하는 함수 달 것을 제안 합니다.

Hh916383.collapse_all(ko-kr,VS.110).gif주석을 때

다음은 몇 가지 지침입니다.

  • 모든 포인터 매개 변수에 주석을 지정 합니다.

  • 코드 분석 버퍼 및 포인터 안전 수 있도록 주석을 값 범위에 주석을 답니다.

  • 규칙을 잠금 및 잠금 부작용에 주석을 답니다.자세한 내용은 잠금 동작에 주석 지정을 참조하십시오.

  • 드라이버 속성 및 기타 도메인 관련 속성에 주석을 답니다.

모든 매개 변수가 전체 사용자 의도 취소 하 고 주석을 수행한 확인 쉽게 주석을 달 수 있습니다.

관련 리소스

코드 분석 팀 블로그

참고 항목

참조

함수 매개 변수 및 반환 값에 주석 지정

함수 동작에 주석 지정

구조체 및 클래스에 주석 지정

잠금 동작에 주석 지정

주석 적용 시기 및 위치 지정

모범 사례 및 예제(SAL)

기타 리소스

C/C++ 코드 오류를 줄이기 위한 SAL 주석 사용