mirror of
https://github.com/torvalds/linux.git
synced 2026-10-26 20:46:02 +01:00
file: let dup_fd() drop only close-on-exec descriptors
Add FD_RANGE_CLOEXEC_ONLY. When set dup_fd() leaves everything behind except for fds that are close-on-exec. Without FD_RANGE_EXCEPT the clone loses the close-on-exec descriptors in the range. With it the clone keeps the range and loses the close-on-exec descriptors everywhere else. Descriptors without the flag are carried over either way. Nothing passes the flag yet. Link: https://patch.msgid.link/20260921-work-file-close_range_except-v1-8-c20d0b49270d@kernel.org Reviewed-by: Jann Horn <jannh@google.com> Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
This commit is contained in:
parent
4ee7588bbb
commit
4587f99cc2
43
fs/file.c
43
fs/file.c
|
|
@ -365,7 +365,8 @@ static unsigned long fd_range_word(struct fd_range *range, unsigned int i)
|
|||
}
|
||||
|
||||
/* Bits of word @i that dup_fd() leaves behind. */
|
||||
static unsigned long dup_fd_dropped_word(unsigned int i, struct fd_range *range)
|
||||
static unsigned long dup_fd_dropped_word(struct fdtable *fdt, unsigned int i,
|
||||
struct fd_range *range)
|
||||
{
|
||||
unsigned long dropped;
|
||||
|
||||
|
|
@ -374,6 +375,8 @@ static unsigned long dup_fd_dropped_word(unsigned int i, struct fd_range *range)
|
|||
dropped = fd_range_word(range, i);
|
||||
if (range->flags & FD_RANGE_EXCEPT)
|
||||
dropped = ~dropped;
|
||||
if (range->flags & FD_RANGE_CLOEXEC_ONLY)
|
||||
dropped &= fdt->close_on_exec[i];
|
||||
return dropped;
|
||||
}
|
||||
|
||||
|
|
@ -393,15 +396,37 @@ static unsigned int sane_fdtable_size(struct fdtable *fdt, struct fd_range *rang
|
|||
|
||||
if (last == fdt->max_fds)
|
||||
return NR_OPEN_DEFAULT;
|
||||
/* Only words up to the last open descriptor can hold a kept one. */
|
||||
i = last / BITS_PER_LONG + 1;
|
||||
while (i--) {
|
||||
unsigned long dropped = dup_fd_dropped_word(i, range);
|
||||
if (!range)
|
||||
return ALIGN(last + 1, BITS_PER_LONG);
|
||||
|
||||
if (fdt->open_fds[i] & ~dropped)
|
||||
return (i + 1) * BITS_PER_LONG;
|
||||
if (range->flags & FD_RANGE_CLOEXEC_ONLY) {
|
||||
/* The close-on-exec bits decide what is dropped, walk the words. */
|
||||
i = last / BITS_PER_LONG + 1;
|
||||
while (i--) {
|
||||
unsigned long dropped = dup_fd_dropped_word(fdt, i, range);
|
||||
|
||||
if (fdt->open_fds[i] & ~dropped)
|
||||
return (i + 1) * BITS_PER_LONG;
|
||||
}
|
||||
return NR_OPEN_DEFAULT;
|
||||
}
|
||||
return NR_OPEN_DEFAULT;
|
||||
|
||||
if (range->flags & FD_RANGE_EXCEPT) {
|
||||
/* Only the range is carried over. */
|
||||
if (last > range->to) {
|
||||
last = find_last_bit(fdt->open_fds, range->to + 1);
|
||||
if (last > range->to)
|
||||
return NR_OPEN_DEFAULT;
|
||||
}
|
||||
if (last < range->from)
|
||||
return NR_OPEN_DEFAULT;
|
||||
} else if (last >= range->from && last <= range->to) {
|
||||
/* The last open descriptor goes, the kept ones sit below the range. */
|
||||
last = find_last_bit(fdt->open_fds, range->from);
|
||||
if (last == range->from)
|
||||
return NR_OPEN_DEFAULT;
|
||||
}
|
||||
return ALIGN(last + 1, BITS_PER_LONG);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -487,7 +512,7 @@ struct files_struct *dup_fd(struct files_struct *oldf, struct fd_range *range)
|
|||
struct file *f = rcu_dereference_raw(*old_fds++);
|
||||
|
||||
if (!(fd % BITS_PER_LONG))
|
||||
dropped = dup_fd_dropped_word(fd / BITS_PER_LONG, range);
|
||||
dropped = dup_fd_dropped_word(old_fdt, fd / BITS_PER_LONG, range);
|
||||
if (f && !(dropped & BIT_MASK(fd))) {
|
||||
get_file(f);
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -104,6 +104,9 @@ int unshare_files(void);
|
|||
enum fd_range_flags {
|
||||
/* Leave behind all descriptors outside of the specified range. */
|
||||
FD_RANGE_EXCEPT = (1U << 0),
|
||||
|
||||
/* Only select descriptors that have close-on-exec set. */
|
||||
FD_RANGE_CLOEXEC_ONLY = (1U << 1),
|
||||
};
|
||||
|
||||
struct fd_range {
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user