Skip to content

Commit

Permalink
vfs: don't force a big memset of stat data just to clear padding fields
Browse files Browse the repository at this point in the history
Admittedly this is something that the compiler should be able to just do
for us, but gcc just isn't that smart.  And trying to use a structure
initializer (which would get us the right semantics) ends up resulting
in gcc allocating stack space for _two_ 'struct stat', and then copying
one into the other.

So do it by hand - just have a per-architecture macro that initializes
the padding fields.  And if the architecture doesn't provide one, fall
back to the old behavior of just doing the whole memset() first.

Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
  • Loading branch information
Linus Torvalds committed May 7, 2012
1 parent a52dd97 commit 8529f61
Show file tree
Hide file tree
Showing 2 changed files with 31 additions and 2 deletions.
21 changes: 21 additions & 0 deletions arch/x86/include/asm/stat.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,12 @@ struct stat {
unsigned long __unused5;
};

/* We don't need to memset the whole thing just to initialize the padding */
#define INIT_STRUCT_STAT_PADDING(st) do { \
st.__unused4 = 0; \
st.__unused5 = 0; \
} while (0)

#define STAT64_HAS_BROKEN_ST_INO 1

/* This matches struct stat64 in glibc2.1, hence the absolutely
Expand Down Expand Up @@ -63,6 +69,12 @@ struct stat64 {
unsigned long long st_ino;
};

/* We don't need to memset the whole thing just to initialize the padding */
#define INIT_STRUCT_STAT64_PADDING(st) do { \
memset(&st.__pad0, 0, sizeof(st.__pad0)); \
memset(&st.__pad3, 0, sizeof(st.__pad3)); \
} while (0)

#else /* __i386__ */

struct stat {
Expand All @@ -87,6 +99,15 @@ struct stat {
unsigned long st_ctime_nsec;
long __unused[3];
};

/* We don't need to memset the whole thing just to initialize the padding */
#define INIT_STRUCT_STAT_PADDING(st) do { \
st.__pad0 = 0; \
st.__unused[0] = 0; \
st.__unused[1] = 0; \
st.__unused[2] = 0; \
} while (0)

#endif

/* for 32bit emulation and 32 bit kernels */
Expand Down
12 changes: 10 additions & 2 deletions fs/stat.c
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,10 @@ SYSCALL_DEFINE2(fstat, unsigned int, fd, struct __old_kernel_stat __user *, stat
#define valid_dev(x) choose_32_64(old_valid_dev,new_valid_dev)(x)
#define encode_dev(x) choose_32_64(old_encode_dev,new_encode_dev)(x)

#ifndef INIT_STRUCT_STAT_PADDING
# define INIT_STRUCT_STAT_PADDING(st) memset(&st, 0, sizeof(st))
#endif

static int cp_new_stat(struct kstat *stat, struct stat __user *statbuf)
{
struct stat tmp;
Expand All @@ -210,7 +214,7 @@ static int cp_new_stat(struct kstat *stat, struct stat __user *statbuf)
return -EOVERFLOW;
#endif

memset(&tmp, 0, sizeof(tmp));
INIT_STRUCT_STAT_PADDING(tmp);
tmp.st_dev = encode_dev(stat->dev);
tmp.st_ino = stat->ino;
if (sizeof(tmp.st_ino) < sizeof(stat->ino) && tmp.st_ino != stat->ino)
Expand Down Expand Up @@ -323,11 +327,15 @@ SYSCALL_DEFINE3(readlink, const char __user *, path, char __user *, buf,
/* ---------- LFS-64 ----------- */
#ifdef __ARCH_WANT_STAT64

#ifndef INIT_STRUCT_STAT64_PADDING
# define INIT_STRUCT_STAT64_PADDING(st) memset(&st, 0, sizeof(st))
#endif

static long cp_new_stat64(struct kstat *stat, struct stat64 __user *statbuf)
{
struct stat64 tmp;

memset(&tmp, 0, sizeof(struct stat64));
INIT_STRUCT_STAT64_PADDING(tmp);
#ifdef CONFIG_MIPS
/* mips has weird padding, so we don't get 64 bits there */
if (!new_valid_dev(stat->dev) || !new_valid_dev(stat->rdev))
Expand Down

0 comments on commit 8529f61

Please sign in to comment.