summaryrefslogtreecommitdiff
path: root/extmod/modbtree.c
diff options
context:
space:
mode:
authorMichael Vornovitsky <michaelvornovitskiy@outlook.com>2023-11-11 20:10:51 -0500
committerDamien George <damien@micropython.org>2024-07-22 10:42:29 +1000
commit6db91dfefb1a7ba0026106d8f0c6589630e9a012 (patch)
treedbbf7ce6a292feb26ab17404715019baa68e8a94 /extmod/modbtree.c
parent8159dcc276dc07bcb28d6ab3a3b7d2a5c1d90ab5 (diff)
extmod/modbtree: Add checks for already-closed database.
Fixes use-after-free when accessing the database after it is closed with `btree_close`. `btree_close` always succeeds when called with an already-closed database. The new test checks that operations that access the underlying database (get, set, flush, seq) fail with a `ValueError` when the btree is already closed. It also checks that closing and printing the btree succeed when the btree is already closed. Fixes issue #12543. Signed-off-by: Michael Vornovitsky <michaelvornovitskiy@outlook.com>
Diffstat (limited to 'extmod/modbtree.c')
-rw-r--r--extmod/modbtree.c22
1 files changed, 21 insertions, 1 deletions
diff --git a/extmod/modbtree.c b/extmod/modbtree.c
index 55c13ac91..f48645a82 100644
--- a/extmod/modbtree.c
+++ b/extmod/modbtree.c
@@ -89,6 +89,12 @@ void __dbpanic(DB *db) {
mp_printf(&mp_plat_print, "__dbpanic(%p)\n", db);
}
+static void check_btree_is_open(mp_obj_btree_t *self) {
+ if (!self->db) {
+ mp_raise_ValueError(MP_ERROR_TEXT("database closed"));
+ }
+}
+
static mp_obj_btree_t *btree_new(DB *db, mp_obj_t stream) {
mp_obj_btree_t *o = mp_obj_malloc(mp_obj_btree_t, (mp_obj_type_t *)&btree_type);
o->stream = stream;
@@ -114,19 +120,28 @@ static void btree_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind
static mp_obj_t btree_flush(mp_obj_t self_in) {
mp_obj_btree_t *self = MP_OBJ_TO_PTR(self_in);
+ check_btree_is_open(self);
return MP_OBJ_NEW_SMALL_INT(__bt_sync(self->db, 0));
}
static MP_DEFINE_CONST_FUN_OBJ_1(btree_flush_obj, btree_flush);
static mp_obj_t btree_close(mp_obj_t self_in) {
mp_obj_btree_t *self = MP_OBJ_TO_PTR(self_in);
- return MP_OBJ_NEW_SMALL_INT(__bt_close(self->db));
+ int res;
+ if (self->db) {
+ res = __bt_close(self->db);
+ self->db = NULL;
+ } else {
+ res = RET_SUCCESS; // Closing an already-closed DB always succeeds.
+ }
+ return MP_OBJ_NEW_SMALL_INT(res);
}
static MP_DEFINE_CONST_FUN_OBJ_1(btree_close_obj, btree_close);
static mp_obj_t btree_put(size_t n_args, const mp_obj_t *args) {
(void)n_args;
mp_obj_btree_t *self = MP_OBJ_TO_PTR(args[0]);
+ check_btree_is_open(self);
DBT key, val;
buf_to_dbt(args[1], &key);
buf_to_dbt(args[2], &val);
@@ -136,6 +151,7 @@ static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(btree_put_obj, 3, 4, btree_put);
static mp_obj_t btree_get(size_t n_args, const mp_obj_t *args) {
mp_obj_btree_t *self = MP_OBJ_TO_PTR(args[0]);
+ check_btree_is_open(self);
DBT key, val;
buf_to_dbt(args[1], &key);
int res = __bt_get(self->db, &key, &val, 0);
@@ -153,6 +169,7 @@ static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(btree_get_obj, 2, 3, btree_get);
static mp_obj_t btree_seq(size_t n_args, const mp_obj_t *args) {
mp_obj_btree_t *self = MP_OBJ_TO_PTR(args[0]);
+ check_btree_is_open(self);
int flags = MP_OBJ_SMALL_INT_VALUE(args[1]);
DBT key, val;
if (n_args > 2) {
@@ -225,6 +242,7 @@ static mp_obj_t btree_getiter(mp_obj_t self_in, mp_obj_iter_buf_t *iter_buf) {
static mp_obj_t btree_iternext(mp_obj_t self_in) {
mp_obj_btree_t *self = MP_OBJ_TO_PTR(self_in);
+ check_btree_is_open(self);
DBT key, val;
int res;
bool desc = self->flags & FLAG_DESC;
@@ -281,6 +299,7 @@ static mp_obj_t btree_iternext(mp_obj_t self_in) {
static mp_obj_t btree_subscr(mp_obj_t self_in, mp_obj_t index, mp_obj_t value) {
mp_obj_btree_t *self = MP_OBJ_TO_PTR(self_in);
+ check_btree_is_open(self);
if (value == MP_OBJ_NULL) {
// delete
DBT key;
@@ -314,6 +333,7 @@ static mp_obj_t btree_subscr(mp_obj_t self_in, mp_obj_t index, mp_obj_t value) {
static mp_obj_t btree_binary_op(mp_binary_op_t op, mp_obj_t lhs_in, mp_obj_t rhs_in) {
mp_obj_btree_t *self = MP_OBJ_TO_PTR(lhs_in);
+ check_btree_is_open(self);
switch (op) {
case MP_BINARY_OP_CONTAINS: {
DBT key, val;