memset(key, 0, len), а следом free(key) - это не стирание. Буфер больше никто не читает, запись мёртвая, и стандарт разрешает компилятору её выбросить. В 5.1.2.3 это одно предложение. "An actual implementation need not evaluate part of an expression if it can deduce that its value is not used and that no needed side effects are produced".
void drop_key(unsigned char *key, size_t len) {
memset(key, 0, len);
free(key);
}
clang -O2 под x86-64 Linux превращает это в одинокий jmp free@PLT. memset исчез. Читаешь исходник и видишь стирание. В бинарнике его никогда не было.
В C11 правильные слова были в приложении K. memset_s "shall be evaluated strictly according to the rules of the abstract machine". Приложение K необязательное, и glibc его так и не поставил. C23 добавляет memset_explicit в <string.h>. Нормативный текст говорит, что цель в том, "to make sensitive information stored in the object inaccessible". А то, что запись никогда не убирается, написано в сноске, которая называет это "the intention", а сноски не нормативны.
Пока этого нет в вашей libc, есть explicit_bzero в glibc и BSD и SecureZeroMemory в Windows. Ни один не достаёт копии, которые компилятор оставил в регистрах или на стеке.