Whamcloud - gitweb
LU-13197 e2fsck: fix e2fsck_allocate_memory() overflow 23/37423/7
authorAndreas Dilger <adilger@whamcloud.com>
Tue, 4 Feb 2020 05:32:12 +0000 (22:32 -0700)
committerAndreas Dilger <adilger@whamcloud.com>
Fri, 22 May 2020 06:18:23 +0000 (06:18 +0000)
e2fsck_allocate_memory() takes an "unsigned int size" argument, which
will overflow for allocations above 4GB.  This happens for dir_info
and dx_dir_info arrays when there are more than 350M directories in a
filesystem, and for the dblist array above 180M directories.

Also, in some places inode numbers are "int" instead of "ext2_ino_t",
which can also cause problems with the array size calculations, and
makes it hard to identify where inode numbers are used.

Fix e2fsck_allocate_memory() to take an "unsigned long" argument so
that it can do single memory allocations larger than 4GB.  Change
variables that hold inode numbers to be ext2_ino_t, and print them as
unsigned values instead of signed.

There is also a risk of overflow during the binary search in both
e2fsck_get_dir_info() and e2fsck_get_dx_dir_info() when the midpoint
of the array is calculated, if there would be more than 2B directories
in the filesystem and working above the half way point.

Signed-off-by: Andreas Dilger <adilger@whamcloud.com>
Change-Id: If6624feb391be2ebf2ddfa46f62a0d2b3f3ebbe5
Reviewed-on: https://review.whamcloud.com/37423
Reviewed-by: Wang Shilong <wshilong@ddn.com>
Tested-by: jenkins <devops@whamcloud.com>
Tested-by: Maloo <maloo@whamcloud.com>
e2fsck/dirinfo.c
e2fsck/dx_dirinfo.c
e2fsck/e2fsck.h
e2fsck/logfile.c
e2fsck/pass2.c
e2fsck/util.c

