Media Log


void f()
{
    TCHAR s1[10];
    TCHAR s2[10];

    _tcscpy( s1, s2 );
}
warning C4996: 'strcpy': This function or variable may be unsafe. Consider using strcpy_s instead. To disable deprecation, use _CRT_SECURE_NO_WARNINGS. See online help for details.

위와 같은 코드는 에러 없이 컴파일 되지만, 경고가 발생한다.
심지어 s2의 버퍼가 s1 보다 크더라도 잘 컴파일 된다.
Visual Studio 2005부터는 strcpy 같은 보안상 취약성을 가진 문자열 함수들은 모두 경고 처리 된다.

학창 시절 Visual Studio 2005가 처음 나와서 써볼 때 strcpy함수가(학부생들이 가장 즐겨쓰는 함수 10위안에는 들어갈) 줄줄이 경고가 발생해 몹시 당황스러웠던 기억이 난다.
당시 우리들은 오직 이 이유 때문에 VS2005가 꼬졌으니 쓰지 말아야 한다면서 VS2003을 사용했는데 지금 생각하니 재밌다.

많은 웹문서에서 올바로된 코드를 작성하는 방법이 아니라 _CRT_SECURE_NO_WARNINGS 옵션을 사용하여 경고 메세지를 숨기는 방법에 대해 설명하곤 한다.
나 역시 한동안 그렇게 사용했었는데 그것은 오히려 경고인 채로 내버려 두는 것보다도 더 나쁘다는 것을 존 로빈스의 책을 통해서 배웠다.
경고 레벨은 항상 최대로 올린채 경고를 숨긴 곳을 찾아 모두 지워라. (VS이던 GCC이던간에) 그리고는 하나씩 경고를 없애 나가야만 한다. - 엄청나게 많던 경고 메세지가 점점 줄어들면서 깔끔하게 컴파일되는 모습을 보는 것은 꽤나 즐거운 일이다. :)

자 그럼, 경고를 원하지 않는다면( 당연히 그래야 한다. ) secure 버전 함수들을 사용해야 한다.
위 코드는 다음과 같이 secure function을 써서 재작성 하자.

void f()
{
    TCHAR s1[10];
   
TCHAR s2[10];

    _tcscpy_s( s1, _countof( s1 ), s2 );

    // 또는 _tcscpy_s( s1, s2 );
}

이제 경고 없이 깨끗하게 컴파일 된다.

2번째 인자로는 s1 배열 요소의 갯수를 넣어주었다.
strcpy_s 함수 같은 경우 2번째 인자의 변수 이름이 _SizeInBytes 라서 sizeof 연산자를 사용하여 코드를 작성하면 될 것 같지만, 유니코드용 함수인 wcscpy_s 같은 경우에는 변수 이름이 SizeInWord로 되어있다. 즉 TCHAR 타입을 사용하는 경우에는 배열의 사이즈가 아니라 배열의 요소의 갯수(문자의 갯수)를 전달해주어야 한다. 이렇게 하면 ANSI나 유니코드 프로젝트에서 모두 잘 동작한다.

위 코드에서는 VC2005부터 제공되는 _countof 매크로를 통해서 배열의 요소 갯수를 구해주었다.

만일 매크로가 없는 환경이라면 단순히
#define _countof(_Array) (sizeof(_Array) / sizeof(_Array[0]))

이렇게 만들어 주면 된다.

빌어먹을 바이트수(cb)와 문자의 갯수(cch)는 언제나 우리를 햇갈리게 하며, 조심스럽게 접근해야 하는 부분이다. 변수 이름 앞에 cb나 cch가 붙어있다면 긴장하고 눈을 크게 뜬채 한번 더 생각해보고 코딩하는 것이 좋다.


Target( 여기선 s1 )이 컴파일 타임에 크기를 잡아둔 정적 배열일 경우에는 2번째 인자를 생략해도 상관없으나 동적으로 크기를 지정한 배열일 경우에는 컴파일러가 알 수 없으므로 인자를 생략할 수 없다.

이제 다음 함수를 보자.
void copy( TCHAR* pResultString )
{
    TCHAR str[MAX] = { 0 };
   
    // some work   

    _tcscpy( pResultString, str );
}

위 함수는 어떤 작업을 한 후에 그 결과 문자열을 pResultString에 저장해주는 기능을 한다.
역시 strcpy 를 사용했기 때문에 C4996 경고.

하지만 이런 함수들은 secure function으로 수정할 수도 없다.
호출하는 쪽에서 문자열 버퍼의 크기를 얼마로 잡았는지 알 수가 없기 때문이다.
따라서 2번째 인자를 넣어줄 수가 없다.

이런 경우에는 이런식으로 메소드 시그내쳐를 수정해야만 한다.
void copy( TCHAR* pResultString, size_t iMaxBuffer )
{
    TCHAR str[MAX] = { 0 };
    
    // some work    

    _tcscpy_s( pResultString, iMaxBuffer, str );
}

그래서 나는 다음과 같은 방법을 더 선호한다.
void copy( CString& strResult ) //또는 std::string& 으로
{
    TCHAR str[MAX] = { 0 };
   
    // some work   

    sResult = str;
}

신고
  1. 김유리 at 2009.03.28 13:52 신고 [edit/del]

    잘보고갑니다 ^^

    Reply
  2. at 2009.05.01 00:07 [edit/del]

    비밀댓글입니다

    Reply
  3. Favicon of http://blog.naver.com/l_need BlogIcon l_need at 2009.05.03 23:48 신고 [edit/del]

    항상 고민하고 있던 내용입니다!! 블로그에 비공개로 담아가겠습니다

    Reply
  4. Favicon of http://www.cvi.kr BlogIcon 허창원 at 2009.05.14 22:43 신고 [edit/del]

    VS를 잘 안쓰다가 오랫만에 쓰려니 이런게 다 문제가 되더군요.
    적절할 때에 큰 도움이 되었습니다.
    감사합니다.

    Reply
  5. only2sea at 2009.06.21 22:17 신고 [edit/del]

    바이트 수랑 문자 개수.. ㅜㅜ 그나마 우리는 덜 틀리지만, 영어권 프로그래머들은 더 많이 틀립니다. 문자도 바이트 단위로 맘대로 싹뚝 자르고... 다행히 요즈음은 그런 경우가 많이 줄었습니다만...

    Reply
  6. 위주 at 2009.06.28 21:57 신고 [edit/del]

    큰 도움되었습니다. 고맙습니다.

    Reply
  7. 주모 at 2014.06.25 13:14 신고 [edit/del]

    안녕하세요. _tsccpy_s() 사용 할때 복사 대상자가 되는 첫번째 인자값이 new로 할당받는 포인터변수이면 _tcscpy_s를 사용하지 못하나요?

    Reply

submit