C++ Logo

std-proposals

Advanced search

[std-proposals] Intercept inwards and outwards (tested & working on x86_64)

From: Frederick Virchanza Gotham <cauldwell.thomas_at_[hidden]>
Date: Sat, 9 May 2026 22:56:46 +0000
Interceptor functions can now intercept on the way in, and also on the way
out. I have it working for x86_64. More architectures to come later.

This means that you can lock a mutex before jumping to the target function,
and then you can unlock the mutex before returning control back to the
caller. If you only want to intercept on the way in, then things like
Controlflow Enforcement Technology (CET) won't kill the process because no
return addresses are edited on the stack. If you want to intercept on the
way out though, you'll need to disable the likes of CET, which is easy to
do on a development PC running MS-Windows, macOS and Linux.

I think I pretty much have this at a level now where I can torture test it
on x86_64, perhaps by taking a massive program (such as a 3D graphics
design program), and creating ‘Man In The Middle’ DLL files to stand
between the executable and all of its DLL dependencies.

Here’s a GodBolt to demonstrate it working:

    https://godbolt.org/z/s389reh1c

I’m currently fantasizing about figuring out a way to catch an exception
thrown from the target function, and to make sure the ‘outward’ interceptor
gets executed before re-throwing the exception.

Here's how the compiler patch is looking for the time being:

https://github.com/healytpk/gcc-thomas-healy/commit/tag_interceptor

And here’s the GodBolt copy-pasted:

#include <cstdio>

#include <iostream>

#include <interceptor>

#include <mutex>



using std::cout, std::endl;



struct Mutex {

  std::mutex m;

  void lock() { cout << " --- locking --- \n"; m. lock(); }

  void unlock() { cout << " --- unlocking --- \n"; m.unlock();}

};



/* The following interceptor function has two purposes:

* (1) Toggle the output between stdout to stderr

* (2) Lock and unlock a mutex before invoking std::fprintf */

[[interceptor]] void MyInterceptor(void) noexcept

{

  static Mutex m;



  /**/ if ( __inter.arg0p() == stdout ) __inter.arg0p() = stderr;

  else if ( __inter.arg0p() == stderr ) __inter.arg0p() = stdout;



  m.lock();



  __inter.outward() = [](){ m.unlock(); };



  goto -> std::fprintf;

}



int main(void)

{

  cout << "First line in main\n";



  // We make a function pointer to point to the interceptor

  auto const fp = (decltype(std::fprintf)*)&MyInterceptor;



  // We invoke the interceptor exactly as though we were invoking 'fprintf'

  fp(stderr,"Hello %s.\n", "world");



  cout << "Last line in main\n";

}

Received on 2026-05-09 22:56:52