close_range(2)
Close, or mark close-on-exec, every file descriptor in a range in one call.
- Since
- Linux 5.9check your kernel →
- glibc
- glibc 2.34
- System
- Linux
- Man page
- close_range(2)
Synopsis
#define _GNU_SOURCE
#include <unistd.h>
#include <linux/close_range.h>
int close_range(unsigned int first, unsigned int last, int flags);Rod's take
The pre-exec loop that calls close() on every number up to _SC_OPEN_MAX is a million syscalls returning EBADF, and it's sitting in code you depend on right now. close_range(3, ~0U, 0) does it in one. CLOSE_RANGE_UNSHARE does the work on a private copy of the fd table, so sibling threads don't get their descriptors yanked mid-use. CLOSE_RANGE_CLOEXEC is the clever one: leave everything open here, guarantee none of it leaks across exec. The BSDs had closefrom for years. Linux showed up late, but it brought flags.
Flags and arguments that matter
| Name | What it does |
|---|---|
| CLOSE_RANGE_UNSHARE | Unshare the fd table first, so closing doesn't race other threads sharing it. |
| CLOSE_RANGE_CLOEXECLinux 5.11 | Set close-on-exec on the range instead of closing the descriptors. |
| last = ~0U | Pass UINT_MAX as last to cover everything from first upward. |
Errors you will actually hit
| errno | Meaning |
|---|---|
| EINVAL | flags is not valid, or first is greater than last. |
| EMFILE | With CLOSE_RANGE_UNSHARE: open fds exceed /proc/sys/fs/nr_open when building the new table. |
| ENOMEM | With CLOSE_RANGE_UNSHARE: insufficient kernel memory for the new table. |
Example
/* in the child, right before exec: nothing above stderr survives */
if (close_range(3, ~0U, CLOSE_RANGE_UNSHARE) < 0)
err(1, "close_range");
execvp(argv[0], argv);
/* or: keep fds open here, but stop them leaking into children */
close_range(3, ~0U, CLOSE_RANGE_CLOEXEC);
dup2(pipe_wr, 3); /* dup2 clears FD_CLOEXEC on fd 3 */Trimmed from the full walkthrough in close_range: one syscall instead of a million EBADFs.
BSD equivalent
FreeBSD: close_range(2) with CLOSE_RANGE_CLOEXEC (FreeBSD 12.2) and closefrom(2) (FreeBSD 8.0). OpenBSD: closefrom(2) (OpenBSD 3.5).
The long version, from the console
Sources
Version numbers are read off the cited man pages, not memory. Spot one that's wrong? Tell Rod.
