CRTのセキュリティ強化(strcpy_s,sprintf_s等)について(2)

前回の続き。。。

もう1点、マニュアルを見ていて、誤記じゃね?と思った。
代表的な以下の関数だけ抜粋

errno_t strcat_s(char *strDestination, size_t numberOfElements, const char *strSource);
numberOfElements 追加先の文字列バッファのサイズ。

int sprintf_s(char *buffer, size_t sizeOfBuffer, const char *format [,argument] ...);
sizeOfBuffer 格納する最大文字数。

errno_t memcpy_s(void *dest, size_t numberOfElements, const void *src, size_t count);
numberOfElements コピー先のバッファのサイズ。

あれ?第二引数の表現が微妙に異なる。で、また、実験してみる。

#include

class CrtUtil
{
public:
CrtUtil();
virtual ~CrtUtil();

static int StrCpyS(char* dest, int len, char* src);
static int StrCatS(char* dest, int len, char* src);
static int SprintfS(char* dest, int len, char* format,...);
static int MemCpyS(char* dest, int len, char*src, int max_len);

private:
static void myInvalidParameterHandler(const wchar_t* expression,
const wchar_t* function,
const wchar_t* file,
unsigned int line,
uintptr_t pReserved);

static bool illegal_operation_flag;
static void SetIllegalOperationFlag(bool flag);
static bool GetIllegalOperationFlag();
};

#include "CrtUtil.h"
#include
#include

bool CrtUtil::illegal_operation_flag = false;

CrtUtil::CrtUtil()
{
}
CrtUtil::~CrtUtil()
{
}

void CrtUtil::myInvalidParameterHandler(const wchar_t* expression,
const wchar_t* function,
const wchar_t* file,
unsigned int line,
uintptr_t pReserved)
{
/*
wprintf(L"Invalid parameter detected in function %s."
L" File: %s Line: %d\n", function, file, line);
wprintf(L"Expression: %s\n", expression);
*/
SetIllegalOperationFlag(true);
}

void CrtUtil::SetIllegalOperationFlag(bool flag)
{
illegal_operation_flag = flag;
}
bool CrtUtil::GetIllegalOperationFlag()
{
return illegal_operation_flag;
}

int CrtUtil::StrCpyS(char* dest, int len, char*src)
{
_invalid_parameter_handler oldHandler, newHandler;

SetIllegalOperationFlag(false);
newHandler = myInvalidParameterHandler;
// CRT が無効な引数を検出した場合に呼び出す関数を設定
oldHandler = _set_invalid_parameter_handler(newHandler);
// アサーションによるメッセージを無効化
_CrtSetReportMode(_CRT_ASSERT, 0);

int ret = strcpy_s(dest, len, src);
if (GetIllegalOperationFlag()) {
ret = -1;
SetIllegalOperationFlag(false);
}
// アサーションによるメッセージを有効化
_CrtSetReportMode(_CRT_ASSERT, 1);
// CRT が無効な引数を検出した場合に呼び出す関数を元に戻す
_set_invalid_parameter_handler(oldHandler);
return ret;
}

int CrtUtil::StrCatS(char* dest, int len, char*src)
{
_invalid_parameter_handler oldHandler, newHandler;

SetIllegalOperationFlag(false);
newHandler = myInvalidParameterHandler;
// CRT が無効な引数を検出した場合に呼び出す関数を設定
oldHandler = _set_invalid_parameter_handler(newHandler);
// アサーションによるメッセージを無効化
_CrtSetReportMode(_CRT_ASSERT, 0);

int ret = strcat_s(dest, len, src);
if (GetIllegalOperationFlag()) {
ret = -1;
SetIllegalOperationFlag(false);
}
// アサーションによるメッセージを有効化
_CrtSetReportMode(_CRT_ASSERT, 1);
// CRT が無効な引数を検出した場合に呼び出す関数を元に戻す
_set_invalid_parameter_handler(oldHandler);
return ret;
}

int CrtUtil::SprintfS(char* dest, int len, char* format,...)
{
_invalid_parameter_handler oldHandler, newHandler;

SetIllegalOperationFlag(false);
newHandler = myInvalidParameterHandler;
// CRT が無効な引数を検出した場合に呼び出す関数を設定
oldHandler = _set_invalid_parameter_handler(newHandler);
// アサーションによるメッセージを無効化
_CrtSetReportMode(_CRT_ASSERT, 0);

va_list ap;
va_start(ap, format);
int ret = vsprintf_s(dest, len, format, ap);
va_end(ap);
if (GetIllegalOperationFlag()) {
ret = -1;
SetIllegalOperationFlag(false);
}
// アサーションによるメッセージを有効化
_CrtSetReportMode(_CRT_ASSERT, 1);
// CRT が無効な引数を検出した場合に呼び出す関数を元に戻す
_set_invalid_parameter_handler(oldHandler);
return ret;
}

