我想我在 VC++10 std::locale 对象中发现了一个缓冲区溢出错误。对于我是否做错了什么,我将不胜感激。
下面的代码被简化以演示问题。我正在尝试将 wchar_t (UTF-16) 字符转换为 (Windows) ANSI 代码页 51949(又名 EUC-KR)。选择代码页是因为它使用双字节 (DBCS) 编码。
我故意为双字节结果提供单个字符缓冲区,期望它以“部分”或“错误”状态失败。
相反,它返回状态“ok”,写入缓冲区末尾并在尝试退出时导致损坏堆栈异常。
#include <iostream>
#include <locale>
#include <cwchar>
typedef std::codecvt<wchar_t, char, mbstate_t> cvt_t;
void PrintResult(cvt_t::result r)
{
switch (r)
{
case cvt_t::ok:
std::cout << "ok\n";
break;
case cvt_t::partial:
std::cout << "partial\n";
break;
case cvt_t::error:
std::cout << "error\n";
break;
case cvt_t::noconv:
std::cout << "noconv\n";
break;
}
}
int main()
{
const wchar_t src = L'안';
char dst = 0;
std::locale loc("Korean_Korea.51949");
mbstate_t state = { 0 };
const cvt_t &facet = std::use_facet<cvt_t>(loc);
cvt_t::result res;
const wchar_t *pSrc;
char *pDst;
res = facet.out(state, &src, &src+1, pSrc, &dst, &dst+1, pDst);
PrintResult(res);
return 0;
}
使用调试器进入 facet.out(),我发现自己(下降了几个级别)在以下函数中:
virtual result __CLR_OR_THIS_CALL do_out(_Statype& _State,
const _Elem *_First1, const _Elem *_Last1, const _Elem *& _Mid1,
_Byte *_First2, _Byte *_Last2, _Byte *& _Mid2) const
{ // convert [_First1, _Last1) to bytes [_First2, _Last)
_DEBUG_RANGE(_First1, _Last1);
_DEBUG_RANGE(_First2, _Last2);
_Mid1 = _First1, _Mid2 = _First2;
result _Ans = _Mid1 == _Last1 ? ok : partial;
int _Bytes;
while (_Mid1 != _Last1 && _Mid2 != _Last2)
if ((int)MB_CUR_MAX <= _Last2 - _Mid2)
if ((_Bytes = _Wcrtomb(_Mid2, *_Mid1,
&_State, &_Cvt)) < 0)
return (error); // locale-specific wcrtomb failed
else
++_Mid1, _Mid2 += _Bytes, _Ans = ok;
else
{ // destination too small, convert into buffer
_Byte _Buf[MB_LEN_MAX];
_Statype _Stsave = _State;
if ((_Bytes = _Wcrtomb(_Buf, *_Mid1,
&_State, &_Cvt)) < 0)
return (error); // locale-specific wcrtomb failed
else if (_Last2 - _Mid2 < _Bytes)
{ // converted too many, roll back and return previous
_State = _Stsave;
return (_Ans);
}
else
{ // copy converted bytes from buffer
_CSTD memcpy(_Mid2, _Buf, _Bytes);
++_Mid1, _Mid2 += _Bytes, _Ans = ok;
}
}
return (_Ans);
}
问题似乎是这样的:
在线上if ((int)MB_CUR_MAX <= _Last2 - _Mid2)
,MB_CURR_MAX(#defined 到函数 ___mb_cur_max_func(),返回“1”,而不是 DBCS 字符预期的“2”。
_CRTIMP int __cdecl ___mb_cur_max_func(void)
{
/*
* Note that we don't need _LocaleUpdate in this function.
* The main reason being, that this is a leaf function in
* locale usage terms.
*/
_ptiddata ptd = _getptd();
pthreadlocinfo ptloci = ptd->ptlocinfo;
__UPDATE_LOCALE(ptd, ptloci);
return ptloci->mb_cur_max;
}
此函数似乎正在访问全局语言环境,而不是在初始化方面时提供的语言环境。
如果我将全局语言环境设置如下:
std::locale::global(loc);
一切正常(并以“部分”状态失败)。
因此,在我看来,这个特定的实现out()
错误地使用全局语言环境来确定 MB_CUR_MAX,而不是在对象构造时提供的语言环境——从而导致缓冲区溢出错误。
有人能指出我在这方面的错误吗?
------------------------------
2013 年 5 月 20 日更新
---------------- --------------
我已将此作为错误 787227 报告给 MS。
------------------------------
2015 年 7 月 27 日更新
---------------- --------------
我收到一封来自 Microsoft 的电子邮件,说此错误已在 Visual Studio 2015 的新 RTM 中修复。(耶)