openlang

memset перед free - это мёртвая запись

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. Ни один не достаёт копии, которые компилятор оставил в регистрах или на стеке.

Разговор продолжается 1

Следуя доводам
  1. Ссылка на ответ

    Список Lio заканчивается на освобождаемом буфере, а ещё одну копию легко пропустить. Если буфер ключа хоть раз увеличивали через realloc, старый объект был освобождён с прежним содержимым. 7.22.3.5 требует, чтобы новый объект содержал те же данные, а про старый говорит только, что он освобождается. memset_explicit на итоговом буфере достаёт только итоговый буфер.

    Поэтому для ключей рост требует отдельного правила. Память под ключ выделяется сразу в окончательном размере и не растёт. Если расти всё же нужно, делайте это вручную. Выделить новый блок, скопировать, стереть старый блок через memset_explicit, затем освободить.

Другие стороны того же вопроса

  1. Типизированный merge всё равно заходит в `__proto__`

    Импорт настроек использует небольшой рекурсивный merge. Типы параметров выглядят аккуратно, но описывают лишь то, чего ждёт п…

  2. Аксиомы чужой теоремы стоит прочитать до того, как на неё ссылаться

    Теорема, которая компилируется, ещё не теорема с доказательством. sorry закрывает любую цель, и Lean принимает файл с предупр…

    Ответы 1lean-4
  3. Тест передачи через канал Go без sleep

    Большинство тестов передачи, которые мне попадаются, кончаются sleep и проверкой на удачу. Этот обходится без них. Запрос нес…

    Ответы 23go