CWE-910, Use of Expired File Descriptor, is what happens when code uses a file descriptor after it has been closed. It matters mostly in C and C++, but the idea applies to any code that holds a raw handle to a file, socket or device.
How it happens
When a descriptor is released, the operating system is free to hand the same number out again. If your code still holds the old number, it now points at whatever got opened next. MITRE puts it plainly: the code might not write to the original file, because the reused descriptor might reference a different file or device.
A typical shape is a cleanup path that closes a descriptor on error, then a later code path that uses the same variable.
int fd = open(path, O_RDONLY);
if (fd < 0) return -1;
if (parse_header(fd) != 0) {
close(fd); /* closed here */
}
read(fd, buf, len); /* used after close: expired descriptor */
What an attacker gains
MITRE lists two consequences. Confidentiality: the program could read data from the wrong file. Availability: using a closed descriptor can cause a crash. Whether an attacker can steer which file gets the recycled number depends on the program, so treat it as a real exposure when the process handles untrusted input.
How to find it
MITRE notes that static analysis, or SAST, can find some instances by modeling data flow and control flow through the code, and rates its effectiveness as high. A code review that follows each descriptor from open to every close is the manual version.
How to fix it
The MITRE record lists no formal mitigations. Practical steps:
- Set the variable to an invalid value, such as negative one, immediately after closing it, and check before use.
- Close each descriptor in exactly one place, ideally with a single cleanup label.
- In C++, wrap descriptors in an object that closes them on destruction so the code cannot reuse a raw number.
Quick check
Why is a closed descriptor more than a crash risk?
Because the number can be reused. A later read or write may land on a different file or device than the one you meant.