On Mon, 14 Apr 2025 at 10:11, Julian Waters via Gcc <gcc@gcc.gnu.org> wrote: > > Hi all, > > A codebase I'm working with has decided that poisoning certain > standard library functions is necessary, as it explicitly does not use > those functions unless absolutely necessary for its own reasons (This > was not my decision to make). As such, we've been looking into ways to > implement that. > > The problem is there's really no way at all to forbid standard library > functions. The crucial thing is that this mechanism must have a > disable switch for when a callsite must actually call a forbidden > function, complicating the whole process. > > It would be nice to have compiler support to help with this. Something like: > > #pragma GCC forbidden void *malloc(size_t) // Forbids malloc from > being called anywhere
Have you looked at https://gcc.gnu.org/onlinedocs/cpp/Pragmas.html#index-_0023pragma-GCC-poison ? > #pragma GCC permitted push > #pragma GCC permitted void *malloc(size_t) > void *ptr = malloc(1); // This callsite must use malloc, bypassing the > usual restriction > #pragma GCC permitted pop > > However, I'm guessing this would be a bit complex to implement. There > are other ways I have tried as well, such as: > > #include <cstdlib> > > namespace permitted { > using ::malloc; > } > > inline namespace forbidden { > [[deprecated("use os::malloc")]] void *malloc(size_t) throw() = delete; > } > > int main() { > void *ptr = malloc(1); // Should raise an error, must call > permitted::malloc(1) to avoid > free(ptr); > } > > The problem with the above is that the error isn't from the function > being deleted or marked as deprecated, it's an ambiguity error that is > confusing and doesn't tell the developer why the error is actually > happening (Because the function has been forbidden). Additionally, > there is no way to disable this system in third party headers included > from the testing framework, making it unusable. Given this, could I > request some sort of compiler specific "shadow" attribute that can be > applied to a namespace? With this attribute, the symbols in this > namespace will take precedence over the symbols of the namespace it is > in (Including the global namespace, if its parent is in the global > namespace) if it is injected into the parent, either because it is > inline, unnamed, or because of a using namespace x; declaration. This > contrasts with the standard behaviour of raising an ambiguity error > when trying to resolve which symbol to use, instead, the symbol from > the namespace marked as shadow will always be given precedence. > > #include <cstdlib> > > namespace permitted { > using ::malloc; > } > > inline namespace [[gnu::shadow]] forbidden { > [[deprecated("use os::malloc")]] void *malloc(size_t) throw() = delete; > } > > int main() { > void *ptr = malloc(1); // Correct error shown, forbidden::malloc > chosen over standard library > free(ptr); > } > > Ideally shadow could have another effect on using declarations. > Normally, trying to do a using declaration to choose between 2 > conflicting symbols will just fail: > > #include <cstdlib> > > namespace permitted { > using ::malloc; > } > > inline namespace forbidden { > [[deprecated("use os::malloc")]] void *malloc(size_t) throw() = delete; > } > > using forbidden::malloc; > > int main() { > void *ptr = malloc(1); > free(ptr); > } > > <source>:11:18: error: 'void* forbidden::malloc(size_t)' conflicts > with a previous declaration > 11 | using forbidden::malloc; > | ^~~~~~ > > Perhaps, if one of those symbols comes from a namespace marked as > shadow, the using declaration would instead behave differently, > choosing which declaration is used instead of just bringing the > declaration into scope: > > #include <cstdlib> > > namespace permitted { > using ::malloc; > } > > inline namespace [[gnu::shadow]] forbidden { > [[deprecated("use os::malloc")]] void *malloc(size_t) throw() = delete; > } > > using forbidden::malloc; // From now on, any calls to malloc use > forbidden::malloc > using ::malloc; // From now on, any call to malloc go to ::malloc > using permitted::malloc; // From now on, any call to malloc goes to > permitted::malloc > > Or alternatively, is there a better way to do this instead of adding > custom support to the compiler? The gcc static analyzer doesn't seem > to support C++ yet nor does it seem to have a configurable check for > whether you are calling a given function, neither does VC for that > matter. Maybe this could be an analyzer request instead of a compiler > feature request. Either way, I'm open to suggestions. > > best regards, > Julian