An INVALID_POINTER_READ_EXPLOITABLE (buffer overrun) in Notepad++
Earlier this week I tracked down an insidious bug in Notepad++.
Thankfully, this bug is extremely hard to reproduce (don’t even try reproduce it, let alone exploit it).
I’ve tracked it down to either Notepad_plus::doOpen
(in NppIO.cpp
) or FileManager::loadFile
(in ScintillaComponent/Buffer.cpp
), but I’m leaning towards doOpen
.
I run many apps with Application Verifier on, and Notepad++ is one of them. The other day, it crashed, and Application Verifier logged:
First chance access violation for current stack trace. 1acfc000 - Invalid address causing the exception. 40cbe0 - Code address executing the invalid access. 138fec - Exception record. 13903c - Context record.
Which is unusual, as I’m used to Notepad++ leaking heap allocations, not access violations.
So I fire up windbg and it hits the same access violation:
FAULTING_IP: npp+cbe0 0040cbe0 8a08 mov cl,byte ptr [eax]
and !analyze -v
tells me:
ADDITIONAL_DEBUG_TEXT: Exploitable because of a buffer overrun or underrun when full pageheap is enabledBUGCHECK_STR: APPLICATION_FAULT_INVALID_POINTER_READ_EXPLOITABLE
Which looks fairly scary. When the last three digits of an access-violation-causing address are zero (under Application Verifier), a buffer overrun is the likely cause (Bruce Dawson confirms this).
My first instinct at this point is to compile Notepad++ and step through it in Visual Studio, but (having tried doing so in the past) I realize it’d be easier to disassemble the Notepad++ executable, and match the offending instructions to the source code manually.
I go ahead and disassemble Notepad++, and see that the offending code is a loop, wherein a pointer (in eax) is incremented every iteration (duh, buffer overrun).
Hopper, like any respectable disassembler, provides a nice control flow graph. 40cbe0
is in a block that begins at 40cbd0
, so I proceeded to look for code that jumps to 40cbd0
. The block that calls 40cbd0
(that is NOT a subblock of 40cbd0
) is 4105a3
, and the nearest recognizable symbol upstream thereof is a call dword [ds:imp_PathFileExistsW]
(in 4104eb
).

No, like it’s really far upstream. ( NPP_lstrcpy_callgraph pdf here )
A grep of the 6.6.8 sourcecode shows 94 separate calls to PathFileExistsW
. To narrow this down, I first looked for symbols earlier the control flow graph than PathFileExistsW
. The first symbol is several blocks before PathFileExistsW
: call dword [ds:imp_lstrcpyW]
(in 41043c
).
Better yet, the call to lstrcpyw
is inside a branch that is controlled by a call to PathFileExistsW
, AND contains another PathFileExistsW
preceding the call to lstrcpyw
in the branch.
Finally, the block that immediately precedes that branch (4103f8
), there are two more system calls: call dword [ds:imp_GetFullPathNameW]
followed by call dword [ds:imp_GetLongPathNameW]
.
Perfect! Now we can narrow this down quite a bit!
Here is where I narrow it down to doOpen
and loadFile
.
Both functions look almost exactly the same.
- They accept a `const TCHAR * fileName` as their first parameter;
- they declare a `TCHAR[MAX_PATH]` buffer;
- they call `GetFullPathName` with `fileName` as the first parameter;
- they then call `GetLongPathName` with the `TCHAR[MAX_PATH]` buffer;
- then a call to `PathFileExists`, and a branch controlled by a call to `PathFileExists`;
- finally they call `lstrcpy` to copy the new buffer into `fileName`.

doOpen, which I think is more likely the bug. Minor correction: a PCWSTR is probably more a better choice here.
I’m leaning towards doOpen, because of this offending snippet of code:

