.. | .. |
---|
677 | 677 | fdt = files_fdtable(files); |
---|
678 | 678 | if (fd >= fdt->max_fds) |
---|
679 | 679 | goto out_unlock; |
---|
| 680 | + fd = array_index_nospec(fd, fdt->max_fds); |
---|
680 | 681 | file = fdt->fd[fd]; |
---|
681 | 682 | if (!file) |
---|
682 | 683 | goto out_unlock; |
---|
.. | .. |
---|
1006 | 1007 | return __fget_light(fd, 0); |
---|
1007 | 1008 | } |
---|
1008 | 1009 | |
---|
| 1010 | +/* |
---|
| 1011 | + * Try to avoid f_pos locking. We only need it if the |
---|
| 1012 | + * file is marked for FMODE_ATOMIC_POS, and it can be |
---|
| 1013 | + * accessed multiple ways. |
---|
| 1014 | + * |
---|
| 1015 | + * Always do it for directories, because pidfd_getfd() |
---|
| 1016 | + * can make a file accessible even if it otherwise would |
---|
| 1017 | + * not be, and for directories this is a correctness |
---|
| 1018 | + * issue, not a "POSIX requirement". |
---|
| 1019 | + */ |
---|
| 1020 | +static inline bool file_needs_f_pos_lock(struct file *file) |
---|
| 1021 | +{ |
---|
| 1022 | + return (file->f_mode & FMODE_ATOMIC_POS) && |
---|
| 1023 | + (file_count(file) > 1 || S_ISDIR(file_inode(file)->i_mode)); |
---|
| 1024 | +} |
---|
| 1025 | + |
---|
1009 | 1026 | unsigned long __fdget_pos(unsigned int fd) |
---|
1010 | 1027 | { |
---|
1011 | 1028 | unsigned long v = __fdget(fd); |
---|
1012 | 1029 | struct file *file = (struct file *)(v & ~3); |
---|
1013 | 1030 | |
---|
1014 | | - if (file && (file->f_mode & FMODE_ATOMIC_POS)) { |
---|
1015 | | - if (file_count(file) > 1) { |
---|
1016 | | - v |= FDPUT_POS_UNLOCK; |
---|
1017 | | - mutex_lock(&file->f_pos_lock); |
---|
1018 | | - } |
---|
| 1031 | + if (file && file_needs_f_pos_lock(file)) { |
---|
| 1032 | + v |= FDPUT_POS_UNLOCK; |
---|
| 1033 | + mutex_lock(&file->f_pos_lock); |
---|
1019 | 1034 | } |
---|
1020 | 1035 | return v; |
---|
1021 | 1036 | } |
---|