Same c++ code sometimes works and sometimes doesnt

1

my c++ code works for a couple of times straight and after few executions it suddenly stops working and throws exceptions (without any changes!), and I cant figure out why.

This is the problematic part of code:

STARTUPINFO si;
PROCESS_INFORMATION pi;
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
ZeroMemory(&pi, sizeof(pi));
TCHAR *path;
SHGetKnownFolderPath(FOLDERID_Startup, KF_FLAG_CREATE, NULL, &path);
lstrcat(path, L"\\calc.exe");
if (CreateProcess(NULL, path, NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi))
{
    WaitForSingleObject(pi.hProcess, INFINITE);
    CloseHandle(pi.hProcess);
    CloseHandle(pi.hThread);

}

After a few executions, 2 exceptions are thrown on the CreateProcess() line, the first one:

Unhandled exception at 0x779D8829 (ntdll.dll) in PS_Down.exe: 0xC0000374: A heap has been corrupted (parameters: 0x77A15890).

The second:

Exception thrown at 0x77946111 (ntdll.dll) in PS_Down.exe: 0xC0000005: Access violation reading location 0x00000069. 

It happened to me with a few other projects (that don't include the CreateProcess() func) and iv'e noticed that it always happens when TCHAR and SHGetKnownFolderPath() are involved. Any help with understanding how to fix the issue will be much appreciated, thanks in advance!

P.S - I'm new to coding in cpp, so please try to explain accordingly

c++
winapi
asked on Stack Overflow Dec 6, 2018 by Apex • edited Dec 6, 2018 by Jabberwocky

2 Answers

8

lstrcat(path, L"\\calc.exe"); will cause a buffer overrun. path is a pointer to array that can contain only folder path, nothing more. You will need to allocate a wide string, append folder path and then file path. Also you will need to check result of SHGetKnownFolderPath to determine whether path contains a valid pointer and free it later by calling CoTaskMemFree.

answered on Stack Overflow Dec 6, 2018 by user7860670
1

path is allocated with a fixed length by SHGetKnownFolderPath so you can't concatenate your executable to it directly. You'd need to use CoTaskMemRealloc to expand the space first. You also need to free the memory after you've used it. Similarly, you need to close the handles created by CreateProcess.

You could therefore make support classes to handle the resources automatically:

#include "pch.h"
#include <iostream>

#include <windows.h>
#include <Shlobj.h>
#include <wchar.h>

// A wide string exception class. perhaps something like this already exist in VS?
class werror {
    std::wstring text;
public:
    werror(const wchar_t* Text) : text(Text) {}
    const wchar_t* what() const { return text.c_str(); }
};

class ConCatToKnownFolderPath {
    PWSTR m_path;
public:
    ConCatToKnownFolderPath(REFKNOWNFOLDERID rfid, DWORD dwFlags, HANDLE hToken, const WCHAR* AddToPath = nullptr) :
        m_path(nullptr)
    {
        if (SHGetKnownFolderPath(rfid, dwFlags, hToken, &m_path) != S_OK)
            throw werror(L"SHGetKnownFolderPath failed");

        if (AddToPath) {
            size_t newlen = wcslen(m_path) + wcslen(AddToPath) + sizeof(WCHAR); // place for \0
            size_t newbufsize = newlen * sizeof(WCHAR);

            auto newPtr = CoTaskMemRealloc(m_path, newbufsize);
            if (!newPtr) {
                CoTaskMemFree(m_path);
                throw werror(L"CoTaskMemRealloc failed");
            }
            m_path = reinterpret_cast<PWSTR>(newPtr);
            wcscat_s(m_path, newlen, AddToPath);
        }
    }
    // move works fine
    ConCatToKnownFolderPath(ConCatToKnownFolderPath&& other) noexcept :
        m_path(other.m_path)
    {
        other.m_path = nullptr;
    }
    ConCatToKnownFolderPath& operator=(ConCatToKnownFolderPath&& other) noexcept {
        if (m_path) CoTaskMemFree(m_path);
        m_path = other.m_path;
        other.m_path = nullptr;
        return *this;
    }
    // copy not supported (but could easily be added
    ConCatToKnownFolderPath(const ConCatToKnownFolderPath&) = delete;
    ConCatToKnownFolderPath& operator=(const ConCatToKnownFolderPath&) = delete;

    // automatic free when it goes out of scope
    ~ConCatToKnownFolderPath() {
        if (m_path) CoTaskMemFree(m_path);
    }

    PWSTR data() const { return m_path; }
    operator LPCWSTR () const { return m_path; }
};

struct WProcessWithInfo : PROCESS_INFORMATION {
    WProcessWithInfo(LPCWSTR lpApplicationName, LPWSTR lpCommandLine, LPCWSTR lpCurrentDirectory) {
        STARTUPINFOW si;
        ZeroMemory(&si, sizeof(STARTUPINFOW));
        si.cb = sizeof(si);
        si.dwFlags = STARTF_USESHOWWINDOW;
        si.wShowWindow = SW_SHOWNORMAL;

        if (!CreateProcessW(lpApplicationName, lpCommandLine, NULL, NULL, FALSE, 0, NULL, lpCurrentDirectory, &si, *this))
            throw werror(L"CreateProcessWCreateProcessW failed");
        CloseHandle(hThread);
    }
    WProcessWithInfo(const WProcessWithInfo&) = delete;
    WProcessWithInfo(WProcessWithInfo&&) = delete;
    WProcessWithInfo& operator=(const WProcessWithInfo&) = delete;
    WProcessWithInfo& operator=(WProcessWithInfo&&) = delete;
    ~WProcessWithInfo() {
        CloseHandle(hProcess);
    }
    DWORD Wait(DWORD  dwMilliseconds=INFINITE) {
        return WaitForSingleObject(*this, dwMilliseconds);
    }

    operator HANDLE () { return hProcess; }
    operator LPPROCESS_INFORMATION () { return this; }
};


int main() {
    try {
        ConCatToKnownFolderPath path(FOLDERID_System, KF_FLAG_CREATE, NULL, L"\\calc.exe");
        std::wcout << L"Starting " << path.data() << L"\n";
        WProcessWithInfo proc(path, NULL, NULL);
        std::wcout << L"Process started\n";
        proc.Wait();
        std::wcout << L"Process done\n";
    }
    catch (const werror& ex) {
        std::wcerr << L"Exception: " << ex.what() << L"\n";
    }
    return 0;
}
answered on Stack Overflow Dec 6, 2018 by Ted Lyngmo • edited Dec 9, 2018 by Ted Lyngmo

User contributions licensed under CC BY-SA 3.0