From a92c532573b293f5c0ed4a386c866bc9a3dbeef3 Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Fri, 6 Apr 2012 14:20:12 -0700 Subject: Bug found by Herb. blkcnt_t st_ex_blksize is defined as a signed value. When it is on a 32-bit system and defined as a long, then inside vfswrap_get_alloc_size() we cast to a uint64_t. This sign-extends when converting to unsigned, so if the high bit of st_ex_blksize is set we return insane values to clients. --- source3/configure.in | 26 ++++++++++++++++++++++++++ source3/modules/vfs_default.c | 13 +++++++++++++ source3/wscript | 14 ++++++++++++++ 3 files changed, 53 insertions(+) (limited to 'source3') diff --git a/source3/configure.in b/source3/configure.in index 8e44c23da0..98714d5dd2 100644 --- a/source3/configure.in +++ b/source3/configure.in @@ -2936,6 +2936,32 @@ fi AC_CHECK_TYPES([blksize_t, blkcnt_t], [], [], [[#include ]]) +AC_CACHE_CHECK([for 32 bit blkcnt_t],samba_cv_SIZEOF_BLKCNT_T_4,[ +AC_TRY_RUN([ +#if defined(HAVE_UNISTD_H) +#include +#endif +#include +#include +main() { exit((sizeof(blkcnt_t) == 4) ? 0 : 1); }], +samba_cv_SIZEOF_BLKCNT_T_4=yes,samba_cv_SIZEOF_BLKCNT_T_4=no,samba_cv_SIZEOF_BLKCNT_T_4=cross)]) +if test x"$samba_cv_SIZEOF_BLKCNT_T_4" = x"yes"; then + AC_DEFINE(SIZEOF_BLKCNT_T_4,1,[The size of the 'blkcnt_t' type]) +fi + +AC_CACHE_CHECK([for 64 bit blkcnt_t],samba_cv_SIZEOF_BLKCNT_T_8,[ +AC_TRY_RUN([ +#if defined(HAVE_UNISTD_H) +#include +#endif +#include +#include +main() { exit((sizeof(blkcnt_t) == 8) ? 0 : 1); }], +samba_cv_SIZEOF_BLKCNT_T_8=yes,samba_cv_SIZEOF_BLKCNT_T_8=no,samba_cv_SIZEOF_BLKCNT_T_8=cross)]) +if test x"$samba_cv_SIZEOF_BLKCNT_T_8" = x"yes"; then + AC_DEFINE(SIZEOF_BLKCNT_T_8,1,[The size of the 'blkcnt_t' type]) +fi + AC_CACHE_CHECK([for st_blksize in struct stat],samba_cv_HAVE_STAT_ST_BLKSIZE,[ AC_TRY_COMPILE([#include #include diff --git a/source3/modules/vfs_default.c b/source3/modules/vfs_default.c index cf2bdb05c5..915eae67d3 100644 --- a/source3/modules/vfs_default.c +++ b/source3/modules/vfs_default.c @@ -1119,7 +1119,20 @@ static uint64_t vfswrap_get_alloc_size(vfs_handle_struct *handle, } #if defined(HAVE_STAT_ST_BLOCKS) && defined(STAT_ST_BLOCKSIZE) + /* The type of st_blocksize is blkcnt_t which *MUST* be + signed (according to POSIX) and can be less than 64-bits. + Ensure when we're converting to 64 bits wide we don't + sign extend. */ +#if defined(SIZEOF_BLKCNT_T_8) result = (uint64_t)STAT_ST_BLOCKSIZE * (uint64_t)sbuf->st_ex_blocks; +#elif defined(SIZEOF_BLKCNT_T_4) + { + uint64_t bs = ((uint64_t)sbuf->st_ex_blocks) & 0xFFFFFFFFLL; + result = (uint64_t)STAT_ST_BLOCKSIZE * bs; + } +#else +#error SIZEOF_BLKCNT_T_NOT_A_SUPPORTED_VALUE +#endif #else result = get_file_size_stat(sbuf); #endif diff --git a/source3/wscript b/source3/wscript index 9a9757a7b2..28ef0d932f 100644 --- a/source3/wscript +++ b/source3/wscript @@ -174,6 +174,20 @@ main() { conf.CHECK_STRUCTURE_MEMBER('struct stat', 'st_flags', define='HAVE_STAT_ST_FLAGS', headers='sys/types.h sys/stat.h unistd.h') + if "HAVE_BLKCNT_T" in conf.env: + conf.CHECK_CODE(''' + return sizeof(blkcnt_t) == 4 ? 0 : 1''', + 'SIZEOF_BLKCNT_T_4', execute=True, + headers='sys/types.h sys/stat.h unistd.h', + msg="Checking whether blkcnt_t is 32 bit") + + if "HAVE_BLKCNT_T" in conf.env: + conf.CHECK_CODE(''' + return sizeof(blkcnt_t) == 8 ? 0 : 1''', + 'SIZEOF_BLKCNT_T_8', execute=True, + headers='sys/types.h sys/stat.h unistd.h', + msg="Checking whether blkcnt_t is 64 bit") + # Check for POSIX capability support conf.CHECK_FUNCS_IN('cap_get_proc', 'cap', headers='sys/capability.h') -- cgit