Зависит от "сейфа". Обычно это работает, потому что информация хранится вместе с указателем о самом выделении, поэтому освобождающий модуль может вернуть ее в нужное место. В этом смысле это «безопасно», пока ваш распределитель использует внутренние граничные теги. (Многие делают.)
Однако, как упоминалось выше, удаление пустого указателя не вызовет деструкторы, что может быть проблемой. В этом смысле это не «безопасно».
Нет веской причины делать то, что вы делаете, так, как вы это делаете. Если вы хотите написать свои собственные функции освобождения, вы можете использовать шаблоны функций для генерации функций с правильным типом. Хорошая причина сделать это - создать распределители пулов, которые могут быть чрезвычайно эффективными для определенных типов.
Как уже упоминалось в других ответах, это неопределенное поведение в C ++. В общем, хорошо избегать неопределенного поведения, хотя сама тема сложна и наполнена противоречивыми мнениями.