#include <linux/bitops.h>
 #include <linux/spinlock.h>
 #include <linux/rcupdate.h>
+#include <linux/close_range.h>
 
 unsigned int sysctl_nr_open __read_mostly = 1024*1024;
 unsigned int sysctl_nr_open_min = BITS_PER_LONG;
        return i;
 }
 
+static unsigned int sane_fdtable_size(struct fdtable *fdt, unsigned int max_fds)
+{
+       unsigned int count;
+
+       count = count_open_files(fdt);
+       if (max_fds < NR_OPEN_DEFAULT)
+               max_fds = NR_OPEN_DEFAULT;
+       return min(count, max_fds);
+}
+
 /*
  * Allocate a new files structure and copy contents from the
  * passed in files structure.
  * errorp will be valid only when the returned files_struct is NULL.
  */
-struct files_struct *dup_fd(struct files_struct *oldf, int *errorp)
+struct files_struct *dup_fd(struct files_struct *oldf, unsigned int max_fds, int *errorp)
 {
        struct files_struct *newf;
        struct file **old_fds, **new_fds;
 
        spin_lock(&oldf->file_lock);
        old_fdt = files_fdtable(oldf);
-       open_files = count_open_files(old_fdt);
+       open_files = sane_fdtable_size(old_fdt, max_fds);
 
        /*
         * Check whether we need to allocate a larger fd array and fd set.
                 */
                spin_lock(&oldf->file_lock);
                old_fdt = files_fdtable(oldf);
-               open_files = count_open_files(old_fdt);
+               open_files = sane_fdtable_size(old_fdt, max_fds);
        }
 
        copy_fd_bitmaps(new_fdt, old_fdt, open_files);
  * This closes a range of file descriptors. All file descriptors
  * from @fd up to and including @max_fd are closed.
  */
-int __close_range(struct files_struct *files, unsigned fd, unsigned max_fd)
+int __close_range(unsigned fd, unsigned max_fd, unsigned int flags)
 {
        unsigned int cur_max;
+       struct task_struct *me = current;
+       struct files_struct *cur_fds = me->files, *fds = NULL;
+
+       if (flags & ~CLOSE_RANGE_UNSHARE)
+               return -EINVAL;
 
        if (fd > max_fd)
                return -EINVAL;
 
        rcu_read_lock();
-       cur_max = files_fdtable(files)->max_fds;
+       cur_max = files_fdtable(cur_fds)->max_fds;
        rcu_read_unlock();
 
        /* cap to last valid index into fdtable */
        cur_max--;
 
+       if (flags & CLOSE_RANGE_UNSHARE) {
+               int ret;
+               unsigned int max_unshare_fds = NR_OPEN_MAX;
+
+               /*
+                * If the requested range is greater than the current maximum,
+                * we're closing everything so only copy all file descriptors
+                * beneath the lowest file descriptor.
+                */
+               if (max_fd >= cur_max)
+                       max_unshare_fds = fd;
+
+               ret = unshare_fd(CLONE_FILES, max_unshare_fds, &fds);
+               if (ret)
+                       return ret;
+
+               /*
+                * We used to share our file descriptor table, and have now
+                * created a private one, make sure we're using it below.
+                */
+               if (fds)
+                       swap(cur_fds, fds);
+       }
+
        max_fd = min(max_fd, cur_max);
        while (fd <= max_fd) {
                struct file *file;
 
-               file = pick_file(files, fd++);
+               file = pick_file(cur_fds, fd++);
                if (!file)
                        continue;
 
-               filp_close(file, files);
+               filp_close(file, cur_fds);
                cond_resched();
        }
 
+       if (fds) {
+               /*
+                * We're done closing the files we were supposed to. Time to install
+                * the new file descriptor table and drop the old one.
+                */
+               task_lock(me);
+               me->files = cur_fds;
+               task_unlock(me);
+               put_files_struct(fds);
+       }
+
        return 0;
 }
 
 
  * as this is the granularity returned by copy_fdset().
  */
 #define NR_OPEN_DEFAULT BITS_PER_LONG
+#define NR_OPEN_MAX ~0U
 
 struct fdtable {
        unsigned int max_fds;
 void put_files_struct(struct files_struct *fs);
 void reset_files_struct(struct files_struct *);
 int unshare_files(struct files_struct **);
-struct files_struct *dup_fd(struct files_struct *, int *) __latent_entropy;
+struct files_struct *dup_fd(struct files_struct *, unsigned, int *) __latent_entropy;
 void do_close_on_exec(struct files_struct *);
 int iterate_fd(struct files_struct *, unsigned,
                int (*)(const void *, struct file *, unsigned),
                      unsigned int fd, struct file *file);
 extern int __close_fd(struct files_struct *files,
                      unsigned int fd);
-extern int __close_range(struct files_struct *files, unsigned int fd,
-                        unsigned int max_fd);
+extern int __close_range(unsigned int fd, unsigned int max_fd, unsigned int flags);
 extern int __close_fd_get_file(unsigned int fd, struct file **res);
+extern int unshare_fd(unsigned long unshare_flags, unsigned int max_fds,
+                     struct files_struct **new_fdp);
 
 extern struct kmem_cache *files_cachep;
 
 
                goto out;
        }
 
-       newf = dup_fd(oldf, &error);
+       newf = dup_fd(oldf, NR_OPEN_MAX, &error);
        if (!newf)
                goto out;
 
 /*
  * Unshare file descriptor table if it is being shared
  */
-static int unshare_fd(unsigned long unshare_flags, struct files_struct **new_fdp)
+int unshare_fd(unsigned long unshare_flags, unsigned int max_fds,
+              struct files_struct **new_fdp)
 {
        struct files_struct *fd = current->files;
        int error = 0;
 
        if ((unshare_flags & CLONE_FILES) &&
            (fd && atomic_read(&fd->count) > 1)) {
-               *new_fdp = dup_fd(fd, &error);
+               *new_fdp = dup_fd(fd, max_fds, &error);
                if (!*new_fdp)
                        return error;
        }
        err = unshare_fs(unshare_flags, &new_fs);
        if (err)
                goto bad_unshare_out;
-       err = unshare_fd(unshare_flags, &new_fd);
+       err = unshare_fd(unshare_flags, NR_OPEN_MAX, &new_fd);
        if (err)
                goto bad_unshare_cleanup_fs;
        err = unshare_userns(unshare_flags, &new_cred);
        struct files_struct *copy = NULL;
        int error;
 
-       error = unshare_fd(CLONE_FILES, ©);
+       error = unshare_fd(CLONE_FILES, NR_OPEN_MAX, ©);
        if (error || !copy) {
                *displaced = NULL;
                return error;