C++ Logo

std-proposals

Advanced search

Re: [std-proposals] interceptor functions (tested and working on x86_64)

From: Frederick Virchanza Gotham <cauldwell.thomas_at_[hidden]>
Date: Mon, 4 May 2026 07:24:56 +0000
I’m thinking of going a step further with this. Up until now, we can
intercept on the way in:


[[intercept]] void Func(void) noexcept
{
  DoLogging();

  goto -> std::fprintf;
}


But it would also be possible to intercept on the way out, meaning we could
lock and unlock a mutex. Ideally we would be able to do the following:


std::mutex m;

[[intercept]] void Func(void) noexcept
{
  std::lock_guard mylock(m);

  goto -> std::fprintf;
}


That would be quite complicated to implement though, as under the hood
you’d need to make it do something like:

std::mutex m;

[[intercept]] void Func(void) noexcept
{
  ::new( some_where ) std::lock_guard(m);

  goto -> std::fprintf;
  some_where->~lock_guard<std::mutex>();
}


Getting the target function to jump back to the interceptor is the easy
part – you only need to change the return address on the stack (or the link
register on aarch64). I’ve heard rumours of something called ‘Control-flow
Enforcement Technology’ which would prevent this on x86_64, but MS-Windows
can disable it with 'SetProcessMitigationPolicy', or 'prctl' on Linux. The
tricky part is where to put the ‘lock_guard’, as it cannot go on the stack
– remember the stack must remain intact for the jump. I suppose it could go
in a thread_local variable. Realistically I think the first ‘proof of
concept’ implementation would need to look something like the following:

std::mutex m;

[[intercept]] void Func(void) noexcept
{
  m.lock();

  goto -> std::fprintf
  <-- [](void *p){ m.unlock(); return p; };
}



So basically the above snippet would do five things:
(1) lock the mutex
(2) store the original return address somewhere (perhaps in a thread_local
variable)
(3) alter the return address on the stack to the entry point of the lambda
body
(4) jump to the target function

I think I’d be able to implement the above snippet in the GNU compiler
without too much hassle; basically it would work as follows:

    goto -> target_address <-- address_of_outward_interceptor

We can argue about the syntax another day, I’m only concerned about getting
it working for now. An alternative syntax could be:

   std::jump_to( target_address, address_of_outward_interceptor );

And if the second argument is ‘nullptr’ then there’s no outward
interception.

I would find this extremely useful for debugging. Let's say we suspect that
a library has a race condition or data race that is causing it to
malfunction -- well we can just wrap every function call in a
mutex-locking-and-unlocking to see if that fixes it.

Received on 2026-05-04 07:25:00