diff --git a/Cargo.toml b/Cargo.toml
index 0665795ed2..36dd6394a4 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -28,7 +28,7 @@ targets = [
 ]
 
 [dependencies]
-libc = { version = "0.2.160", features = ["extra_traits"] }
+libc = { version = "0.2.162", features = ["extra_traits"] }
 bitflags = "2.3.3"
 cfg-if = "1.0"
 pin-utils = { version = "0.1.0", optional = true }
diff --git a/changelog/2525.added.md b/changelog/2525.added.md
new file mode 100644
index 0000000000..89cd93f2e8
--- /dev/null
+++ b/changelog/2525.added.md
@@ -0,0 +1 @@
+Add `close_range` in unistd for Linux/glibc and FreeBSD
diff --git a/src/unistd.rs b/src/unistd.rs
index 4e35cb5b32..d2baa0ce2c 100644
--- a/src/unistd.rs
+++ b/src/unistd.rs
@@ -3992,3 +3992,61 @@ pub fn chflags<P: ?Sized + NixPath>(path: &P, flags: FileFlag) -> Result<()> {
     Errno::result(res).map(drop)
 }
 }
+
+#[cfg(any(
+    all(target_os = "linux", target_env = "gnu"),
+    target_os = "freebsd"
+))]
+#[cfg(feature = "fs")]
+libc_bitflags! {
+    /// Options for close_range()
+    #[cfg_attr(docsrs, doc(cfg(feature = "fs")))]
+    pub struct CloseRangeFlags : c_int {
+        #[cfg(all(target_os = "linux", target_env = "gnu"))]
+        /// Unshare the file descriptors table, then close the file descriptors specified in the
+        /// range
+        CLOSE_RANGE_UNSHARE as c_int;
+        /// Set the close-on-exec flag on the file descriptors range instead of closing them
+        CLOSE_RANGE_CLOEXEC as c_int;
+    }
+}
+
+feature! {
+#![feature = "fs"]
+
+/// Close all the file descriptor from a given range.
+/// An optional flag can be applied to modify its behavior.
+///
+/// # Safety
+///
+/// This function as there are risks of double closes on the file descriptors.
+#[cfg(any(
+    all(target_os = "linux", target_env = "gnu"),
+    target_os = "freebsd"
+))]
+pub unsafe fn close_range<F: std::os::fd::AsRawFd>(fdbegin: F, fdlast: F, flags: CloseRangeFlags) -> Result<Option<c_int>> {
+    let raw = unsafe {
+        Errno::clear();
+
+        cfg_if! {
+            if #[cfg(all(target_os = "linux", target_env = "gnu"))] {
+                libc::syscall(libc::SYS_close_range, fdbegin.as_raw_fd() as u32, fdlast.as_raw_fd() as u32, flags.bits())
+            } else {
+                libc::close_range(fdbegin.as_raw_fd() as u32, fdlast.as_raw_fd() as u32, flags.bits())
+            }
+        }
+    };
+    if raw == -1 {
+        if Errno::last_raw() == 0 {
+            Ok(None)
+        } else {
+            Err(Errno::last())
+        }
+    } else {
+        #[cfg(all(target_os = "linux", target_env = "gnu", target_pointer_width = "64"))]
+        let raw = raw as i32;
+        Ok(Some(raw))
+    }
+
+}
+}
diff --git a/test/test_unistd.rs b/test/test_unistd.rs
index 5cb019bd95..f3c961cfef 100644
--- a/test/test_unistd.rs
+++ b/test/test_unistd.rs
@@ -1391,3 +1391,36 @@ fn test_group_from() {
     assert_eq!(group.gid, group_id);
     assert_eq!(group.name, "wheel");
 }
+
+#[test]
+#[cfg(any(
+    all(target_os = "linux", target_env = "gnu"),
+    target_os = "freebsd"
+))]
+#[cfg_attr(qemu, ignore)]
+fn test_close_range() {
+    use tempfile::NamedTempFile;
+    const CONTENTS: &[u8] = b"abcdef123456";
+    let mut tempfile: [NamedTempFile; 3] = [
+        NamedTempFile::new().unwrap(),
+        NamedTempFile::new().unwrap(),
+        NamedTempFile::new().unwrap(),
+    ];
+
+    for tf in &mut tempfile {
+        let _ = tf.write_all(CONTENTS);
+    }
+    let areclosed = unsafe {
+        close_range(
+            tempfile[0].as_file().as_fd(),
+            tempfile[2].as_file().as_fd(),
+            CloseRangeFlags::CLOSE_RANGE_CLOEXEC,
+        )
+    };
+    assert_eq!(
+        areclosed
+            .expect("close_range failed")
+            .expect("invalid flag"),
+        0
+    );
+}