openlang

memset before free is a dead store

memset(key, 0, len) followed by free(key) is not a wipe. Nothing reads the buffer again, so the store is dead, and the standard lets the compiler drop it. 5.1.2.3 says so in one sentence. "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 for x86-64 Linux turns that into a lone jmp free@PLT. The memset is gone. Read the source and you see a wipe. The binary never had one.

C11 had the right words in Annex K. memset_s "shall be evaluated strictly according to the rules of the abstract machine". Annex K is optional and glibc never shipped it. C23 puts memset_explicit in <string.h>. Its normative text says the purpose is "to make sensitive information stored in the object inaccessible". The part about the store never being elided lives in a footnote that calls it "the intention", and footnotes aren't normative.

Until your libc has it, explicit_bzero on glibc and the BSDs, SecureZeroMemory on Windows. None of them reach copies the compiler left in registers or on the stack.

后续讨论 1

顺着彼此的依据读下去
  1. 固定链接

    Lio's list stops at the buffer being freed, and one more copy is easy to miss. If the key buffer was ever grown with realloc, the old object was deallocated with its contents as they were. 7.22.3.5 requires the new object to hold the same contents and says nothing about the old one beyond deallocating it. memset_explicit on the final buffer reaches the final buffer only.

    So growth needs its own rule for keys. Allocate key storage at its final size and never grow it. If it has to grow, do it by hand. Allocate the new block, copy, wipe the old block with memset_explicit, then free it.

同一个问题的其他角度

  1. Solidity reverts on overflow, Rust release builds wrap

    Since Solidity 0.8, arithmetic reverts on overflow and underflow. Turning that off takes an explicit unchecked { ... } block.…

    回复 4最近活动 solidityrust
  2. Testing a Go channel handoff without sleep

    Most handoff tests I run into end with a sleep and a hopeful assertion. This one can do without. A request carries an identif…

    回复 23最近活动 go
  3. partial def is the honest answer for Collatz

    Lean has to know a recursive def terminates before it accepts it as a definition. Structural recursion is found automatically…

    回复 10最近活动 lean-4