Skip to content

Commit a1460e4

Browse files
committed
Merge tag 'pull-fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs
Pull fd bitmap fix from Al Viro: "Fix bitmap corruption on close_range() by cleaning up copy_fd_bitmaps()" * tag 'pull-fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs: fix bitmap corruption on close_range() with CLOSE_RANGE_UNSHARE
2 parents 7c626ce + 9a2fa14 commit a1460e4

File tree

3 files changed

+60
-17
lines changed

3 files changed

+60
-17
lines changed

fs/file.c

Lines changed: 13 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -46,27 +46,23 @@ static void free_fdtable_rcu(struct rcu_head *rcu)
4646
#define BITBIT_NR(nr) BITS_TO_LONGS(BITS_TO_LONGS(nr))
4747
#define BITBIT_SIZE(nr) (BITBIT_NR(nr) * sizeof(long))
4848

49+
#define fdt_words(fdt) ((fdt)->max_fds / BITS_PER_LONG) // words in ->open_fds
4950
/*
5051
* Copy 'count' fd bits from the old table to the new table and clear the extra
5152
* space if any. This does not copy the file pointers. Called with the files
5253
* spinlock held for write.
5354
*/
54-
static void copy_fd_bitmaps(struct fdtable *nfdt, struct fdtable *ofdt,
55-
unsigned int count)
55+
static inline void copy_fd_bitmaps(struct fdtable *nfdt, struct fdtable *ofdt,
56+
unsigned int copy_words)
5657
{
57-
unsigned int cpy, set;
58-
59-
cpy = count / BITS_PER_BYTE;
60-
set = (nfdt->max_fds - count) / BITS_PER_BYTE;
61-
memcpy(nfdt->open_fds, ofdt->open_fds, cpy);
62-
memset((char *)nfdt->open_fds + cpy, 0, set);
63-
memcpy(nfdt->close_on_exec, ofdt->close_on_exec, cpy);
64-
memset((char *)nfdt->close_on_exec + cpy, 0, set);
65-
66-
cpy = BITBIT_SIZE(count);
67-
set = BITBIT_SIZE(nfdt->max_fds) - cpy;
68-
memcpy(nfdt->full_fds_bits, ofdt->full_fds_bits, cpy);
69-
memset((char *)nfdt->full_fds_bits + cpy, 0, set);
58+
unsigned int nwords = fdt_words(nfdt);
59+
60+
bitmap_copy_and_extend(nfdt->open_fds, ofdt->open_fds,
61+
copy_words * BITS_PER_LONG, nwords * BITS_PER_LONG);
62+
bitmap_copy_and_extend(nfdt->close_on_exec, ofdt->close_on_exec,
63+
copy_words * BITS_PER_LONG, nwords * BITS_PER_LONG);
64+
bitmap_copy_and_extend(nfdt->full_fds_bits, ofdt->full_fds_bits,
65+
copy_words, nwords);
7066
}
7167

7268
/*
@@ -84,7 +80,7 @@ static void copy_fdtable(struct fdtable *nfdt, struct fdtable *ofdt)
8480
memcpy(nfdt->fd, ofdt->fd, cpy);
8581
memset((char *)nfdt->fd + cpy, 0, set);
8682

87-
copy_fd_bitmaps(nfdt, ofdt, ofdt->max_fds);
83+
copy_fd_bitmaps(nfdt, ofdt, fdt_words(ofdt));
8884
}
8985

9086
/*
@@ -379,7 +375,7 @@ struct files_struct *dup_fd(struct files_struct *oldf, unsigned int max_fds, int
379375
open_files = sane_fdtable_size(old_fdt, max_fds);
380376
}
381377

382-
copy_fd_bitmaps(new_fdt, old_fdt, open_files);
378+
copy_fd_bitmaps(new_fdt, old_fdt, open_files / BITS_PER_LONG);
383379

384380
old_fds = old_fdt->fd;
385381
new_fds = new_fdt->fd;

include/linux/bitmap.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -270,6 +270,18 @@ static inline void bitmap_copy_clear_tail(unsigned long *dst,
270270
dst[nbits / BITS_PER_LONG] &= BITMAP_LAST_WORD_MASK(nbits);
271271
}
272272

273+
static inline void bitmap_copy_and_extend(unsigned long *to,
274+
const unsigned long *from,
275+
unsigned int count, unsigned int size)
276+
{
277+
unsigned int copy = BITS_TO_LONGS(count);
278+
279+
memcpy(to, from, copy * sizeof(long));
280+
if (count % BITS_PER_LONG)
281+
to[copy - 1] &= BITMAP_LAST_WORD_MASK(count);
282+
memset(to + copy, 0, bitmap_size(size) - copy * sizeof(long));
283+
}
284+
273285
/*
274286
* On 32-bit systems bitmaps are represented as u32 arrays internally. On LE64
275287
* machines the order of hi and lo parts of numbers match the bitmap structure.

tools/testing/selftests/core/close_range_test.c

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -589,4 +589,39 @@ TEST(close_range_cloexec_unshare_syzbot)
589589
EXPECT_EQ(close(fd3), 0);
590590
}
591591

592+
TEST(close_range_bitmap_corruption)
593+
{
594+
pid_t pid;
595+
int status;
596+
struct __clone_args args = {
597+
.flags = CLONE_FILES,
598+
.exit_signal = SIGCHLD,
599+
};
600+
601+
/* get the first 128 descriptors open */
602+
for (int i = 2; i < 128; i++)
603+
EXPECT_GE(dup2(0, i), 0);
604+
605+
/* get descriptor table shared */
606+
pid = sys_clone3(&args, sizeof(args));
607+
ASSERT_GE(pid, 0);
608+
609+
if (pid == 0) {
610+
/* unshare and truncate descriptor table down to 64 */
611+
if (sys_close_range(64, ~0U, CLOSE_RANGE_UNSHARE))
612+
exit(EXIT_FAILURE);
613+
614+
ASSERT_EQ(fcntl(64, F_GETFD), -1);
615+
/* ... and verify that the range 64..127 is not
616+
stuck "fully used" according to secondary bitmap */
617+
EXPECT_EQ(dup(0), 64)
618+
exit(EXIT_FAILURE);
619+
exit(EXIT_SUCCESS);
620+
}
621+
622+
EXPECT_EQ(waitpid(pid, &status, 0), pid);
623+
EXPECT_EQ(true, WIFEXITED(status));
624+
EXPECT_EQ(0, WEXITSTATUS(status));
625+
}
626+
592627
TEST_HARNESS_MAIN

0 commit comments

Comments
 (0)