index cceadac..49d624c 100644 (file)
@@ -17,8 +17,8 @@
 #include <ext2fs/tdb.h>
 
 struct dir_info_db {
-       int             count;
-       int             size;
+       ext2_ino_t      count;
+       ext2_ino_t      size;
        struct dir_info *array;
        struct dir_info *last_lookup;
 #ifdef CONFIG_TDB
@@ -28,7 +28,7 @@ struct dir_info_db {
 };
 
 struct dir_info_iter {
-       int     i;
+       ext2_ino_t      i;
 #ifdef CONFIG_TDB
        TDB_DATA        tdb_iter;
 #endif
@@ -46,7 +46,7 @@ static void e2fsck_put_dir_info(e2fsck_t ctx, struct dir_info *dir);
 static void setup_tdb(e2fsck_t ctx, ext2_ino_t num_dirs)
 {
        struct dir_info_db      *db = ctx->dir_info;
-       unsigned int            threshold;
+       ext2_ino_t              threshold;
        errcode_t               retval;
        mode_t                  save_umask;
        char                    *tdb_dir, uuid[40];
@@ -130,12 +130,12 @@ static void setup_db(e2fsck_t ctx)
 void e2fsck_add_dir_info(e2fsck_t ctx, ext2_ino_t ino, ext2_ino_t parent)
 {
        struct dir_info         *dir, *old_array;
-       int                     i, j;
+       ext2_ino_t              i, j;
        errcode_t               retval;
        unsigned long           old_size;
 
 #ifdef DIRINFO_DEBUG
-       printf("add_dir_info for inode (%lu, %lu)...\n", ino, parent);
+       printf("add_dir_info for inode (%u, %u)...\n", ino, parent);
 #endif
        if (!ctx->dir_info)
                setup_db(ctx);
@@ -149,7 +149,7 @@ void e2fsck_add_dir_info(e2fsck_t ctx, ext2_ino_t ino, ext2_ino_t parent)
                                           &ctx->dir_info->array);
                if (retval) {
                        fprintf(stderr, "Couldn't reallocate dir_info "
-                               "structure to %d entries\n",
+                               "structure to %u entries\n",
                                ctx->dir_info->size);
                        fatal_error(ctx, 0);
                        ctx->dir_info->size -= 10;
@@ -204,13 +204,13 @@ void e2fsck_add_dir_info(e2fsck_t ctx, ext2_ino_t ino, ext2_ino_t parent)
 static struct dir_info *e2fsck_get_dir_info(e2fsck_t ctx, ext2_ino_t ino)
 {
        struct dir_info_db      *db = ctx->dir_info;
-       int                     low, high, mid;
+       ext2_ino_t low, high, mid;
 
        if (!db)
                return 0;
 
 #ifdef DIRINFO_DEBUG
-       printf("e2fsck_get_dir_info %d...", ino);
+       printf("e2fsck_get_dir_info %u...", ino);
 #endif
 
 #ifdef CONFIG_TDB
@@ -235,7 +235,7 @@ static struct dir_info *e2fsck_get_dir_info(e2fsck_t ctx, ext2_ino_t ino)
                ret_dir_info.dotdot = buf->dotdot;
                ret_dir_info.parent = buf->parent;
 #ifdef DIRINFO_DEBUG
-               printf("(%d,%d,%d)\n", ino, buf->dotdot, buf->parent);
+               printf("(%u,%u,%u)\n", ino, buf->dotdot, buf->parent);
 #endif
                free(data.dptr);
                return &ret_dir_info;
@@ -246,10 +246,10 @@ static struct dir_info *e2fsck_get_dir_info(e2fsck_t ctx, ext2_ino_t ino)
                return db->last_lookup;
 
        low = 0;
-       high = ctx->dir_info->count-1;
+       high = ctx->dir_info->count - 1;
        if (ino == ctx->dir_info->array[low].ino) {
 #ifdef DIRINFO_DEBUG
-               printf("(%d,%d,%d)\n", ino,
+               printf("(%u,%u,%u)\n", ino,
                       ctx->dir_info->array[low].dotdot,
                       ctx->dir_info->array[low].parent);
 #endif
@@ -257,7 +257,7 @@ static struct dir_info *e2fsck_get_dir_info(e2fsck_t ctx, ext2_ino_t ino)
        }
        if (ino == ctx->dir_info->array[high].ino) {
 #ifdef DIRINFO_DEBUG
-               printf("(%d,%d,%d)\n", ino,
+               printf("(%u,%u,%u)\n", ino,
                       ctx->dir_info->array[high].dotdot,
                       ctx->dir_info->array[high].parent);
 #endif
@@ -265,12 +265,13 @@ static struct dir_info *e2fsck_get_dir_info(e2fsck_t ctx, ext2_ino_t ino)
        }
 
        while (low < high) {
-               mid = (low+high)/2;
+               /* sum may overflow, but result will fit into mid again */
+               mid = (unsigned long long)(low + high) / 2;
                if (mid == low || mid == high)
                        break;
                if (ino == ctx->dir_info->array[mid].ino) {
 #ifdef DIRINFO_DEBUG
-                       printf("(%d,%d,%d)\n", ino,
+                       printf("(%u,%u,%u)\n", ino,
                               ctx->dir_info->array[mid].dotdot,
                               ctx->dir_info->array[mid].parent);
 #endif
@@ -294,7 +295,7 @@ static void e2fsck_put_dir_info(e2fsck_t ctx EXT2FS_NO_TDB_UNUSED,
 #endif
 
 #ifdef DIRINFO_DEBUG
-       printf("e2fsck_put_dir_info (%d, %d, %d)...", dir->ino, dir->dotdot,
+       printf("e2fsck_put_dir_info (%u, %u, %u)...", dir->ino, dir->dotdot,
               dir->parent);
 #endif
 
@@ -329,7 +330,7 @@ void e2fsck_free_dir_info(e2fsck_t ctx)
                        if (unlink(ctx->dir_info->tdb_fn) < 0)
                                com_err("e2fsck_free_dir_info", errno,
                                        _("while freeing dir_info tdb file"));
-                       free(ctx->dir_info->tdb_fn);
+                       ext2fs_free_mem(&ctx->dir_info->tdb_fn);
                }
 #endif
                if (ctx->dir_info->array)
@@ -412,7 +413,7 @@ struct dir_info *e2fsck_dir_info_iter(e2fsck_t ctx, struct dir_info_iter *iter)
                return 0;
 
 #ifdef DIRINFO_DEBUG
-       printf("iter(%d, %d, %d)...", ctx->dir_info->array[iter->i].ino,
+       printf("iter(%u, %u, %u)...", ctx->dir_info->array[iter->i].ino,
               ctx->dir_info->array[iter->i].dotdot,
               ctx->dir_info->array[iter->i].parent);
 #endif
index c0b0e9a..89672b7 100644 (file)
@@ -17,7 +17,7 @@ void e2fsck_add_dx_dir(e2fsck_t ctx, ext2_ino_t ino, struct ext2_inode *inode,
                       int num_blocks)
 {
        struct dx_dir_info *dir;
-       int             i, j;
+       ext2_ino_t      i, j;
        errcode_t       retval;
        unsigned long   old_size;
 
@@ -41,7 +41,7 @@ void e2fsck_add_dx_dir(e2fsck_t ctx, ext2_ino_t ino, struct ext2_inode *inode,
                                           &ctx->dx_dir_info);
                if (retval) {
                        fprintf(stderr, "Couldn't reallocate dx_dir_info "
-                               "structure to %d entries\n",
+                               "structure to %u entries\n",
                                ctx->dx_dir_info_size);
                        fatal_error(ctx, 0);
                        ctx->dx_dir_info_size -= 10;
@@ -86,7 +86,7 @@ void e2fsck_add_dx_dir(e2fsck_t ctx, ext2_ino_t ino, struct ext2_inode *inode,
  */
 struct dx_dir_info *e2fsck_get_dx_dir_info(e2fsck_t ctx, ext2_ino_t ino)
 {
-       int     low, high, mid;
+       ext2_ino_t low, high, mid;
 
        low = 0;
        high = ctx->dx_dir_info_count-1;
@@ -98,7 +98,8 @@ struct dx_dir_info *e2fsck_get_dx_dir_info(e2fsck_t ctx, ext2_ino_t ino)
                return &ctx->dx_dir_info[high];
 
        while (low < high) {
-               mid = (low+high)/2;
+               /* sum may overflow, but result will fit into mid again */
+               mid = (unsigned long long)(low + high) / 2;
                if (mid == low || mid == high)
                        break;
                if (ino == ctx->dx_dir_info[mid].ino)
@@ -116,8 +117,8 @@ struct dx_dir_info *e2fsck_get_dx_dir_info(e2fsck_t ctx, ext2_ino_t ino)
  */
 void e2fsck_free_dx_dir_info(e2fsck_t ctx)
 {
-       int     i;
        struct dx_dir_info *dir;
+       ext2_ino_t i;
 
        if (ctx->dx_dir_info) {
                dir = ctx->dx_dir_info;
index e3ab420..58b8f9d 100644 (file)
@@ -350,9 +350,9 @@ struct e2fsck_struct {
        /*
         * Indexed directory information
         */
-       int             dx_dir_info_count;
-       int             dx_dir_info_size;
-       struct dx_dir_info *dx_dir_info;
+       ext2_ino_t              dx_dir_info_count;
+       ext2_ino_t              dx_dir_info_size;
+       struct dx_dir_info      *dx_dir_info;
 
        /*
         * Directories to hash
@@ -643,7 +643,7 @@ int check_backup_super_block(e2fsck_t ctx);
 void check_resize_inode(e2fsck_t ctx);
 
 /* util.c */
-extern void *e2fsck_allocate_memory(e2fsck_t ctx, unsigned int size,
+extern void *e2fsck_allocate_memory(e2fsck_t ctx, unsigned long size,
                                    const char *description);
 extern int ask(e2fsck_t ctx, const char * string, int def);
 extern int ask_yn(e2fsck_t ctx, const char * string, int def);
index 3eeefd1..63e9a12 100644 (file)
@@ -353,7 +353,7 @@ void set_up_logging(e2fsck_t ctx)
                                            ctx->problem_log_fn);
 }
 #else
-void *e2fsck_allocate_memory(e2fsck_t ctx, unsigned int size,
+void *e2fsck_allocate_memory(e2fsck_t ctx, unsigned long size,
                             const char *description)
 {
        void *ret;
index 37f3f72..969db54 100644 (file)
@@ -130,7 +130,8 @@ void e2fsck_pass2(e2fsck_t ctx)
        struct dx_dir_info      *dx_dir;
        struct dx_dirblock_info *dx_db;
        int                     b;
-       int                     i, depth;
+       ext2_ino_t              i;
+       int                     depth;
        problem_t               code;
        int                     bad_dir;
        int (*check_dir_func)(ext2_filsys fs,
@@ -670,10 +671,10 @@ static void parse_int_node(ext2_filsys fs,
 #ifdef DX_DEBUG
                printf("Root node dump:\n");
                printf("\t Reserved zero: %u\n", root->reserved_zero);
-               printf("\t Hash Version: %d\n", root->hash_version);
-               printf("\t Info length: %d\n", root->info_length);
-               printf("\t Indirect levels: %d\n", root->indirect_levels);
-               printf("\t Flags: %d\n", root->unused_flags);
+               printf("\t Hash Version: %u\n", root->hash_version);
+               printf("\t Info length: %u\n", root->info_length);
+               printf("\t Indirect levels: %u\n", root->indirect_levels);
+               printf("\t Flags: %u\n", root->unused_flags);
 #endif
                ent = (struct ext2_dx_entry *)((char *)root +
                                               root->info_length);
index db6a1cc..300993d 100644 (file)
@@ -116,7 +116,7 @@ void log_err(e2fsck_t ctx, const char *fmt, ...)
        }
 }
 
-void *e2fsck_allocate_memory(e2fsck_t ctx, unsigned int size,
+void *e2fsck_allocate_memory(e2fsck_t ctx, unsigned long size,
                             const char *description)
 {
        void *ret;
@@ -125,13 +125,12 @@ void *e2fsck_allocate_memory(e2fsck_t ctx, unsigned int size,
 #ifdef DEBUG_ALLOCATE_MEMORY
        printf("Allocating %u bytes for %s...\n", size, description);
 #endif
-       ret = malloc(size);
-       if (!ret) {
+       if (ext2fs_get_memzero(size, &ret)) {
                sprintf(buf, "Can't allocate %u bytes for %s\n",
                        size, description);
                fatal_error(ctx, buf);
        }
-       memset(ret, 0, size);
+
        return ret;
 }