Self encryptor callback cast issue


#1

I snipped this out of the other topic, since this is quite likely another problem.

Here goes:

There seems to be something strange with the self encryptor callbacks and so on…

I’m referencing the C# method names, but they are more or less the same as the underlying api method names.
It appeared like IDataSelfEncryptorWriterFree was not needed when calling IDataCloseSelfEncryptor

The pattern for reader however is different. There is no example of using CloseSelfEncryptorAsync for the reader, and so there we actually need to call IDataSelfEncryptorReaderFree, otherwise there would be a memory leak, and eventually the machine would run out of memory.

But, it seems like this sequence of calls:

async Task<EventData> GetEventDataFromAddress(StoredEvent stored)
{
        var seReaderHandle = await IData.FetchSelfEncryptorAsync(stored.DataMapAddress);
        var len = await IData.SizeAsync(seReaderHandle);
        var readData = await IData.ReadFromSelfEncryptorAsync(seReaderHandle, 0, len);

        var eventData = new EventData(readData.ToArray(),
            stored.MetaData.CorrelationId,
            stored.MetaData.CausationId,
            stored.MetaData.EventClrType,
            stored.MetaData.Id,
            stored.MetaData.Name,
            stored.MetaData.SequenceNumber,
            stored.MetaData.TimeStamp);

        await IData.SelfEncryptorReaderFreeAsync(seReaderHandle);

        return eventData;
    }

leads to some strange things:

invalidcastexception
In the image above, an IDataSizeCb is clearly returned from native code, when expecting an IDataSelfEncryptorReaderFreeCb. Then on another occasion, it is a IDataFetchSelfEncryptorCb returned from native code.
Same method call, same parameter types, but returning pointers to different kind of callbacks?
I have no idea how this happens, but there is without doubt something wrong.

So, the code that is called is identical to SafeMessages example:

 public void IDataSelfEncryptorReaderFree(IntPtr appPtr, ulong sEReaderHandle, 
   IDataSelfEncryptorReaderFreeCb callback)
   {
        IDataSelfEncryptorReaderFreeNative(appPtr, sEReaderHandle, callback.ToHandlePtr(), 
  OnIDataSelfEncryptorReaderFreeCb);
   }

   [DllImport("safe_app.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "idata_self_encryptor_reader_free")]
    public static extern void IDataSelfEncryptorReaderFreeNative(
      IntPtr appPtr,
      ulong sEReaderHandle,
      IntPtr self,
      IDataSelfEncryptorReaderFreeCb callback);

    private static void OnIDataSelfEncryptorReaderFreeCb(IntPtr self, FfiResult result)
    {
        var cb = self.HandlePtrToType<IDataSelfEncryptorReaderFreeCb>();
        cb(IntPtr.Zero, result);
    }

So you can see that when calling the idata_self_encryptor_reader_free we are expecting one kind of callback, but getting a few various kinds of callbacks returned from native code. This is quite weird.

Something seems to be messing this up, making things go haywire. Is there something wrong with how I call the read and write of immutable data, that is messing up the native state?

For reference, here is how I write immutable data:

async Task<List<byte>> StoreImmutableData(byte[] payload)
        {
            var cipherOptHandle = await CipherOpt.NewPlaintextAsync();
            var seWriterHandle = await IData.NewSelfEncryptorAsync();
            await IData.WriteToSelfEncryptorAsync(seWriterHandle, payload.ToList());
            var dataMapAddress = await IData.CloseSelfEncryptorAsync(seWriterHandle, cipherOptHandle);
            //await IData.SelfEncryptorWriterFreeAsync(seWriterHandle); <= commented out since I couldn't free after having closed on the line before..
            return dataMapAddress;
        }

I feel like I’m missing information about how this should be done. Because I am doing something wrong here, which corrupts memory somehow, making a mess with returned pointers, or no?

Should we close a writer, but not free it? Or free it but not close, or both? (well, both did not work, I tried that)

And how about reader?


Need help understanding an error thrown from safe_app.dll
#2

I’m having a guess at some missed freeing of a handle somewhere.
Does it seem like the symptoms we see here are a possible result of such mistakes?

Regarding the writing, I am guessing now that we

  1. write and receive address
  2. then free

or

  1. write only
  2. then close and receive address

Because when looking closer at the write method documentation, I see it actually returns address, but the code I used from SafeMessages example, returns null.

Anyway, added a couple of using clauses:

    async Task<List<byte>> StoreImmutableData(byte[] payload)
    {
        using (var cipherOptHandle = await CipherOpt.NewPlaintextAsync())
        {
            using (var seWriterHandle = await IData.NewSelfEncryptorAsync())
            {
                await IData.WriteToSelfEncryptorAsync(seWriterHandle, payload.ToList());
                var dataMapAddress = await IData.CloseSelfEncryptorAsync(seWriterHandle, cipherOptHandle);
                return dataMapAddress;
            }
        }
    }

and

 async Task<EventData> GetEventDataFromAddress(StoredEvent stored)
    {
        using (var seReaderHandle = await IData.FetchSelfEncryptorAsync(stored.DataMapAddress))
        {
            var len = await IData.SizeAsync(seReaderHandle);
            var readData = await IData.ReadFromSelfEncryptorAsync(seReaderHandle, 0, len);
          
            // ... snipped out for brevity
        }
    }

(this is because class NativeHandle was implemented for alpha-2, where it implements IDisposable, and it receives in ctor the freeing method, sowe do not need to call it explicitly)

and what we get then is that

On a newly created db (i.e. new MD to store info on stream MDs) there is a NullReferenceException with undefined callstack (seems to be from native code), after about 22x2 writes (number of writes is consistent with previous results, but the error is different after introducing these missing using clauses - i.e. no longer the invalid cast of OP).
After about 2 iterations, we get the “untraceable” crash mentioned in other topic, which seems to stem from safe_app.dll.

So, I guess problem of OP is kind of solved, and replaced with this NullreferenceException on the exact same number of writes.