You would basically have to issue a check for every memory read through a pointer. Some moving GCs are based on this design, so it's feasable and can be made efficient enough. You can also argue that you've effectively described Valgrind, so yes, "simply always run C/C++ via Valgrind" (or a comparable sanitizer) would work.
However, it's much more convenient to use a language where safety wasn't an afterthought (such as Nim, Rust, Swift, ...) and the performance of these is better than "C plus a sanitizer" too...