int CrtUtil::MemCpyS(char* dest, int len, char*src, int max_len)
{
_invalid_parameter_handler oldHandler, newHandler;

SetIllegalOperationFlag(false);
newHandler = myInvalidParameterHandler;
// CRT が無効な引数を検出した場合に呼び出す関数を設定
oldHandler = _set_invalid_parameter_handler(newHandler);
// アサーションによるメッセージを無効化
_CrtSetReportMode(_CRT_ASSERT, 0);

int ret = memcpy_s(dest, len, src, max_len);
if (GetIllegalOperationFlag()) {
ret = -1;
SetIllegalOperationFlag(false);
}
// アサーションによるメッセージを有効化
_CrtSetReportMode(_CRT_ASSERT, 1);
// CRT が無効な引数を検出した場合に呼び出す関数を元に戻す
_set_invalid_parameter_handler(oldHandler);
return ret;
}

// CrtEnhancementTest.cpp : コンソール アプリケーションのエントリ ポイントを定義します。
//

#include "stdafx.h"
#include
#include "CrtUtil.h"

int _tmain(int argc, _TCHAR* argv[])
{
char str[10];
str[0] = '\0';
int ret = -1;
char src[21][32] = {
"",
"1",
"12",
"123",
"1234",
"12345",
"123456",
"1234567",
"12345678",
"123456789",
"1234567890",
"12345678901",
"123456789012",
"1234567890123",
"12345678901234",
"123456789012345",
"1234567890123456",
"12345678901234567",
"123456789012345678",
"1234567890123456789",
"12345678901234567890"
};

printf("----------------------------------------\n");
printf("-- strcpy_s ----------------------------\n");
printf("----------------------------------------\n");
for (int i = 9; i <= 11; i++) {
ret = CrtUtil::StrCpyS(str, (int)sizeof(str), src[i]);
printf("len:%2d,ret:%2d,str:%s\n", i, ret, str);
}
printf("----------------------------------------\n");
// 実際の出力バッファより小さく指定すると。。。
for (int i = 9; i <= 11; i++) {
ret = CrtUtil::StrCpyS(str, (int)sizeof(str) - 5, src[i]);
printf("len:%2d,ret:%2d,str:%s\n", i, ret, str);
}
printf("----------------------------------------\n");
// 実際の出力バッファより大きく指定すると。。。
for (int i = 9; i <= 11; i++) {
ret = CrtUtil::StrCpyS(str, (int)sizeof(str) + 5, src[i]);
printf("len:%2d,ret:%2d,str:%s\n", i, ret, str);
}

printf("----------------------------------------\n");
printf("-- strcat_s ----------------------------\n");
printf("----------------------------------------\n");
memset(str, '\0', sizeof(str));
for (int i = 1; i <= 20; i++) {
ret = CrtUtil::StrCatS(str, (int)sizeof(str), src[1]);
printf("len:%2d,ret:%2d,str:%s\n", i, ret, str);
if (ret) break;
}
printf("----------------------------------------\n");
// 実際の出力バッファより小さく指定すると。。。
memset(str, '\0', sizeof(str));
for (int i = 1; i <= 20; i++) {
ret = CrtUtil::StrCatS(str, (int)sizeof(str) - 5, src[1]);
printf("len:%2d,ret:%2d,str:%s\n", i, ret, str);
if (ret) break;
}
printf("----------------------------------------\n");
// 実際の出力バッファより大きく指定すると。。。
memset(str, '\0', sizeof(str));
for (int i = 1; i <= 20; i++) {
ret = CrtUtil::StrCatS(str, (int)sizeof(str) + 5, src[1]);
printf("len:%2d,ret:%2d,str:%s\n", i, ret, str);
if (ret) break;
}

printf("----------------------------------------\n");
printf("-- sprintf_s(vsprintf_s) ---------------\n");
printf("----------------------------------------\n");
for (int i = 9; i <= 11; i++) {
ret = CrtUtil::SprintfS(str, (int)sizeof(str), "%s", src[i]);
printf("len:%2d,ret:%2d,str:%s\n", i, ret, str);
}
printf("----------------------------------------\n");
// 実際の出力バッファより小さく指定すると。。。
for (int i = 9; i <= 11; i++) {
ret = CrtUtil::SprintfS(str, (int)sizeof(str) - 5, "%s", src[i]);
printf("len:%2d,ret:%2d,str:%s\n", i, ret, str);
}
printf("----------------------------------------\n");
// 実際の出力バッファより大きく指定すると。。。
for (int i = 9; i <= 11; i++) {
ret = CrtUtil::SprintfS(str, (int)sizeof(str) + 5, "%s", src[i]);
printf("len:%2d,ret:%2d,str:%s\n", i, ret, str);
}

printf("----------------------------------------\n");
printf("-- memcpy_s ----------------------------\n");
printf("----------------------------------------\n");
memset(str, '\0', sizeof(str));
for (int i = 9; i <= 11; i++) {
ret = CrtUtil::MemCpyS(str, (int)sizeof(str), src[i], strlen(src[i]));
printf("len:%2d,ret:%2d,Mem:%s\n", i, ret, str);
}
printf("----------------------------------------\n");
// 実際の出力バッファより小さく指定すると。。。
memset(str, '\0', sizeof(str));
for (int i = 9; i <= 11; i++) {
ret = CrtUtil::MemCpyS(str, (int)sizeof(str) - 5, src[i], strlen(src[i]));
printf("len:%2d,ret:%2d,str:%s\n", i, ret, str);
}
printf("----------------------------------------\n");
// 実際の出力バッファより大きく指定すると。。。
memset(str, '\0', sizeof(str));
for (int i = 9; i <= 11; i++) {
ret = CrtUtil::MemCpyS(str, (int)sizeof(str) + 5, src[i], strlen(src[i]));
printf("len:%2d,ret:%2d,str:%s\n", i, ret, str);
}
printf("----------------------------------------\n");

return 0;
}

