tdb: Make tdb_recovery_size overflow-safe
authorVolker Lendecke <vl@samba.org>
Thu, 30 May 2013 13:24:27 +0000 (15:24 +0200)
committerVolker Lendecke <vl@samba.org>
Mon, 3 Jun 2013 08:21:31 +0000 (10:21 +0200)
Signed-off-by: Volker Lendecke <vl@samba.org>
Reviewed-by: Rusty Russell <rusty@rustcorp.com.au>
lib/tdb/common/tdb_private.h
lib/tdb/common/transaction.c

index c37246f1503f779761251881fa27000156286a69..ce92188688b021eb63923e2a89263a9b3c29af3f 100644 (file)
@@ -283,4 +283,7 @@ void tdb_header_hash(struct tdb_context *tdb,
 unsigned int tdb_old_hash(TDB_DATA *key);
 size_t tdb_dead_space(struct tdb_context *tdb, tdb_off_t off);
 bool tdb_add_off_t(tdb_off_t a, tdb_off_t b, tdb_off_t *pret);
+
+/* tdb_off_t and tdb_len_t right now are both uint32_t */
+#define tdb_add_len_t tdb_add_off_t
 #endif /* TDB_PRIVATE_H */
index 81cfd16942906f79b9b2dbc992c240214339bf9c..080d0586c36b35fbbf89f82b0b2415c820b76eb2 100644 (file)
@@ -630,28 +630,37 @@ _PUBLIC_ int tdb_transaction_cancel(struct tdb_context *tdb)
 /*
   work out how much space the linearised recovery data will consume
 */
-static tdb_len_t tdb_recovery_size(struct tdb_context *tdb)
+static bool tdb_recovery_size(struct tdb_context *tdb, tdb_len_t *result)
 {
        tdb_len_t recovery_size = 0;
        int i;
 
        recovery_size = sizeof(uint32_t);
        for (i=0;i<tdb->transaction->num_blocks;i++) {
+               tdb_len_t block_size;
                if (i * tdb->transaction->block_size >= tdb->transaction->old_map_size) {
                        break;
                }
                if (tdb->transaction->blocks[i] == NULL) {
                        continue;
                }
-               recovery_size += 2*sizeof(tdb_off_t);
+               if (!tdb_add_len_t(recovery_size, 2*sizeof(tdb_off_t),
+                                  &recovery_size)) {
+                       return false;
+               }
                if (i == tdb->transaction->num_blocks-1) {
-                       recovery_size += tdb->transaction->last_block_size;
+                       block_size = tdb->transaction->last_block_size;
                } else {
-                       recovery_size += tdb->transaction->block_size;
+                       block_size =  tdb->transaction->block_size;
+               }
+               if (!tdb_add_len_t(recovery_size, block_size,
+                                  &recovery_size)) {
+                       return false;
                }
        }
 
-       return recovery_size;
+       *result = recovery_size;
+       return true;
 }
 
 int tdb_recovery_area(struct tdb_context *tdb,
@@ -700,7 +709,11 @@ static int tdb_recovery_allocate(struct tdb_context *tdb,
                return -1;
        }
 
-       *recovery_size = tdb_recovery_size(tdb);
+       if (!tdb_recovery_size(tdb, recovery_size)) {
+               TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_recovery_allocate: "
+                        "overflow recovery size\n"));
+               return -1;
+       }
 
        /* Existing recovery area? */
        if (recovery_head != 0 && *recovery_size <= rec.rec_len) {
@@ -728,7 +741,12 @@ static int tdb_recovery_allocate(struct tdb_context *tdb,
 
                        /* the tdb_free() call might have increased
                         * the recovery size */
-                       *recovery_size = tdb_recovery_size(tdb);
+                       if (!tdb_recovery_size(tdb, recovery_size)) {
+                               TDB_LOG((tdb, TDB_DEBUG_FATAL,
+                                        "tdb_recovery_allocate: "
+                                        "overflow recovery size\n"));
+                               return -1;
+                       }
                }
 
                /* New head will be at end of file. */