From 1d289a65b3f7919037e0f59bd2607da9f4d9664b Mon Sep 17 00:00:00 2001 From: Evan Miller Date: Wed, 5 Aug 2020 22:35:56 -0400 Subject: [PATCH] Fix buffer overruns and NULL pointer deferences --- src/libmdb/data.c | 18 +++++++++++++----- src/libmdb/table.c | 5 +++++ src/libmdb/write.c | 38 +++++++++++++++++++++++++++++++------- 3 files changed, 49 insertions(+), 12 deletions(-) diff --git a/src/libmdb/data.c b/src/libmdb/data.c index fc3dbd8..883294c 100644 --- a/src/libmdb/data.c +++ b/src/libmdb/data.c @@ -111,20 +111,21 @@ mdb_bind_column_by_name(MdbTableDef *table, gchar *col_name, void *bind_ptr, int * @off: Pointer for returning an offset to the row * @len: Pointer for returning the length of the row * - * Returns: 0 on success. 1 on failure. + * Returns: 0 on success. -1 on failure. */ int mdb_find_pg_row(MdbHandle *mdb, int pg_row, void **buf, int *off, size_t *len) { unsigned int pg = pg_row >> 8; unsigned int row = pg_row & 0xff; + int result = 0; if (mdb_read_alt_pg(mdb, pg) != mdb->fmt->pg_size) - return 1; + return -1; mdb_swap_pgbuf(mdb); - mdb_find_row(mdb, row, off, len); + result = mdb_find_row(mdb, row, off, len); mdb_swap_pgbuf(mdb); *buf = mdb->alt_pg_buf; - return 0; + return result; } int mdb_find_row(MdbHandle *mdb, int row, int *start, size_t *len) @@ -138,6 +139,11 @@ int mdb_find_row(MdbHandle *mdb, int row, int *start, size_t *len) next_start = (row == 0) ? mdb->fmt->pg_size : mdb_get_int16(mdb->pg_buf, rco + row*2) & OFFSET_MASK; *len = next_start - (*start & OFFSET_MASK); + + if ((*start & OFFSET_MASK) >= mdb->fmt->pg_size || + next_start > mdb->fmt->pg_size) + return -1; + return 0; } @@ -277,7 +283,7 @@ int mdb_read_row(MdbTableDef *table, unsigned int row) return 0; if (mdb_find_row(mdb, row, &row_start, &row_size)) { - fprintf(stderr, "warning: mdb_find_row failed."); + fprintf(stderr, "warning: mdb_find_row failed.\n"); return 0; } @@ -298,6 +304,8 @@ int mdb_read_row(MdbTableDef *table, unsigned int row) num_fields = mdb_crack_row(table, row_start, row_start + row_size - 1, fields); + if (num_fields < 0) + return 0; if (!mdb_test_sargs(table, fields, num_fields)) return 0; #if MDB_DEBUG diff --git a/src/libmdb/table.c b/src/libmdb/table.c index 348729a..72395cb 100644 --- a/src/libmdb/table.c +++ b/src/libmdb/table.c @@ -99,6 +99,11 @@ MdbTableDef *mdb_read_table(MdbCatalogEntry *entry) mdb_debug(MDB_DEBUG_USAGE,"free map found on page %ld row %d start %d len %d\n", pg_row >> 8, pg_row & 0xff, row_start, table->freemap_sz); + if (!table->usage_map || !table->free_usage_map) { + mdb_free_tabledef(table); + return NULL; + } + table->first_data_pg = mdb_get_int16(pg_buf, fmt->tab_first_dpg_offset); if (entry->props) diff --git a/src/libmdb/write.c b/src/libmdb/write.c index af56314..4ef4793 100644 --- a/src/libmdb/write.c +++ b/src/libmdb/write.c @@ -112,17 +112,22 @@ mdb_is_col_indexed(MdbTableDef *table, int colnum) return 0; } -static void +static int mdb_crack_row4(MdbHandle *mdb, int row_start, int row_end, unsigned int bitmask_sz, unsigned int row_var_cols, unsigned int *var_col_offsets) { unsigned int i; + if (bitmask_sz + 3 + row_var_cols*2 + 2 > row_end) + return 0; + for (i=0; ipg_buf, row_end - bitmask_sz - 3 - (i*2)); } + + return 1; } -static void +static int mdb_crack_row3(MdbHandle *mdb, int row_start, int row_end, unsigned int bitmask_sz, unsigned int row_var_cols, unsigned int *var_col_offsets) { unsigned int i; @@ -136,6 +141,9 @@ mdb_crack_row3(MdbHandle *mdb, int row_start, int row_end, unsigned int bitmask_ if ((col_ptr-row_start-row_var_cols)/256 < num_jumps) num_jumps--; + if (bitmask_sz + num_jumps + 1 > row_end) + return 0; + jumps_used = 0; for (i=0; ipg_buf[col_ptr-i]+(jumps_used*256); } + + return 1; } /** * mdb_crack_row: @@ -164,7 +174,7 @@ mdb_crack_row3(MdbHandle *mdb, int row_start, int row_end, unsigned int bitmask_ * This routine is mostly used internally by mdb_fetch_row() but may have some * applicability for advanced application programs. * - * Return value: number of fields present. + * Return value: number of fields present, or -1 if the buffer is invalid. */ int mdb_crack_row(MdbTableDef *table, int row_start, int row_end, MdbField *fields) @@ -194,6 +204,11 @@ mdb_crack_row(MdbTableDef *table, int row_start, int row_end, MdbField *fields) } bitmask_sz = (row_cols + 7) / 8; + if (bitmask_sz >= row_end) { + fprintf(stderr, "warning: Invalid page buffer detected in mdb_crack_row.\n"); + return -1; + } + nullmask = (unsigned char*)pg_buf + row_end - bitmask_sz + 1; /* read table of variable column locations */ @@ -202,13 +217,18 @@ mdb_crack_row(MdbTableDef *table, int row_start, int row_end, MdbField *fields) mdb_get_byte(pg_buf, row_end - bitmask_sz) : mdb_get_int16(pg_buf, row_end - bitmask_sz - 1); var_col_offsets = (unsigned int *)g_malloc((row_var_cols+1)*sizeof(int)); + int success = 0; if (IS_JET3(mdb)) { - mdb_crack_row3(mdb, row_start, row_end, bitmask_sz, - row_var_cols, var_col_offsets); + success = mdb_crack_row3(mdb, row_start, row_end, bitmask_sz, + row_var_cols, var_col_offsets); } else { - mdb_crack_row4(mdb, row_start, row_end, bitmask_sz, - row_var_cols, var_col_offsets); + success = mdb_crack_row4(mdb, row_start, row_end, bitmask_sz, + row_var_cols, var_col_offsets); } + if (!success) { + fprintf(stderr, "warning: Invalid page buffer detected in mdb_crack_row.\n"); + return -1; + } } fixed_cols_found = 0; @@ -687,6 +707,10 @@ unsigned int num_fields; } } num_fields = mdb_crack_row(table, row_start, row_end, fields); + if (num_fields == -1) { + fprintf(stderr, "Invalid row buffer, update will not occur\n"); + return 0; + } if (mdb_get_option(MDB_DEBUG_WRITE)) { for (i=0;i