<実行結果>

                                                                              • -
    • strcpy_s ----------------------------
                                                                              • -

len: 9,ret: 0,str:123456789
len:10,ret:-1,str:
len:11,ret:-1,str:

                                                                              • -

len: 9,ret:-1,str:
len:10,ret:-1,str:
len:11,ret:-1,str:

                                                                              • -

len: 9,ret: 0,str:123456789
len:10,ret: 0,str:1234567890
len:11,ret: 0,str:12345678901

                                                                              • -
    • strcat_s ----------------------------
                                                                              • -

len: 1,ret: 0,str:1
len: 2,ret: 0,str:11
len: 3,ret: 0,str:111
len: 4,ret: 0,str:1111
len: 5,ret: 0,str:11111
len: 6,ret: 0,str:111111
len: 7,ret: 0,str:1111111
len: 8,ret: 0,str:11111111
len: 9,ret: 0,str:111111111
len:10,ret:-1,str:

                                                                              • -

len: 1,ret: 0,str:1
len: 2,ret: 0,str:11
len: 3,ret: 0,str:111
len: 4,ret: 0,str:1111
len: 5,ret:-1,str:

                                                                              • -

len: 1,ret: 0,str:1
len: 2,ret: 0,str:11
len: 3,ret: 0,str:111
len: 4,ret: 0,str:1111
len: 5,ret: 0,str:11111
len: 6,ret: 0,str:111111
len: 7,ret: 0,str:1111111
len: 8,ret: 0,str:11111111
len: 9,ret: 0,str:111111111
len:10,ret: 0,str:1111111111
len:11,ret: 0,str:11111111111
len:12,ret: 0,str:111111111111
len:13,ret: 0,str:1111111111111
len:14,ret: 0,str:11111111111111
len:15,ret:-1,str:

                                                                              • -
    • sprintf_s(vsprintf_s) ---------------
                                                                              • -

len: 9,ret: 9,str:123456789
len:10,ret:-1,str:
len:11,ret:-1,str:

                                                                              • -

len: 9,ret:-1,str:
len:10,ret:-1,str:
len:11,ret:-1,str:

                                                                              • -

len: 9,ret: 9,str:123456789
len:10,ret:10,str:1234567890
len:11,ret:11,str:12345678901

                                                                              • -
    • memcpy_s ----------------------------
                                                                              • -

len: 9,ret: 0,Mem:123456789
len:10,ret: 0,Mem:12345678901
len:11,ret:-1,Mem:

                                                                              • -

len: 9,ret:-1,str:
len:10,ret:-1,str:
len:11,ret:-1,str:

                                                                              • -

len: 9,ret: 0,str:123456789
len:10,ret: 0,str:12345678901
len:11,ret: 0,str:12345678901

                                                                              • -

ほむ。ということで、纏めると

  • strcpy関数などCRTのセキュリティ強化により、VC++ではstrcpy_s関数などが推奨される。

(バッファ取扱以外に対応した関数あり。そっちは調べてない)

  • 上記のバッファを扱う関数は概ね第二引数に出力バッファのサイズを指定する。

 (※文字数って書いてある関数もあるけど、NULL終端子も含めたサイズ(memcpyは別))

  • バッファのサイズ超えたら、アサーションや発生時点での実行エラーになる。
  • バッファのサイズ間違えてもえらい目にあう。

 (strcat_s関数なんか、思いっきりメモリぶっ壊してますよ。。。)
ってとこかな。