Question

I am trying to use cryptopp, the below code cause access violation at the stringsource function. What could be the possible cause of this ? I have previously run the similar code with little difference successfully.

AesHelper.cpp

#include "dll.h"
#include "AesHelper.h"

#include "aes.h"
using CryptoPP::AES;
#include "ccm.h"
using CryptoPP::CBC_Mode;

#include "filters.h"
using CryptoPP::StringSink;
using CryptoPP::StringSource;
using CryptoPP::StreamTransformationFilter;

#include "hex.h"
using CryptoPP::HexEncoder;
using CryptoPP::HexDecoder;

#include <string>
using namespace std;

#include "osrng.h"
using CryptoPP::AutoSeededRandomPool;

byte AesHelper::_key[AES::DEFAULT_KEYLENGTH];
byte AesHelper::_iv[AES::BLOCKSIZE];

void AesHelper::encrypt(const char* str, char ** outIv, char ** encrypted )
{
    try
    {
        AutoSeededRandomPool prng;

        byte key[AES::DEFAULT_KEYLENGTH];
        prng.GenerateBlock(key, sizeof(key));

        byte iv[AES::BLOCKSIZE];
        prng.GenerateBlock(iv, sizeof(iv));

        string cipher, encoded;
        string plain = "CBC Test Mode";

        CBC_Mode<AES>::Encryption e;
        e.SetKeyWithIV(key, sizeof(key), iv);

        // The StreamTransformationFilter removes
        //  padding as required.
        StreamTransformationFilter *stf = new StreamTransformationFilter(e,
                new StringSink(cipher),
                CryptoPP::BlockPaddingSchemeDef::ZEROS_PADDING
            );

        StringSource s(plain, true, stf); // This line cause Access Violation

        StreamTransformationFilter filter(e);
        filter.Put((const byte*)plain.data(), plain.size());
        filter.MessageEnd();

        const size_t ret = filter.MaxRetrievable();
        cipher.resize(ret);
        filter.Get((byte*)cipher.data(), cipher.size());

        //encode the cipher to hexadecimal
        StringSource(cipher, true,
        new HexEncoder(
                new StringSink(encoded)
            ) // HexEncoder
        ); // StringSource


        //set the output parameter
        outIv = (char**)_iv;
        encrypted = (char**)cipher.c_str();
    }
    catch(const CryptoPP::Exception& e)
    {
        cerr << "exception : " << e.what() << endl;
        exit(1);
    }

}

Error

Unhandled exception at 0x550714CA (cryptopp.dll) in PaymentManager.exe: 0xC0000005: Access violation reading location 0x74736554.

cryptopp.dll!memcpy(unsigned char * dst, unsigned char * src, unsigned long count) Line 188 Unknown

UPDATE : Problem solved after making both DLL and Exe program to "Release". But now there's new problem. on this line, the problem also in stringsource function

StringSource(cipher, true,
            new HexEncoder(
                    new StringSink(encoded)
                ) // HexEncoder
            ); // StringSource

error

PaymentManager.exe has triggered a breakpoint.

The program stop at

void __cdecl _free_base (void * pBlock) {

        int retval = 0;


        if (pBlock == NULL)
            return;

        RTCCALLBACK(_RTC_Free_hook, (pBlock, 0));

        retval = HeapFree(_crtheap, 0, pBlock); // program stop at this line
        if (retval == 0)
        {
            errno = _get_errno_from_oserr(GetLastError());
        } }
Was it helpful?

Solution 2

I don't know this library, but as I see StringSource get std::string as first parameter. In this case you should be absolutely sure, that both DLL and your program compiled and linked with exactly the same STL, with exactly the same compilers and their options

OTHER TIPS

0x74736554 is hex for the four ASCII characters "tseT" (big-endian) or "Test" (little-endian)—the latter of which is exactly the bytes at indices 4-7 of your string plain. The fact that the StringSource constructor is trying to read that address suggests that your executable and the DLL disagree on what a std::string looks like. In particular, the library is dereferencing the memory address at offset 4 from the object you pass it, but the object you pass it doesn't have a valid pointer value there.

In other words, the string you're passing (or possibly some sub-object of it) looks like this in memory:

Offset    0      1      2      3      4      5      6      7
       +------+------+------+------+------+------+------+------+--
Value  | 0x43 | 0x42 | 0x43 | 0x20 | 0x54 | 0x65 | 0x73 | 0x74 | ...
       | 'C'  | 'B'  | 'C'  | ' '  | 'T'  | 'e'  | 's'  | 't'  | ...
       +------+------+------+------+------+------+------+------+--

But, the library is treating it like this:

Offset    0      1      2      3      4      5      6      7
       +------+------+------+------+------+------+------+------+--
Value  |           ?????           | Pointer to character data | ...
       +------+------+------+------+------+------+------+------+--

I figured this all out by just realizing that the address causing the fault consists entirely of ASCII values which match the values from the source code.

The cause of this is almost certainly your code and the library using different std::string implementations, which have different object layouts. It's exactly the same problem as Allocating and freeing memory across module boundaries. In order to pass C++ objects between modules (i.e. the main executable and any DLLs it loads), both modules need to agree on how the object is laid out. If the modules were compiled at different times, then you need to work harder to ensure that they're compiled against the same header files.

If you're compiling the DLL from source, then the easiest thing to do is to make sure that both the DLL and your executable are using the same C++ standard library implementation. If you're using a DLL that's already been compiled by someone else, then you need to ask them or check the documentation to find the C++ standard library against which it was compiled, and then compile your executable against the same library.

If you can't do that, then the next best solution is to avoid passing C++ objects across module boundaries in all cases—use only APIs that take predefined data types (like integers and raw pointers) or data types defined in the DLL's header files. This will completely avoid the problem, but it will also make your code much harder to write, since you can no longer pass or receive a std::string.

In addition to Boris and Adam's answers, I've seen this cause a problem on Linux (I realize you are on Windows):

    StringSource(cipher, true,
    new HexEncoder(
            new StringSink(encoded)
        ) // HexEncoder
    ); // StringSource

Anonymous declarations are legal C/C++. But a particular version of GCC (circa 4.3 or 4.4 or 4.5) would begin running the destructors too soon in the generated code. I was talking to Jonathan Wakely about it back in 2011, but I was never able to figure out the cause. Here was the thread that caused m to start looking at it: Running the RSA samples and some installation issues.

The work around was simply to:

    StringSource ss(cipher, true,
    new HexEncoder(
            new StringSink(encoded)
        ) // HexEncoder
    ); // StringSource
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top