Bug found by Herb. blkcnt_t st_ex_blksize is defined as a signed value. When it is
authorJeremy Allison <jra@samba.org>
Fri, 6 Apr 2012 21:20:12 +0000 (14:20 -0700)
committerJeremy Allison <jra@samba.org>
Fri, 6 Apr 2012 21:20:12 +0000 (14:20 -0700)
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
source3/modules/vfs_default.c
source3/wscript

index 8e44c23da0224880dd816347d7bc7e562515f5b2..98714d5dd2045e6a51c3af7d4df359e1dd039c6a 100644 (file)
@@ -2936,6 +2936,32 @@ fi
 
 AC_CHECK_TYPES([blksize_t, blkcnt_t], [], [], [[#include <sys/stat.h>]])
 
+AC_CACHE_CHECK([for 32 bit blkcnt_t],samba_cv_SIZEOF_BLKCNT_T_4,[
+AC_TRY_RUN([
+#if defined(HAVE_UNISTD_H)
+#include <unistd.h>
+#endif
+#include <stdio.h>
+#include <sys/stat.h>
+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 <unistd.h>
+#endif
+#include <stdio.h>
+#include <sys/stat.h>
+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 <sys/types.h>
 #include <sys/stat.h>
index cf2bdb05c520f56a59bc9ed0c2894ed05d5089a5..915eae67d3d23b060e09b61424e02407c6d55899 100644 (file)
@@ -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
index 9a9757a7b2a6fe532bdd74b63d4828d8f803fb9b..28ef0d932f1fd4acc64473fcaaaf741c69d07f65 100644 (file)
@@ -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')