Skip to content
/linux-syscalls

File & I/O · Section 2

close(2)

Release a file descriptor.

Signature

#include <unistd.h>

int close(int fd);
fd
File descriptor to close. After a successful return, fd is invalid in this process; do not use it again.

Description

close() releases the file descriptor fd, allowing it to be reused by the next open()/socket()/pipe()/etc. call. If fd is the last reference to its underlying open-file description, the kernel releases the file as well — flushing buffered writes, releasing record locks held by this process, removing memory mappings created by mmap on the descriptor, and (for sockets) starting the TCP shutdown sequence. close() returns 0 on success and -1 with errno set on failure. The traditional advice to retry close() on EINTR is wrong on Linux: once close() returns, the descriptor is gone regardless of the return value. The return value reports an asynchronous error (typically a deferred write-back failure) but does not give you a second chance to close.

Architecture mapping

ArchitectureNumberABIEntry point
x86 (i386)6i386sys_close
x64 (x86_64)3commonsys_close
ARM64 (aarch64)57sys_close

Kernel history

Introduced in Linux 1.0.

  1. 1.0

    close() has been part of Linux since 1.0 with POSIX.1 semantics, modulo the Linux-specific EINTR rule above.

  2. 5.9

    close_range(first, last, flags) was added to close a contiguous range of file descriptors in one syscall — useful in post-fork() child code that wants to close everything except a small allow-list, replacing the slow /proc/self/fd-scan pattern.

seccomp & containers

Docker default profile

Allowed

Podman default profile

Allowed

close() is on every default profile and is impossible to block usefully: every program closes descriptors. The interesting filtering is on close_range() and the CLOSE_RANGE_UNSHARE flag (Linux 5.11+), which can be used to selectively close descriptors after sharing the fd table — restricting it makes some sandboxing patterns awkward. For normal workloads, allow both.

libseccomp

seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(close), 0);

strace example

$ strace -e openat,close cat /etc/hostname > /dev/null
openat(AT_FDCWD, "/etc/hostname", O_RDONLY) = 3
close(3)                                = 0
close(1)                                = 0

close() output is one-line and uninteresting on its own; pair it with openat() in the -e set to see the full open/close lifecycle. A process leaking fds shows up as long runs of openat() without matching close() — easy to spot in strace -c summary mode.

Security & observability

close() itself is rarely a primary security event, but its absence is interesting: a process that opens sensitive files (/etc/shadow, /proc/<pid>/mem) and never closes them may be staging an exploit. fd accounting via /proc/<pid>/fd is the easier audit surface. The one real footgun is the EINTR retry pattern — pre-2008 code that loops on close() can close a file descriptor that the kernel has already reissued to another thread, leaking sensitive data to the wrong consumer. Always treat close() as a fire-and-forget on Linux.

Errors

EBADF
fd is not a valid open descriptor.
EINTR
On Linux, EINTR after close() means the descriptor has already been released — DO NOT retry the close with the same fd. (Other Unixes behave differently; Linux semantics are documented in close(2).)
EIO
An I/O error occurred — typically a deferred write-back failure surfaced at close time. The data was lost; the fd is still closed.
ENOSPC
On NFS and some other filesystems, close-time write-back can surface ENOSPC even though the original write() returned success.

Related syscalls