dbwrap_rbt: add nested traverse protection
authorStefan Metzmacher <metze@samba.org>
Wed, 25 Nov 2015 08:22:08 +0000 (09:22 +0100)
committerKarolin Seeger <kseeger@samba.org>
Mon, 11 Jan 2016 10:21:09 +0000 (11:21 +0100)
Multiple dbwrap_traverse_read() calls are possible.

store() and delete() on a fetch locked record
are rejected during dbwrap_traverse_read().

A dbwrap_traverse() within a dbwrap_traverse_read()
behaves like a dbwrap_traverse_read().

Nested dbwrap_traverse() calls are not possible.

BUG: https://bugzilla.samba.org/show_bug.cgi?id=11375
BUG: https://bugzilla.samba.org/show_bug.cgi?id=11394

Signed-off-by: Stefan Metzmacher <metze@samba.org>
Reviewed-by: Volker Lendecke <vl@samba.org>
(cherry picked from commit 590507951fc514a679f44b8bfdd03c721189c3fa)

lib/dbwrap/dbwrap_rbt.c

index 2d656472219fa8256e44e18bb82cea3d8107cf9f..d4cb40d3d93fac59b4a79b6ba4f1225855a9e550 100644 (file)
@@ -27,6 +27,8 @@
 
 struct db_rbt_ctx {
        struct rb_root tree;
+       size_t traverse_read;
+       bool traverse_write;
 };
 
 struct db_rbt_rec {
@@ -126,6 +128,10 @@ static NTSTATUS db_rbt_store(struct db_record *rec, TDB_DATA data, int flag)
        ssize_t reclen;
        TDB_DATA this_key, this_val;
 
+       if (db_ctx->traverse_read > 0) {
+               return NT_STATUS_MEDIA_WRITE_PROTECTED;
+       }
+
        if (rec_priv->node != NULL) {
 
                /*
@@ -222,6 +228,10 @@ static NTSTATUS db_rbt_delete(struct db_record *rec)
                rec->db->private_data, struct db_rbt_ctx);
        struct db_rbt_rec *rec_priv = (struct db_rbt_rec *)rec->private_data;
 
+       if (db_ctx->traverse_read > 0) {
+               return NT_STATUS_MEDIA_WRITE_PROTECTED;
+       }
+
        if (rec_priv->node == NULL) {
                return NT_STATUS_OK;
        }
@@ -232,16 +242,6 @@ static NTSTATUS db_rbt_delete(struct db_record *rec)
        return NT_STATUS_OK;
 }
 
-static NTSTATUS db_rbt_store_deny(struct db_record *rec, TDB_DATA data, int flag)
-{
-       return NT_STATUS_MEDIA_WRITE_PROTECTED;
-}
-
-static NTSTATUS db_rbt_delete_deny(struct db_record *rec)
-{
-       return NT_STATUS_MEDIA_WRITE_PROTECTED;
-}
-
 struct db_rbt_search_result {
        TDB_DATA key;
        TDB_DATA val;
@@ -414,13 +414,8 @@ static int db_rbt_traverse_internal(struct db_context *db,
        ZERO_STRUCT(rec);
        rec.db = db;
        rec.private_data = &rec_priv;
-       if (rw) {
-               rec.store = db_rbt_store;
-               rec.delete_rec = db_rbt_delete;
-       } else {
-               rec.store = db_rbt_store_deny;
-               rec.delete_rec = db_rbt_delete_deny;
-       }
+       rec.store = db_rbt_store;
+       rec.delete_rec = db_rbt_delete;
        db_rbt_parse_node(rec_priv.node, &rec.key, &rec.value);
 
        ret = f(&rec, private_data);
@@ -440,18 +435,21 @@ static int db_rbt_traverse_internal(struct db_context *db,
        return db_rbt_traverse_internal(db, rb_right, f, private_data, count, rw);
 }
 
-static int db_rbt_traverse(struct db_context *db,
-                          int (*f)(struct db_record *db,
-                                   void *private_data),
-                          void *private_data)
+static int db_rbt_traverse_read(struct db_context *db,
+                               int (*f)(struct db_record *db,
+                                        void *private_data),
+                               void *private_data)
 {
        struct db_rbt_ctx *ctx = talloc_get_type_abort(
                db->private_data, struct db_rbt_ctx);
        uint32_t count = 0;
+       int ret;
 
-       int ret = db_rbt_traverse_internal(db, ctx->tree.rb_node,
-                                          f, private_data, &count,
-                                          true /* rw */);
+       ctx->traverse_read++;
+       ret = db_rbt_traverse_internal(db, ctx->tree.rb_node,
+                                      f, private_data, &count,
+                                      false /* rw */);
+       ctx->traverse_read--;
        if (ret != 0) {
                return -1;
        }
@@ -461,18 +459,29 @@ static int db_rbt_traverse(struct db_context *db,
        return count;
 }
 
-static int db_rbt_traverse_read(struct db_context *db,
-                               int (*f)(struct db_record *db,
-                                        void *private_data),
-                               void *private_data)
+static int db_rbt_traverse(struct db_context *db,
+                          int (*f)(struct db_record *db,
+                                   void *private_data),
+                          void *private_data)
 {
        struct db_rbt_ctx *ctx = talloc_get_type_abort(
                db->private_data, struct db_rbt_ctx);
        uint32_t count = 0;
+       int ret;
+
+       if (ctx->traverse_write) {
+               return -1;
+       };
+
+       if (ctx->traverse_read > 0) {
+               return db_rbt_traverse_read(db, f, private_data);
+       }
 
-       int ret = db_rbt_traverse_internal(db, ctx->tree.rb_node,
-                                          f, private_data, &count,
-                                          false /* rw */);
+       ctx->traverse_write = true;
+       ret = db_rbt_traverse_internal(db, ctx->tree.rb_node,
+                                      f, private_data, &count,
+                                      true /* rw */);
+       ctx->traverse_write = false;
        if (ret != 0) {
                return -1;
        }