我们的应用程序浏览命名管道列表,并查找由我们的应用程序创建的命名管道。
如果我们的命名管道不存在,我们继续创建一个。但是,最近我们的客户报告了
应用程序在行崩溃:
fileName = std::wstring(TmpInfo->FileName);
您可以查看下面附带的windbg崩溃转储报告。崩溃是零星的,并且在任何时候都不可重复。你能帮我辨认一下这个问题吗?将TmpInfo->FileName
转换为wstring
时是否存在问题。请注意,此组件是ActiveX控件。我们的ActiveX正在崩溃其他应用程序。
以下代码负责枚举命名管道。
typedef struct {
ULONG NextEntryOffset;
ULONG FileIndex;
ULONG FileNameLength;
WCHAR FileName[1];
} FILE_NAMES_INFORMATION, *PFILE_NAMES_INFORMATION;
inline void EnumerateRunningPipes(std::vector<std::wstring>& pipeNames, std::wstring stringLookup,
bool useStringLookup, bool
truncatePipeDirPrefix)
{
LONG ntStatus;
IO_STATUS_BLOCK IoStatus;
BOOL bReset = TRUE;
PFILE_NAMES_INFORMATION fileNameInfo, TmpInfo;
fileNameInfo = (PFILE_NAMES_INFORMATION) new BYTE[1024];
HANDLE hPipe = CreateFile("//./pipe/",
GENERIC_READ,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
NULL,
OPEN_EXISTING,
0,
NULL);
if(hPipe == INVALID_HANDLE_VALUE)
throw CEnumeratePipeException("Could not get handle to root directory for pipes.",
CEnumeratePipeException::ErrorType::CREATE_FILE, GetLastError());
USES_CONVERSION;
locale loc;
mbstate_t state = {0};
while(true)
{
memset(fileNameInfo, 0, 1024);
ntStatus = NtQueryDirectoryFile( hPipe, NULL, NULL, NULL, &IoStatus, fileNameInfo,
1024,
FileNameInformation, FALSE, NULL, bReset );
if (ntStatus!=NO_ERROR)
{
if (ntStatus == STATUS_NO_MORE_FILES)
break;
std::stringstream sstream;
sstream << "NtQueryDirectoryFile error " << ntStatus;
logger->writeLog(sstream.str());
throw CEnumeratePipeException(
"NtQueryDirectoryFile could not get information about the directory or
file.",
CEnumeratePipeException::ErrorType::QUERY_DIR, ntStatus);
}
TmpInfo = fileNameInfo;
while(true)
{
const int endStringAt = TmpInfo->FileNameLength / sizeof(WCHAR);
std::wstring fileName;
try
{
fileName = std::wstring(TmpInfo->FileName);//The access violation
occurs at this line of code.
fileName = fileName.substr(0, endStringAt);
}
catch (...)
{
logger->writeLog("Caught unknown exception.");
if (TmpInfo->FileName == NULL)
{
logger->writeLog("TmpInfo->FileName = NULL");
}
}
if (useStringLookup)
{
if (fileName.find(stringLookup) != std::wstring::npos)
{
if (!truncatePipeDirPrefix)
fileName.insert(0, L"\\\\.\\pipe\\");
pipeNames.push_back(fileName);
}
}
else
{
pipeNames.push_back(fileName);
}
if(TmpInfo->NextEntryOffset==0)
break;
TmpInfo = (PFILE_NAMES_INFORMATION)((DWORD)TmpInfo+TmpInfo->NextEntryOffset);
}
bReset = FALSE;
}
delete fileNameInfo;
}
以下是崩溃转储报告的一部分:
FAULTING_IP: msvcr100!memcpy+57 [f:\dd\vctools\crt_bld\SELF_X86\crt\src\INTEL\memcpy.asm @ 185] 78aa1ed7 f3a5 rep movs dword ptr es:[edi],dword ptr [esi] EXCEPTION_RECORD: ffffffff -- (.exr 0xffffffffffffffff) ExceptionAddress: 78aa1ed7 (msvcr100!memcpy+0x00000057) ExceptionCode: c0000005 (Access violation) ExceptionFlags: 00000000 NumberParameters: 2 Parameter[0]: 00000000 Parameter[1]: 0d6b8000 Attempt to read from address 0d6b8000 SYMBOL_STACK_INDEX: 0 SYMBOL_NAME: msvcr100!memcpy+57 FOLLOWUP_NAME: MachineOwner MODULE_NAME: msvcr100 IMAGE_NAME: msvcr100.dll DEBUG_FLR_IMAGE_TIMESTAMP: 4ba1dbbe FAILURE_BUCKET_ID: STRING_DEREFERENCE_c0000005_msvcr100.dll!memcpy BUCKET_ID: APPLICATION_FAULT_STRING_DEREFERENCE_CODE_ADDRESS_MISMATCH_INVALID_POINTER_READ_WRONG_SYMBOLS_msvcr100!memcpy +57
答案 0 :(得分:2)
我在FILE_NAMES_INFORMATION
文档中没有看到任何说明FileName
成员以空值终止的内容。并且FileNameLength
成员的存在表明它不是以空值终止的。
所以似乎很可能
fileName = std::wstring(TmpInfo->FileName);
正在读取TmpInfo
缓冲区的末尾,直到它遇到两个连续的空字节。如果在遇到不可读的虚拟内存区域之前未遇到空字节,则会出现访问冲突。
我建议更换这两行代码:
fileName = std::wstring(TmpInfo->FileName);
fileName = fileName.substr(0, endStringAt);
用这个:
fileName = std::wstring(TmpInfo->FileName, TmpInfo->FileNameLength);
该构造函数只会从缓冲区中读取FileNameLength
个字符,因此它不会崩溃。而且效率更高!
答案 1 :(得分:1)
如何定义TmpInfo和TmpInfo-&gt; FileName? TmpInfo-&gt; FileName崩溃时是否有示例值?
我的猜测是它在某些情况下不会被终止,导致std::wstring
的构造函数读入不属于它的内存。