Skip to content

Commit 932aba1

Browse files
Mikulas Patockatorvalds
authored andcommitted
stat: fix inconsistency between struct stat and struct compat_stat
struct stat (defined in arch/x86/include/uapi/asm/stat.h) has 32-bit st_dev and st_rdev; struct compat_stat (defined in arch/x86/include/asm/compat.h) has 16-bit st_dev and st_rdev followed by a 16-bit padding. This patch fixes struct compat_stat to match struct stat. [ Historical note: the old x86 'struct stat' did have that 16-bit field that the compat layer had kept around, but it was changes back in 2003 by "struct stat - support larger dev_t": https://git.kernel.org/pub/scm/linux/kernel/git/tglx/history.git/commit/?id=e95b2065677fe32512a597a79db94b77b90c968d and back in those days, the x86_64 port was still new, and separate from the i386 code, and had already picked up the old version with a 16-bit st_dev field ] Note that we can't change compat_dev_t because it is used by compat_loop_info. Also, if the st_dev and st_rdev values are 32-bit, we don't have to use old_valid_dev to test if the value fits into them. This fixes -EOVERFLOW on filesystems that are on NVMe because NVMe uses the major number 259. Signed-off-by: Mikulas Patocka <mpatocka@redhat.com> Cc: Andreas Schwab <schwab@linux-m68k.org> Cc: Matthew Wilcox <willy@infradead.org> Cc: Christoph Hellwig <hch@infradead.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
1 parent 7281a59 commit 932aba1

2 files changed

Lines changed: 12 additions & 13 deletions

File tree

arch/x86/include/asm/compat.h

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,15 +28,13 @@ typedef u16 compat_ipc_pid_t;
2828
typedef __kernel_fsid_t compat_fsid_t;
2929

3030
struct compat_stat {
31-
compat_dev_t st_dev;
32-
u16 __pad1;
31+
u32 st_dev;
3332
compat_ino_t st_ino;
3433
compat_mode_t st_mode;
3534
compat_nlink_t st_nlink;
3635
__compat_uid_t st_uid;
3736
__compat_gid_t st_gid;
38-
compat_dev_t st_rdev;
39-
u16 __pad2;
37+
u32 st_rdev;
4038
u32 st_size;
4139
u32 st_blksize;
4240
u32 st_blocks;

fs/stat.c

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -348,9 +348,6 @@ SYSCALL_DEFINE2(fstat, unsigned int, fd, struct __old_kernel_stat __user *, stat
348348
# define choose_32_64(a,b) b
349349
#endif
350350

351-
#define valid_dev(x) choose_32_64(old_valid_dev(x),true)
352-
#define encode_dev(x) choose_32_64(old_encode_dev,new_encode_dev)(x)
353-
354351
#ifndef INIT_STRUCT_STAT_PADDING
355352
# define INIT_STRUCT_STAT_PADDING(st) memset(&st, 0, sizeof(st))
356353
#endif
@@ -359,15 +356,17 @@ static int cp_new_stat(struct kstat *stat, struct stat __user *statbuf)
359356
{
360357
struct stat tmp;
361358

362-
if (!valid_dev(stat->dev) || !valid_dev(stat->rdev))
359+
if (sizeof(tmp.st_dev) < 4 && !old_valid_dev(stat->dev))
360+
return -EOVERFLOW;
361+
if (sizeof(tmp.st_rdev) < 4 && !old_valid_dev(stat->rdev))
363362
return -EOVERFLOW;
364363
#if BITS_PER_LONG == 32
365364
if (stat->size > MAX_NON_LFS)
366365
return -EOVERFLOW;
367366
#endif
368367

369368
INIT_STRUCT_STAT_PADDING(tmp);
370-
tmp.st_dev = encode_dev(stat->dev);
369+
tmp.st_dev = new_encode_dev(stat->dev);
371370
tmp.st_ino = stat->ino;
372371
if (sizeof(tmp.st_ino) < sizeof(stat->ino) && tmp.st_ino != stat->ino)
373372
return -EOVERFLOW;
@@ -377,7 +376,7 @@ static int cp_new_stat(struct kstat *stat, struct stat __user *statbuf)
377376
return -EOVERFLOW;
378377
SET_UID(tmp.st_uid, from_kuid_munged(current_user_ns(), stat->uid));
379378
SET_GID(tmp.st_gid, from_kgid_munged(current_user_ns(), stat->gid));
380-
tmp.st_rdev = encode_dev(stat->rdev);
379+
tmp.st_rdev = new_encode_dev(stat->rdev);
381380
tmp.st_size = stat->size;
382381
tmp.st_atime = stat->atime.tv_sec;
383382
tmp.st_mtime = stat->mtime.tv_sec;
@@ -665,11 +664,13 @@ static int cp_compat_stat(struct kstat *stat, struct compat_stat __user *ubuf)
665664
{
666665
struct compat_stat tmp;
667666

668-
if (!old_valid_dev(stat->dev) || !old_valid_dev(stat->rdev))
667+
if (sizeof(tmp.st_dev) < 4 && !old_valid_dev(stat->dev))
668+
return -EOVERFLOW;
669+
if (sizeof(tmp.st_rdev) < 4 && !old_valid_dev(stat->rdev))
669670
return -EOVERFLOW;
670671

671672
memset(&tmp, 0, sizeof(tmp));
672-
tmp.st_dev = old_encode_dev(stat->dev);
673+
tmp.st_dev = new_encode_dev(stat->dev);
673674
tmp.st_ino = stat->ino;
674675
if (sizeof(tmp.st_ino) < sizeof(stat->ino) && tmp.st_ino != stat->ino)
675676
return -EOVERFLOW;
@@ -679,7 +680,7 @@ static int cp_compat_stat(struct kstat *stat, struct compat_stat __user *ubuf)
679680
return -EOVERFLOW;
680681
SET_UID(tmp.st_uid, from_kuid_munged(current_user_ns(), stat->uid));
681682
SET_GID(tmp.st_gid, from_kgid_munged(current_user_ns(), stat->gid));
682-
tmp.st_rdev = old_encode_dev(stat->rdev);
683+
tmp.st_rdev = new_encode_dev(stat->rdev);
683684
if ((u64) stat->size > MAX_NON_LFS)
684685
return -EOVERFLOW;
685686
tmp.st_size = stat->size;

0 commit comments

Comments
 (0)