Thread: Implementing type safety in a C-style callback scenario

Threaded View

Previous Post Previous Post   Next Post Next Post
  1. #1
    Registered User
    Join Date
    Jul 2015
    Posts
    64

    Implementing type safety in a C-style callback scenario

    Hey all,

    I'm currently writing a function that I intend to compile into a personal-use library, so, naturally, I'd like to get things right first time. At the moment, I'm using default (or not) arguments passed to the function to selectively reinterpret_cast a function pointer to a callback routine. I've knocked together some demo code to show you what I mean in case it's unclear:

    Code:
    #include <iostream>
    #include <string>
    #include <Windows.h>
    
    void Caller(void* gen_fpCallback = 0, void* lpCallbackData = 0)
    {
        std::cout << "Caller function executing.\n\n";
        
        // Do stuff here
    
        if (gen_fpCallback)
        {
            if (lpCallbackData)
            {
                auto fpCallback = reinterpret_cast<void(*)(void*)>(gen_fpCallback);
                fpCallback(lpCallbackData);
            }
    
            else
            {
                auto fpCallback = reinterpret_cast<void(*)()>(gen_fpCallback);
                fpCallback();
            }    
        }
    }
    
    void A()
    {
        std::cout << "Function A is running.\nNo parameters specified.\n\n";
    }
    
    struct Data
    {
        std::string arbitraryparam_string1;
        std::string arbitraryparam_string2;
    };
    
    void B(void* param)
    {
        std::cout << ((Data*) param)->arbitraryparam_string1 << ((Data*) param)->arbitraryparam_string2;
        MessageBox(NULL, L"MessageBox to confirm that function B actually executes on final call", L"Note", MB_OK);
    }
    
    int main()
    {
        // Launch caller with no callback
        Caller();
    
        // Launch caller with a callback function that takes no parameters
        void(*ptr_to_A)() = &A;
        Caller(ptr_to_A);
    
        // Launch caller with a callback that accepts a void* parameter
        Data data = { "Function B is running.\n", "These strings were passed via a pointer to a structure.\n\n" };
        void* ptr_to_data = &data;
    
        void(*ptr_to_B)(void*) = &B;
        Caller(ptr_to_B, ptr_to_data);
        
        Caller(ptr_to_B);        // What is happening here?
    
        std::cin.get();
        return 0;
    }
    As it stands, I'm very wary of the reinterpret_cast; I've never come across a situation where I've had to use it before. The first three calls to Caller() work as expected. However, the final call casts the generic function pointer to a void (*)() and, through it, executes function B, even though B is prototyped to accept a void pointer parameter.

    What is happening during this call? Is the compiler implicitly (and nicely) passing '0' as the second parameter or does, perhaps, whatever happened to be in that memory location at the time act as the second parameter passed to B? I realise the latter may be dangerous. As clever as compilers are these days (and please correct me if I'm wrong here), I don't believe that function B is being implicitly overloaded by calling it through a mismatched function pointer. I also note that this behaviour may be undefined by the standard; I am using MSVC if it matters.

    My question: is there a more type safe way of achieving this? Whilst I could leave it up to the calling application to ensure that a valid function pointer is passed to the calling function, there is no reason to leave this "type unsafe" if it can be rectified simply and efficiently. In order to avoid a large library footprint (and I realise MSVC isn't necessarily the way to go for this), I'd like to avoid overloading the Caller function twice over. Is it perhaps possible to perform a runtime check on the callback function in order to enforce correct function pointer casting?

    Also, how does this technique look in terms of security? I'm aware that a malicious attacker could take over execution if they are able to overwrite the function pointer itself. If I am careful to avoid this scenario, is this a relatively 'safe' technique to work with?

    Many thanks for your time; I much appreciate it!
    Abyssion

    P.S. I see that these boards now support TLS; on behalf of the security-conscious users here, "Thank you very much!"
    P.P.S. Many apologies if the example code doesn't compile; my IDE is currently out of action.
    Last edited by Abyssion; 03-12-2017 at 04:33 PM.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Macro type safety
    By Aslaville in forum C Programming
    Replies: 7
    Last Post: 06-27-2016, 01:57 PM
  2. How can I enforce type safety?
    By Absurd in forum C Programming
    Replies: 4
    Last Post: 03-26-2016, 11:44 AM
  3. Callback style
    By nvoigt in forum C++ Programming
    Replies: 7
    Last Post: 08-19-2011, 08:46 PM
  4. C/C++ Type Safety
    By forumuser in forum C Programming
    Replies: 8
    Last Post: 09-23-2009, 12:27 AM
  5. Callback type things
    By Rune Hunter in forum C++ Programming
    Replies: 3
    Last Post: 02-04-2006, 08:39 PM

Tags for this Thread