While investigating this bug, I discovered another bug in `const TCHAR* fnss.getFileName(int index)` whereby getFileName might return a garbage string. Thus, doOpen may be passed a garbage string (pFn), which will then trigger evil behavior in lstrcpy.
fnss
is an instance of a class named FileNameStringSplitter, which is declared in “npp.6.6.8.src/PowerEditor/src/MISC/FileNameStringSplitter.h”, and has a single data member ( a std::vector<generic_string>
). generic_string
is a typedef, in “npp.6.6.8.src/PowerEditor/src/MISC/Common/Common.h”, line 90: typedef std::<a href="http://www.cplusplus.com/reference/string/basic_string/" target="_blank">basic_string</a><TCHAR> generic_string;
.
The bug is threefold:
- `fileName` is NOT guaranteed to be null-terminated, or valid.
- Investigating this led to bug #4943.
- The results of neither `GetFullPathName` nor `GetLongPathName` are checked.
- This means that `GetFullPathName` may fail (the `TCHAR[MAX_PATH]` is too small), and return the size of a buffer sufficiently large to hold the `GetFullPathName` `_Out_` parameter `lpBuffer`.
- `lstrcpy` then blindly copies the memory from `fileName`[0] to `fileName`[n], where `n` is the index of a NULL-terminator memory location, from the start of `fileName`.
- In the event that `fileName` is an invalid string (i.e. bug #4943), and the in-garbage-memory index of a NULL-terminator is more than `MAX_PATH` characters from `fileName`, `lstrcpy` will copy more than `MAX_PATH` characters into the `TCHAR[MAX_PATH]` buffer, thus writing beyond the end of the buffer, and corrupting the stack.
- In the event that `fileName` is a valid string, but longer than `MAX_PATH`, `lstrcpy` will copy `fileName` into the undersized buffer, and corrupt the stack.
Suggested fix:
- Use the return values of GetFullPathName and GetLongPathName.
-
Possibly annotate heavily with SAL.
Here is some sample code that demonstrates the problem:
#include <Windows.h> #include <string> #include <Shlwapi.h> #include <wchar.h> #include <cwchar> #include <iostream> typedef std::basic_string<WCHAR> generic_string; #define TRACE_OUT(x) std::endl << L"\t\t" << #x << L" = `" << x << L"` " void doOpen( _In_ PCWSTR fileName ) { LPTSTR myass; char a[ 12 ] = { 'A', 's', 's', 'h', 'o', 'l', 'e', ' ', ' ', ' ' , ' ', '\0' }; WCHAR longFileName[MAX_PATH ]; char d[ 12 ] = { 'A', 's', 's', 'h', 'o', 'l', 'e', ' ', ' ', ' ' , ' ', '\0' }; auto fpnRes = ::GetFullPathName(fileName, MAX_PATH, longFileName, NULL); auto lpnRes = ::GetLongPathName( longFileName, longFileName, MAX_PATH ); lstrcpy(longFileName, fileName); auto ass = PathFileExists( longFileName ); bool globbing = wcsrchr(longFileName, TCHAR('*')) || wcsrchr(longFileName, TCHAR('?')); auto res = longFileName; auto res2 = fileName; generic_string asss( longFileName ); auto neww = asss.c_str( ); std::wcout << TRACE_OUT( fileName ) << TRACE_OUT( longFileName ) << TRACE_OUT( fpnRes ) << TRACE_OUT( lpnRes ) << TRACE_OUT( ass ) << TRACE_OUT( globbing ) << TRACE_OUT( res ) << TRACE_OUT( res2 ) << TRACE_OUT( neww ) << TRACE_OUT( a ) << TRACE_OUT( d ) << std::endl << std::endl; } void caller( ) { generic_string a( L"C:\\Users\\Alexander Riccio\\AFSgcyuhihgdASdfghucvbbkmlk.fdvfhkjyxfgyhktfsdwghjijhgEASYTF6UGLO8DFH;RYWQREFRWEHQR;FOQFY;ERFY;REFY;FY;QFY;EQFQR8FYRE8FY;RFYEQRFYQE;FY;QERFYQE;FYQFYERFYQER8FQR9FY;ER8YF;QEFYQE;OFYQE;FY8EFY8F8YQEFYE;QE;R8FYER;FY;QREFY;EFYQRFY8QRVFaaaa" ); PCWSTR pFn = a.c_str( ); PCWSTR pan = L"C:\\Users\\Alexander Riccio\\AFSgcyuhihgdASdfghucvbbkmlk.fdvfhkjyxfgyhktfsdwghjijhgEASYTF6UGLO8DFH;RYWQREFRWEHQR;FOQFY;ERFY;REFY;FY;QFY;EQFQR8FYRE8FY;RFYEQRFYQE;FY;QERFYQE;FYQFYERFYQER8FQR9FY;ER8YF;QEFYQE;OFYQE;FY8EFY8F8YQEFYE;QE;R8FYER;FY;QREFY;EFYQRFY8QRVFaaaaaaaaayomama"; PCWSTR panShort = L"C:\\Users\\Alexander Riccio\\AFSgcyuhihgdASdfghucvbbkmlk.fdvfhkjyxfgyhktfsdwghjijhgEASYTF6UGLO8DFH;RYWQREFRWEHQR;FOQFY;ERFY;REFY;FY;QFY;EQFQR8FYRE8FY;RFYEQRFYQE;FY;QERFYQE;FYQFYERFYQER8FQR9FY;ER8YF;QEFYQE;OFYQE;FY8EFY8F8YQEFYE;QE;R8FYER;FY;QREFY;EFYQRFY8QRVFaaaa"; doOpen( pFn ); doOpen( panShort ); doOpen( pan ); doOpen( NULL ); } int main( ) { caller( ); }
And here’s the bug in action:

Yeah, it’s pretty subtle. (the source is slightly different in this pic, but functionally equivalent)
A grep of Notepad++ 6.6.8 source code finds 116 uses of lstrcpy! Yes, one hundred and sixteen! There are even more if you include lstrcpy’s friends.
./PowerEditor/installer/APIs/c.xml:1140: <KeyWord name="strcpy" func="yes"> ./PowerEditor/installer/APIs/c.xml:1499: <KeyWord name="_fstrcpy" /> ./PowerEditor/installer/APIs/cpp.xml:1580: <KeyWord name="strcpy" func="yes"> ./PowerEditor/installer/APIs/cpp.xml:2001: <KeyWord name="_fstrcpy" /> ./PowerEditor/src/localization.cpp:965: strcpy(title, titre); ./PowerEditor/src/MISC/PluginsManager/PluginsManager.cpp:145: lstrcpy(xmlPath, nppParams->getNppPath().c_str()); ./PowerEditor/src/MISC/PluginsManager/PluginsManager.cpp:153: lstrcpyn(xmlPath, TEXT("\0"), MAX_PATH ); ./PowerEditor/src/MISC/PluginsManager/PluginsManager.cpp:154: lstrcpy(xmlPath, nppParams->getAppDataNppDir() ); ./PowerEditor/src/MISC/Process/ProcessAvecThread/Process.h:44: lstrcpy(_command, cmd); ./PowerEditor/src/MISC/Process/ProcessAvecThread/Process.h:45: lstrcpy(_curDir, cDir); ./PowerEditor/src/MISC/Process/ProcessAvecThread/ProcessThread.h:38: lstrcpy(_appName, appName); ./PowerEditor/src/MISC/Process/ProcessAvecThread/ProcessThread.h:39: lstrcpy(_command, cmd); ./PowerEditor/src/MISC/Process/ProcessAvecThread/ProcessThread.h:40: lstrcpy(_curDir, cDir); ./PowerEditor/src/MISC/Process/ProcessAvecThread/ProcessThread.h:71: lstrcpy(appName, _appName); ./PowerEditor/src/Notepad_plus.cpp:2232: lstrcpy(generic_fontname, fontNameW); ./PowerEditor/src/Notepad_plus.cpp:4361: lstrcpy(pStr, str2cpy); ./PowerEditor/src/Notepad_plus.cpp:5038: lstrcpy(title, title_temp.c_str()); ./PowerEditor/src/Notepad_plus.cpp:5080: lstrcpy(title, title_temp.c_str()); ./PowerEditor/src/Notepad_plus.cpp:5121: lstrcpy(title, title_temp.c_str()); ./PowerEditor/src/Notepad_plus.cpp:5168: lstrcpy(title, title_temp.c_str()); ./PowerEditor/src/Notepad_plus.cpp:5214: lstrcpy(title, title_temp.c_str()); ./PowerEditor/src/Notepad_plus.cpp:5254: lstrcpy(title, title_temp.c_str()); ./PowerEditor/src/NppBigSwitch.cpp:591: TCHAR *fileStr = lstrcpy(str, _pEditView->getCurrentBuffer()->getFullPathName()); ./PowerEditor/src/NppBigSwitch.cpp:616: lstrcpy((TCHAR *)lParam, fileStr); ./PowerEditor/src/NppBigSwitch.cpp:637: lstrcpyn((TCHAR *)lParam, str, wParam); ./PowerEditor/src/NppBigSwitch.cpp:642: lstrcpy((TCHAR *)lParam, str); ./PowerEditor/src/NppBigSwitch.cpp:665: lstrcpy((TCHAR *)lParam, str); ./PowerEditor/src/NppBigSwitch.cpp:729: lstrcpy(fileNames[j++], buf->getFullPathName()); ./PowerEditor/src/NppBigSwitch.cpp:737: lstrcpy(fileNames[j++], buf->getFullPathName()); ./PowerEditor/src/NppBigSwitch.cpp:826: lstrcpy(sessionFileArray[i++], pFn); ./PowerEditor/src/NppBigSwitch.cpp:832: lstrcpy(sessionFileArray[i++], pFn); ./PowerEditor/src/NppBigSwitch.cpp:1753: lstrcpy(pluginsConfigDir, pluginsConfigDirPrefix.c_str()); ./PowerEditor/src/NppBigSwitch.cpp:1943: lstrcpy((LPTSTR)lParam, langName.c_str()); ./PowerEditor/src/NppBigSwitch.cpp:1951: lstrcpy((LPTSTR)lParam, langDesc.c_str()); ./PowerEditor/src/NppIO.cpp:49: lstrcpy(longFileName, fileName); ./PowerEditor/src/NppNotification.cpp:693: lstrcpy(lpttt->szText, tipTmp.c_str()); ./PowerEditor/src/NppNotification.cpp:704: lstrcpy(docTip, tipTmp.c_str()); ./PowerEditor/src/NppNotification.cpp:716: lstrcpy(docTip, tipTmp.c_str()); ./PowerEditor/src/Parameters.cpp:716: lstrcpy(str2scan, str2cut); ./PowerEditor/src/Parameters.cpp:1150: lstrcpy(nppDirLocation, _nppPath.c_str()); ./PowerEditor/src/Parameters.cpp:3073: lstrcpy(_userLangArray[_nbUserLang - 1]->_keywordLists[SCE_USER_KWLIST_DELIMITERS], temp.c_str()); ./PowerEditor/src/Parameters.cpp:3107: lstrcpy(_userLangArray[_nbUserLang - 1]->_keywordLists[SCE_USER_KWLIST_COMMENTS], temp.c_str()); ./PowerEditor/src/Parameters.cpp:3115: lstrcpy(_userLangArray[_nbUserLang - 1]->_keywordLists[id], kwl); ./PowerEditor/src/Parameters.cpp:4499: lstrcpyn(_nppGUI._defaultDir, path, MAX_PATH); ./PowerEditor/src/Parameters.cpp:6261: style._styleDesc = lstrcpy(str, style._styleDesc); ./PowerEditor/src/Parameters.cpp:6265: style._fontName = lstrcpy(str, style._fontName); ./PowerEditor/src/Parameters.h:1008: lstrcpy(this->_keywordLists[i], ulc._keywordLists[i]); ./PowerEditor/src/ScitillaComponent/Buffer.cpp:476: lstrcpy(fullpath, filename); // we restore fullpath with filename, in our case is "new #" ./PowerEditor/src/ScitillaComponent/Buffer.cpp:1300: lstrcpy(fn2copy, buf->getFullPathName()); ./PowerEditor/src/ScitillaComponent/FindReplaceDlg.cpp:1164: lstrcpy(pText, txt2find); ./PowerEditor/src/ScitillaComponent/FindReplaceDlg.cpp:1360: lstrcpy(pTextFind, txt2find); ./PowerEditor/src/ScitillaComponent/FindReplaceDlg.cpp:1361: lstrcpy(pTextReplace, txt2replace); ./PowerEditor/src/ScitillaComponent/FindReplaceDlg.cpp:1520: lstrcpy(pTextFind, str2Search.c_str()); ./PowerEditor/src/ScitillaComponent/FindReplaceDlg.cpp:1526: lstrcpy(pTextFind, txt2find); ./PowerEditor/src/ScitillaComponent/FindReplaceDlg.cpp:1544: lstrcpy(pTextReplace, str2Replace.c_str()); ./PowerEditor/src/ScitillaComponent/FindReplaceDlg.cpp:1550: lstrcpy(pTextReplace, txt2replace); ./PowerEditor/src/ScitillaComponent/FunctionCallTip.cpp:260: lstrcpy(_funcName, funcToken.token); ./PowerEditor/src/ScitillaComponent/ScintillaEditView.cpp:1779: lstrcpy(txt, txtW); ./PowerEditor/src/ScitillaComponent/ScintillaEditView.cpp:1810: lstrcpy(txt, txtW); ./PowerEditor/src/ScitillaComponent/ScintillaEditView.cpp:1902: lstrcpyn(line, lineW, lineBufferLen); ./PowerEditor/src/ScitillaComponent/ScintillaEditView.cpp:2466: lstrcpy(str, j); ./PowerEditor/src/ScitillaComponent/UserDefineDialog.cpp:526: lstrcpy(_pUserLang->_keywordLists[index], newList); ./PowerEditor/src/ScitillaComponent/UserDefineDialog.cpp:933: lstrcpy(_pUserLang->_keywordLists[SCE_USER_KWLIST_DELIMITERS], newList); ./PowerEditor/src/TinyXml/tinyxml.cpp:111: lstrcpy( buffer, str.c_str() ); ./PowerEditor/src/TinyXml/tinyXmlA/tinystrA.cpp:51: // strcpy (newstring, instring); ./PowerEditor/src/TinyXml/tinyXmlA/tinystrA.cpp:76: // strcpy (newstring, copy . cstring); ./PowerEditor/src/TinyXml/tinyXmlA/tinystrA.cpp:96: // strcpy (newstring, content); ./PowerEditor/src/TinyXml/tinyXmlA/tinystrA.cpp:117: // strcpy (newstring, copy . c_str ()); ./PowerEditor/src/TinyXml/tinyXmlA/tinystrA.cpp:161: // strcpy (new_string, cstring); ./PowerEditor/src/TinyXml/tinyXmlA/tinystrA.cpp:211: // strcpy (new_string, cstring); ./PowerEditor/src/TinyXml/tinyXmlA/tinyxmlA.cpp:115: strcpy( buffer, str.c_str() ); ./PowerEditor/src/tools/NppShell/src/NppShell.cpp:280: lstrcpyn(customText, szDefaultMenutext, TITLE_SIZE); ./PowerEditor/src/tools/NppShell/src/NppShell.cpp:286: lstrcpyn(customCommand, TEXT(""), MAX_PATH); ./PowerEditor/src/tools/NppShell/src/NppShell.cpp:499: lstrcpyn(m_szMenuTitle, szDefaultMenutext, TITLE_SIZE); ./PowerEditor/src/tools/NppShell/src/NppShell.cpp:678: lstrcpynA(pszName, szHelpTextA, cchMax); ./PowerEditor/src/tools/NppShell/src/NppShell.cpp:681: lstrcpynW(wBuffer, szHelpTextW, cchMax); ./PowerEditor/src/tools/NppShell/src/NppShell.cpp:742: lstrcpyn((LPWSTR)file, pszFileName, MAX_PATH); ./PowerEditor/src/tools/NppShell/src/NppShell.cpp:753: lstrcpyn(m_szFilePath, ext, copySize); ./PowerEditor/src/tools/NppShell/src/NppShell.cpp:764: lstrcpyn(szIconFile, m_szModule, cchMax); ./PowerEditor/src/tools/NppShell/src/NppShell.cpp:767: lstrcpyn(szIconFile, m_szCustomPath, cchMax); ./PowerEditor/src/tools/NppShell/src/NppShell.cpp:774: lstrcpyn(szIconFile, TEXT("NppShellIcon"), cchMax); ./PowerEditor/src/tools/NppShell/src/NppShell.cpp:776: lstrcpyn(szIconFile, m_szFilePath, cchMax-len); ./PowerEditor/src/tools/NppShell/src/NppShell.cpp:847: lstrcpyn(lf.lfFaceName, TEXT("Courier New"), LF_FACESIZE); ./PowerEditor/src/WinControls/ColourPicker/WordStyleDlg.cpp:633: lstrcpy(themeFileName, prevThemeName.c_str()); ./PowerEditor/src/WinControls/FunctionList/functionListPanel.cpp:385: lstrcpy(lpttt->szText, _sortTipStr.c_str()); ./PowerEditor/src/WinControls/FunctionList/functionListPanel.cpp:389: lstrcpy(lpttt->szText, _reloadTipStr.c_str()); ./PowerEditor/src/WinControls/Grid/BabyGrid.cpp:418: lstrcpy(buffer, TEXT("")); ./PowerEditor/src/WinControls/Grid/BabyGrid.cpp:465: lstrcpy(buffer, TEXT("")); ./PowerEditor/src/WinControls/Grid/BabyGrid.cpp:1137: lstrcpy(BGHS[SI].editstring, TEXT("")); ./PowerEditor/src/WinControls/Grid/BabyGrid.cpp:1174: lstrcpy(BGHS[SI].editstringdisplay,BGHS[SI].editstring); ./PowerEditor/src/WinControls/Grid/BabyGrid.cpp:1212: lstrcpy(BGHS[j].protect, TEXT("U")); ./PowerEditor/src/WinControls/Grid/BabyGrid.cpp:1247: lstrcpy(BGHS[j].editstring, TEXT("")); ./PowerEditor/src/WinControls/Grid/BabyGrid.cpp:1297: lstrcpy(temptext,text); ./PowerEditor/src/WinControls/Grid/BabyGrid.cpp:1405: lstrcpy(BGHS[SelfIndex].title, TEXT("Title too long (300 chars max)")); ./PowerEditor/src/WinControls/Grid/BabyGrid.cpp:1409: lstrcpy(BGHS[SelfIndex].title,(TCHAR*)lParam); ./PowerEditor/src/WinControls/Grid/BabyGrid.cpp:1567: lstrcpy(BGHS[SelfIndex].protect, TEXT("P")); ./PowerEditor/src/WinControls/Grid/BabyGrid.cpp:1571: lstrcpy(BGHS[SelfIndex].protect, TEXT("U")); ./PowerEditor/src/WinControls/Grid/BabyGrid.cpp:1691: lstrcpy(tbuffer,(TCHAR*)lParam); ./PowerEditor/src/WinControls/Grid/BabyGrid.cpp:1750: lstrcpy(tbuffer,(TCHAR*)lParam); ./PowerEditor/src/WinControls/Grid/BabyGrid.cpp:1759: lstrcpy((TCHAR*)lParam,buffer); ./PowerEditor/src/WinControls/Grid/BabyGrid.cpp:1763: lstrcpy((TCHAR*)lParam, TEXT("")); ./PowerEditor/src/WinControls/Grid/BabyGrid.cpp:2257: lstrcpy(BGHS[SelfIndex].editstring, TEXT("")); ./PowerEditor/src/WinControls/Grid/BabyGrid.cpp:2276: lstrcpy(BGHS[SelfIndex].editstring, TEXT("")); ./PowerEditor/src/WinControls/Grid/BabyGrid.cpp:2826: lstrcpy(BGHS[SelfIndex].protect, TEXT("U")); ./PowerEditor/src/WinControls/Grid/BabyGrid.cpp:3014: lstrcpy(BGHS[BG_GridIndex].title,lpcs->lpszName); ./PowerEditor/src/WinControls/OpenSaveFileDialog/FileDialog.cpp:151: lstrcpy(pFileExt, extFilter.c_str()); ./PowerEditor/src/WinControls/OpenSaveFileDialog/FileDialog.cpp:155: lstrcpy(pFileExt, exts); ./PowerEditor/src/WinControls/OpenSaveFileDialog/FileDialog.cpp:220: lstrcpy(fn, _fileName); ./PowerEditor/src/WinControls/OpenSaveFileDialog/FileDialog.h:78: void setDefFileName(const TCHAR *fn){lstrcpy(_fileName, fn);} ./PowerEditor/src/WinControls/Preference/preferenceDlg.cpp:1317: lstrcpy(nppGUI._defaultDir, inputDir); ./PowerEditor/src/WinControls/ProjectPanel/ProjectPanel.cpp:457: lstrcpy(wsfn, workSpaceFileName); ./PowerEditor/src/WinControls/ProjectPanel/ProjectPanel.cpp:504: lstrcpy(absolutePath, _workSpaceFilePath.c_str()); ./PowerEditor/src/WinControls/ProjectPanel/ProjectPanel.cpp:1041: lstrcpy(textBuffer, strValueLabel); ./PowerEditor/src/WinControls/ProjectPanel/ProjectPanel.cpp:1149: lstrcpy(dir, _workSpaceFilePath.c_str()); ./PowerEditor/src/WinControls/shortcut/shortcut.cpp:192: lstrcpyn(_menuName, name, nameLenMax); ./PowerEditor/src/WinControls/shortcut/shortcut.cpp:193: lstrcpyn(_name, name, nameLenMax); ./PowerEditor/src/WinControls/StaticDialog/RunDlg/RunDlg.cpp:182: lstrcpy(cmdIntermediate, cmdPure); ./PowerEditor/src/WinControls/StaticDialog/RunDlg/RunDlg.cpp:188: lstrcpy(argsIntermediate, args); ./PowerEditor/src/WinControls/TrayIcon/trayIconControler.cpp:40: lstrcpy(_nid.szTip, tip); ./PowerEditor/src/WinControls/VerticalFileSwitcher/VerticalFileSwitcher.cpp:142: lstrcpyn(pGetInfoTip->pszText, fn.c_str(), pGetInfoTip->cchTextMax); ./PowerEditor/src/WinControls/VerticalFileSwitcher/VerticalFileSwitcherListView.cpp:124: lstrcpy(fn, ::PathFindFileName(fileNameStatus._fn.c_str())); ./PowerEditor/src/WinControls/VerticalFileSwitcher/VerticalFileSwitcherListView.cpp:183: lstrcpy(fn, ::PathFindFileName(buf->getFileName())); ./PowerEditor/src/WinControls/VerticalFileSwitcher/VerticalFileSwitcherListView.cpp:263: lstrcpy(fn, ::PathFindFileName(fileName)); ./scintilla/boostregex/BoostRegExSearch.cxx:474: strcpy(charPtr, str.c_str()); ./scintilla/gtk/Converter.h:47: strcpy(fullDest, charSetDestination); ./scintilla/lexers/LexAU3.cxx:243: strcpy(s_save,s); ./scintilla/lexers/LexEScript.cxx:240: strcpy(prevWord, s); ./scintilla/lexers/LexEScript.cxx:255: strcpy(prevWord, ""); ./scintilla/lexers/LexFortran.cxx:415: strcpy(prevWord, s); ./scintilla/lexers/LexFortran.cxx:445: strcpy(prevWord, ""); ./scintilla/lexers/LexHTML.cxx:380: strcpy(prevWord, s); ./scintilla/lexers/LexHTML.cxx:932: strcpy(makoBlockType, "%"); ./scintilla/lexers/LexHTML.cxx:934: strcpy(makoBlockType, "{"); ./scintilla/lexers/LexHTML.cxx:1003: strcpy(djangoBlockType, "%"); ./scintilla/lexers/LexHTML.cxx:1005: strcpy(djangoBlockType, "{"); ./scintilla/lexers/LexHTML.cxx:1919: strcpy(phpStringDelimiter, "\""); ./scintilla/lexers/LexHTML.cxx:1929: strcpy(phpStringDelimiter, "\'"); ./scintilla/lexers/LexHTML.cxx:2049: strcpy(phpStringDelimiter, "\""); ./scintilla/lexers/LexHTML.cxx:2059: strcpy(phpStringDelimiter, "\'"); ./scintilla/lexers/LexPowerPro.cxx:157: strcpy(s_save,s); ./scintilla/lexers/LexRuby.cxx:143: strcpy(prevWord, s); ./scintilla/lexers/LexScriptol.cxx:50: strcpy(prevWord, s); ./scintilla/lexers/LexVHDL.cxx:229: strcpy(prevWord, s); ./scintilla/lexers/LexVHDL.cxx:243: strcpy(prevWord, ";"); ./scintilla/lexers/LexVHDL.cxx:296: strcpy(prevWord, ";"); ./scintilla/lexers/LexVHDL.cxx:385: strcpy(prevWord, s); ./scintilla/lexlib/PropSetSimple.cxx:149: strcpy(result, val.c_str()); ./scintilla/lexlib/WordList.cxx:124: strcpy(list, s); ./scintilla/qt/ScintillaEditBase/PlatQt.cpp:1127: strcpy(fontNameDefault, font.family().toUtf8()); ./scintilla/src/CaseConvert.cxx:377: strcpy(conversion.conversion, conversion_); ./scintilla/src/Editor.cxx:7335: strcpy(CharPtrFromSPtr(lParam), vs.styles[wParam].fontName); ./scintilla/src/Editor.cxx:7357: strcpy(ptr, val); ./scintilla/src/Editor.cxx:9288: strcpy(CharPtrFromSPtr(lParam), ""); ./scintilla/src/Editor.cxx:9312: strcpy(CharPtrFromSPtr(lParam), ""); ./scintilla/src/Editor.cxx:9331: strcpy(CharPtrFromSPtr(lParam), ""); ./scintilla/src/Editor.cxx:9355: strcpy(CharPtrFromSPtr(lParam), ""); ./scintilla/src/ExternalLexer.cxx:71: strcpy(lexname, ""); ./scintilla/src/ScintillaBase.cxx:384: strcpy(buffer, selected.c_str()); ./scintilla/src/ViewStyle.cxx:59: strcpy(nameSave, name); ./scintilla/win32/ScintillaWin.cxx:2140: strcpy(lf.lfFaceName, vs.styles[styleHere].fontName);
Windbg session:
[29,000 lines of stepping...] 0:000> g Breakpoint 1 hit eax=0011af7d ebx=06e3de58 ecx=06e37c2a edx=0011945d esi=0011945c edi=06e37ce8 eip=0040cbe0 esp=0013947c ebp=0c5229b8 iopl=0 nv up ei pl nz na po nc cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000202 npp+0xcbe0: 0040cbe0 8a08 mov cl,byte ptr [eax] ds:002b:0011af7d=2a 0:000> g Breakpoint 1 hit eax=0011af7e ebx=06e3de58 ecx=06e37c2a edx=0011945d esi=0011945c edi=06e37ce8 eip=0040cbe0 esp=0013947c ebp=0c5229b8 iopl=0 nv up ei pl nz na po nc cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000202 npp+0xcbe0: 0040cbe0 8a08 mov cl,byte ptr [eax] ds:002b:0011af7e=2a 0:000> BL 0 e 18bb232b 0001 (0001) 0:**** NppExport!setInfo 1 e 0040cbe0 0001 (0001) 0:**** npp+0xcbe0 0:000> bp npp+0xcbe0 "j @eax==0 ''; 'gc'" breakpoint 1 redefined 0:000> g ModLoad: 1f2d0000 1f39b000 C:\Program Files (x86)\Aspell\bin\aspell-15.dll (3658.37a8): Break instruction exception - code 80000003 (first chance) eax=7fea6000 ebx=00000000 ecx=00000000 edx=7713ac18 esi=00000000 edi=00000000 eip=770b98fc esp=1ffcff60 ebp=1ffcff8c iopl=0 nv up ei pl zr na pe nc cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000246 ntdll!DbgBreakPoint: 770b98fc cc int 3 0:006> g (3658.3470): Access violation - code c0000005 (first chance) First chance exceptions are reported before any exception handling. This exception may be expected and handled. eax=1adc4000 ebx=1da23f28 ecx=06e37cf0 edx=1adc3ff9 esi=1adc3ff8 edi=06e37ce8 eip=0040cbe0 esp=00137824 ebp=0c5229b8 iopl=0 nv up ei ng nz na pe nc cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00010286 npp+0xcbe0: 0040cbe0 8a08 mov cl,byte ptr [eax] ds:002b:1adc4000=?? 0:000> bl 0 e 18bb232b 0001 (0001) 0:**** NppExport!setInfo 1 e 0040cbe0 0001 (0001) 0:**** npp+0xcbe0 "j @eax==0 ''; 'gc'" 0:000> !analyze -v ERROR: FindPlugIns 8007007b ERROR: Some plugins may not be available [8007007b] ******************************************************************************* * * * Exception Analysis * * * ******************************************************************************* SYMSRV: c:\code\symbols*http://chromium- browser-symsrv.commondatastorage.googleapis.com needs a downstream store SYMSRV: c:\code\symbols*http://chromium- browser-symsrv.commondatastorage.googleapis.com needs a downstream store SYMSRV: c:\code\symbols*http://chromium- browser-symsrv.commondatastorage.googleapis.com needs a downstream store SYMSRV: c:\code\symbols*http://chromium- browser-symsrv.commondatastorage.googleapis.com needs a downstream store SYMSRV: c:\code\symbols*http://chromium- browser-symsrv.commondatastorage.googleapis.com needs a downstream store SYMSRV: c:\code\symbols*http://chromium- browser-symsrv.commondatastorage.googleapis.com needs a downstream store SYMSRV: c:\code\symbols*http://chromium- browser-symsrv.commondatastorage.googleapis.com needs a downstream store SYMSRV: c:\code\symbols*http://chromium- browser-symsrv.commondatastorage.googleapis.com needs a downstream store *** ERROR: Symbol file could not be found. Defaulted to export symbols for C:\WINDOWS\AppPatch\EMET.DLL - *** ERROR: Symbol file could not be found. Defaulted to export symbols for C:\Program Files (x86)\Notepad++\plugins\DSpellCheck.dll - FAULTING_IP: npp+cbe0 0040cbe0 8a08 mov cl,byte ptr [eax] EXCEPTION_RECORD: ffffffff -- (.exr 0xffffffffffffffff) ExceptionAddress: 0040cbe0 (npp+0x0000cbe0) ExceptionCode: c0000005 (Access violation) ExceptionFlags: 00000000 NumberParameters: 2 Parameter[0]: 00000000 Parameter[1]: 1adc4000 Attempt to read from address 1adc4000 CONTEXT: 00000000 -- (.cxr 0x0;r) eax=1adc4000 ebx=1da23f28 ecx=06e37cf0 edx=1adc3ff9 esi=1adc3ff8 edi=06e37ce8 eip=0040cbe0 esp=00137824 ebp=0c5229b8 iopl=0 nv up ei ng nz na pe nc cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00010284 npp+0xcbe0: 0040cbe0 8a08 mov cl,byte ptr [eax] ds:002b:1adc4000=?? FAULTING_THREAD: 00003470 PROCESS_NAME: npp.exe ERROR_CODE: (NTSTATUS) 0xc0000005 - The instruction at 0x%08lx referenced memory at 0x%08lx. The memory could not be %s. EXCEPTION_CODE: (NTSTATUS) 0xc0000005 - The instruction at 0x%08lx referenced memory at 0x%08lx. The memory could not be %s. EXCEPTION_PARAMETER1: 00000000 EXCEPTION_PARAMETER2: 1adc4000 READ_ADDRESS: 1adc4000 FOLLOWUP_IP: npp+cbe0 0040cbe0 8a08 mov cl,byte ptr [eax] NTGLOBALFLAG: 2000100 APPLICATION_VERIFIER_FLAGS: 81643067 APP: npp.exe ANALYSIS_VERSION: 6.3.9600.16384 (debuggers(dbg).130821-1623) x86fre ADDITIONAL_DEBUG_TEXT: Exploitable because of a buffer overrun or underrun when full pageheap is enabled BUGCHECK_STR: APPLICATION_FAULT_INVALID_POINTER_READ_EXPLOITABLE PRIMARY_PROBLEM_CLASS: INVALID_POINTER_READ_EXPLOITABLE DEFAULT_BUCKET_ID: INVALID_POINTER_READ_EXPLOITABLE LAST_CONTROL_TRANSFER: from 004105ab to 0040cbe0 STACK_TEXT: WARNING: Stack unwind information not available. Following frames may be wrong. 00137824 004105ab 1adc3ff8 c7637d85 76e4590a npp+0xcbe0 00137828 1adc3ff8 c7637d85 76e4590a 0013a294 npp+0x105ab 0013782c c7637d85 76e4590a 0013a294 0eaca8f0 0x1adc3ff8 00137830 76e45909 0013a294 0eaca8f0 0018edb8 0xc7637d85 00137834 0013a294 0eaca8f0 0018edb8 012213b0 SHLWAPI!SHFreeDataBlockList+0xa 00137838 0eaca8f0 0018edb8 012213b0 1da23f28 0x13a294 0013a294 0013a284 0013a5c8 0057491c 00400000 0xeaca8f0 0013a298 0013a5c8 0057491c 00400000 00100dda 0x13a284 0013a29c 0057491c 00400000 00100dda 000b0d06 0x13a5c8 0013a5c8 00400000 00100dda 000a0dae 00000013 npp+0x17491c 0013a5cc 00100dda 000a0dae 00000013 00000001 npp 0013a5d0 000a0dae 00000013 00000001 028a0047 0x100dda 0013a5d4 00000000 00000001 028a0047 038a0029 0xa0dae STACK_COMMAND: .cxr 0x0 ; kb SYMBOL_STACK_INDEX: 0 SYMBOL_NAME: npp+cbe0 FOLLOWUP_NAME: MachineOwner MODULE_NAME: npp DEBUG_FLR_IMAGE_TIMESTAMP: 53d579ca FAILURE_BUCKET_ID: INVALID_POINTER_READ_EXPLOITABLE_c0000005_notepad++.exe!Unknown BUCKET_ID: APPLICATION_FAULT_INVALID_POINTER_READ_EXPLOITABLE_npp+cbe0 IMAGE_NAME: notepad++.exe ANALYSIS_SOURCE: UM FAILURE_ID_HASH_STRING: um:invalid_pointer_read_exploitable_c0000005_notepad++.exe!unknown FAILURE_ID_HASH: {32293818-bbe7-ba7c-d491-6fa48a74f722} Followup: MachineOwner --------- 0:000> lmvm npp start end module name 00400000 0065e000 npp (no symbols) Loaded symbol image file: C:\Program Files (x86)\Notepad++\notepad++.exe Image path: npp.exe Image name: npp.exe Timestamp: Sun Jul 27 18:14:34 2014 (53D579CA) CheckSum: 00226C3F ImageSize: 0025E000 File version: 6.6.8.0 Product version: 6.6.8.0 File flags: 0 (Mask 3F) File OS: 40004 NT Win32 File type: 1.0 App File date: 00000000.00000000 Translations: 0409.04b0 CompanyName: Don HO don.h@free.fr ProductName: Notepad++ InternalName: npp.exe OriginalFilename: Notepad++.exe ProductVersion: 6.68 FileVersion: 6.68 FileDescription: Notepad++ : a free (GNU) source code editor LegalCopyright: Copyleft 1998-2013 by Don HO 0:000> g ======================================= VERIFIER STOP 00000013: pid 0x3658: First chance access violation for current stack trace. 1ADC4000 : Invalid address causing the exception. 0040CBE0 : Code address executing the invalid access. 00137370 : Exception record. 001373C0 : Context record. ======================================= This verifier stop is continuable. After debugging it use `go' to continue. ======================================= (3658.3470): Break instruction exception - code 80000003 (first chance) eax=70e5f01c ebx=00000000 ecx=000001a1 edx=000001cb esi=70e5db08 edi=1adc4000 eip=70e53466 esp=00136f68 ebp=0013716c iopl=0 nv up ei pl nz na po nc cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000202 vrfcore!VerifierStopMessageEx+0x571: 70e53466 cc int 3 0:000> !analyze -v ERROR: FindPlugIns 8007007b ERROR: Some plugins may not be available [8007007b] ******************************************************************************* * * * Exception Analysis * * * ******************************************************************************* APPLICATION_VERIFIER_HEAPS_FIRST_CHANCE_ACCESS_VIOLATION (13) First chance access violation for current stack trace. This is the most common application verifier stop. Typically it is caused by a buffer overrun error. The heap verifier places a non-accessible page at the end of a heap allocation and a buffer overrun will cause an exception by touching this page. To debug this stop identify the access address that caused the exception and then use the following debugger command: !heap -p -a ACCESS_ADDRESS This command will give details about the nature of the error and what heap block is overrun. It will also give the stack trace for the block allocation. There are several other causes for this stop. For example accessing a heap block after being freed. The same debugger command will be useful for this case too. Arguments: Arg1: 1adc4000, Invalid address causing the exception. Arg2: 0040cbe0, Code address executing the invalid access. Arg3: 00137370, Exception record. Arg4: 001373c0, Context record. SYMSRV: c:\code\symbols*http://chromium- browser-symsrv.commondatastorage.googleapis.com needs a downstream store SYMSRV: c:\code\symbols*http://chromium- browser-symsrv.commondatastorage.googleapis.com needs a downstream store SYMSRV: c:\code\symbols*http://chromium- browser-symsrv.commondatastorage.googleapis.com needs a downstream store SYMSRV: c:\code\symbols*http://chromium- browser-symsrv.commondatastorage.googleapis.com needs a downstream store SYMSRV: c:\code\symbols*http://chromium- browser-symsrv.commondatastorage.googleapis.com needs a downstream store SYMSRV: c:\code\symbols*http://chromium- browser-symsrv.commondatastorage.googleapis.com needs a downstream store SYMSRV: c:\code\symbols*http://chromium- browser-symsrv.commondatastorage.googleapis.com needs a downstream store SYMSRV: c:\code\symbols*http://chromium- browser-symsrv.commondatastorage.googleapis.com needs a downstream store SYMSRV: c:\code\symbols*http://chromium- browser-symsrv.commondatastorage.googleapis.com needs a downstream store SYMSRV: c:\code\symbols*http://chromium- browser-symsrv.commondatastorage.googleapis.com needs a downstream store SYMSRV: c:\code\symbols*http://chromium- browser-symsrv.commondatastorage.googleapis.com needs a downstream store SYMSRV: c:\code\symbols*http://chromium- browser-symsrv.commondatastorage.googleapis.com needs a downstream store SYMSRV: c:\code\symbols*http://chromium- browser-symsrv.commondatastorage.googleapis.com needs a downstream store SYMSRV: c:\code\symbols*http://chromium- browser-symsrv.commondatastorage.googleapis.com needs a downstream store SYMSRV: c:\code\symbols*http://chromium- browser-symsrv.commondatastorage.googleapis.com needs a downstream store FAULTING_IP: npp+cbe0 0040cbe0 8a08 mov cl,byte ptr [eax] EXCEPTION_RECORD: 00137370 -- (.exr 0x137370) ExceptionAddress: 0040cbe0 (npp+0x0000cbe0) ExceptionCode: c0000005 (Access violation) ExceptionFlags: 00000000 NumberParameters: 2 Parameter[0]: 00000000 Parameter[1]: 1adc4000 Attempt to read from address 1adc4000 CONTEXT: 001373c0 -- (.cxr 0x1373c0;r) eax=1adc4000 ebx=1da23f28 ecx=06e37cf0 edx=1adc3ff9 esi=1adc3ff8 edi=06e37ce8 eip=0040cbe0 esp=00137824 ebp=0c5229b8 iopl=0 nv up ei ng nz na pe nc cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00010286 npp+0xcbe0: 0040cbe0 8a08 mov cl,byte ptr [eax] ds:002b:1adc4000=?? Last set context: eax=1adc4000 ebx=1da23f28 ecx=06e37cf0 edx=1adc3ff9 esi=1adc3ff8 edi=06e37ce8 eip=0040cbe0 esp=00137824 ebp=0c5229b8 iopl=0 nv up ei ng nz na pe nc cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00010286 npp+0xcbe0: 0040cbe0 8a08 mov cl,byte ptr [eax] ds:002b:1adc4000=?? Resetting default scope FAULTING_THREAD: 00003470 DEFAULT_BUCKET_ID: INVALID_POINTER_READ PROCESS_NAME: npp.exe ERROR_CODE: (NTSTATUS) 0x80000003 - {EXCEPTION} Breakpoint A breakpoint has been reached. EXCEPTION_CODE: (HRESULT) 0x80000003 (2147483651) - One or more arguments are invalid EXCEPTION_PARAMETER1: 00000000 NTGLOBALFLAG: 2000100 APPLICATION_VERIFIER_FLAGS: 81643067 APP: npp.exe ANALYSIS_VERSION: 6.3.9600.16384 (debuggers(dbg).130821-1623) x86fre LAST_CONTROL_TRANSFER: from 70e57e3c to 70e53466 PRIMARY_PROBLEM_CLASS: INVALID_POINTER_READ BUGCHECK_STR: APPLICATION_FAULT_INVALID_POINTER_READ STACK_TEXT: 00137824 0040cbe0 npp+0xcbe0 0013782c 004105ab npp+0x105ab 00137830 1adc3ff8 unknown!unknown+0x0 004105b3 1bd9f7ff unknown!unknown+0x0 004105b7 89c823c9 unknown!unknown+0x0 004105bb 07eb384e unknown!unknown+0x0 004105bf 003846c7 unknown!unknown+0x0 004105c3 80000000 unknown!unknown+0x0 004105c7 7400047e shcore!Windows::Internal::COperationLambda0<Windows::Internal::CCallAsyncStagedLambda< >,Windows::Internal::ProgressResult<Windows::Internal::CBasicResult,unsigned int> >::Run+0xeb 004106c7 005583e3 npp+0x1583e3 004106d7 33005c3f unknown!unknown+0x0 004106db 244489c4 unknown!unknown+0x0 004106df 57565334 unknown!printable+0x0 004106e3 5c3ff0a1 unknown!unknown+0x0 004106e7 50c43300 vfprint!`string'+0x20 004106eb 4824448d unknown!unknown+0x0 FOLLOWUP_IP: npp+cbe0 0040cbe0 8a08 mov cl,byte ptr [eax] SYMBOL_STACK_INDEX: 0 SYMBOL_NAME: npp+cbe0 FOLLOWUP_NAME: MachineOwner MODULE_NAME: npp DEBUG_FLR_IMAGE_TIMESTAMP: 53d579ca STACK_COMMAND: .cxr 1373c0 ; kb ; dps 137824 ; kb BUCKET_ID: APPLICATION_FAULT_INVALID_POINTER_READ_npp+cbe0 IMAGE_NAME: notepad++.exe FAILURE_BUCKET_ID: INVALID_POINTER_READ_80000003_notepad++.exe!Unknown ANALYSIS_SOURCE: UM FAILURE_ID_HASH_STRING: um:invalid_pointer_read_80000003_notepad++.exe!unknown FAILURE_ID_HASH: {742c053d-8ef7-d5b0-3d1d-e8b1579a7e40} Followup: MachineOwner ---------
Epilogue
Lately I’ve been running a tremendous number of applications under Application Verifier – and I’ve observed bugs in everything from Dropbox & GitHub, to Media Player Classic-Home Cinema and Xaos. I’ve been thinking about the security implications of the numerous bugs; what if running everyday applications under dynamic analysis can mitigate exploits?
I’m certain that the kind of stress imposed on software during the course of everyday usage is far greater than that which can be applied in any kind of formal testing – but what if, in addition to detecting bugs, dynamic analysis is an easy method of preventing zero-day attacks?
Perhaps I’ll write a full article about it, but until then, I’d like to hear your thoughts. What do you think?