The branch, master has been updated via ba33d90 ldb: Ensure we can open a new LDB after a fork() via f891b8d ldb: Add tests for ldb_tdb use after a fork() via 2136664 ldb_tdb: Allow use of a TDB for ldb_tdb after as fork() via 3b06915 ldb: Reset errno before checking it in ltdb_connect() via daf79e5 ldb/tests: add tests for transaction_{start,commit}/lock_read across forks via 1174b52 ldb_tdb: Prevent ldb_tdb reuse after a fork() from 4e78aee s3: VFS: Fix memory leak in vfs_ceph.
https://git.samba.org/?p=samba.git;a=shortlog;h=master - Log ----------------------------------------------------------------- commit ba33d90ed67b787a11b26bc69f9553d0d52dfe45 Author: Andrew Bartlett <abart...@samba.org> Date: Wed May 9 12:53:53 2018 +1200 ldb: Ensure we can open a new LDB after a fork() Based on work for an mdb-specific test by Gary Lockyer Signed-off-by: Andrew Bartlett <abart...@samba.org> Reviewed-by: Garming Sam <garm...@catalyst.net.nz> Autobuild-User(master): Andrew Bartlett <abart...@samba.org> Autobuild-Date(master): Wed May 9 07:27:24 CEST 2018 on sn-devel-144 commit f891b8dc32d24eea81ee5fe6ace02c1cf7129443 Author: Andrew Bartlett <abart...@samba.org> Date: Mon May 7 12:59:00 2018 +1200 ldb: Add tests for ldb_tdb use after a fork() We need to show that despite the internal cache of TDB pointers that it is safe to open a ldb_tdb after a fork() Signed-off-by: Andrew Bartlett <abart...@samba.org> Reviewed-by: Garming Sam <garm...@catalyst.net.nz> commit 21366649410c29904a463b57e7d8688ce6e11381 Author: Andrew Bartlett <abart...@samba.org> Date: Fri May 4 22:22:26 2018 +1200 ldb_tdb: Allow use of a TDB for ldb_tdb after as fork() Otherwise we rely on the caller doing tdb_reopen_all() which should not be their job. Signed-off-by: Andrew Bartlett <abart...@samba.org> Reviewed-by: Garming Sam <garm...@catalyst.net.nz> commit 3b069156639e4d53f8bd392fe6a21c36ff0caa99 Author: Andrew Bartlett <abart...@samba.org> Date: Mon May 7 12:59:49 2018 +1200 ldb: Reset errno before checking it in ltdb_connect() Signed-off-by: Andrew Bartlett <abart...@samba.org> Reviewed-by: Garming Sam <garm...@catalyst.net.nz> commit daf79e5b35b354200b26b2b0fac3487287ffa720 Author: Gary Lockyer <g...@catalyst.net.nz> Date: Wed May 9 11:02:41 2018 +1200 ldb/tests: add tests for transaction_{start,commit}/lock_read across forks (Split from a larger commit by Andrew Bartlett) Signed-off-by: Gary Lockyer <g...@catalyst.net.nz> Signed-off-by: Andrew Bartlett <abart...@samba.org> Reviewed-by: Garming Sam <garm...@catalyst.net.nz> commit 1174b52b91de045b5c111dff97c49488ac963bfa Author: Andrew Bartlett <abart...@samba.org> Date: Fri May 4 14:35:14 2018 +1200 ldb_tdb: Prevent ldb_tdb reuse after a fork() We may relax this restriction in the future, but for now do not assume that the caller has done a tdb_reopen_all() at the right time. Signed-off-by: Andrew Bartlett <abart...@samba.org> Reviewed-by: Garming Sam <garm...@catalyst.net.nz> ----------------------------------------------------------------------- Summary of changes: lib/ldb/ldb_tdb/ldb_tdb.c | 112 +++++++++++- lib/ldb/ldb_tdb/ldb_tdb.h | 7 + lib/ldb/ldb_tdb/ldb_tdb_wrap.c | 18 ++ lib/ldb/tests/ldb_mod_op_test.c | 280 +++++++++++++++++++++++++++++ lib/ldb/tests/ldb_tdb_test.c | 387 ++++++++++++++++++++++++++++++++++++++++ lib/ldb/wscript | 8 +- 6 files changed, 802 insertions(+), 10 deletions(-) create mode 100644 lib/ldb/tests/ldb_tdb_test.c Changeset truncated at 500 lines: diff --git a/lib/ldb/ldb_tdb/ldb_tdb.c b/lib/ldb/ldb_tdb/ldb_tdb.c index 0833a4f..f9bc35c 100644 --- a/lib/ldb/ldb_tdb/ldb_tdb.c +++ b/lib/ldb/ldb_tdb/ldb_tdb.c @@ -100,6 +100,17 @@ static int ltdb_lock_read(struct ldb_module *module) struct ltdb_private *ltdb = talloc_get_type(data, struct ltdb_private); int tdb_ret = 0; int ret; + pid_t pid = getpid(); + + if (ltdb->pid != pid) { + ldb_asprintf_errstring( + ldb_module_get_ctx(module), + __location__": Reusing ldb opend by pid %d in " + "process %d\n", + ltdb->pid, + pid); + return LDB_ERR_PROTOCOL_ERROR; + } if (tdb_transaction_active(ltdb->tdb) == false && ltdb->read_lock_count == 0) { @@ -128,6 +139,17 @@ static int ltdb_unlock_read(struct ldb_module *module) { void *data = ldb_module_get_private(module); struct ltdb_private *ltdb = talloc_get_type(data, struct ltdb_private); + pid_t pid = getpid(); + + if (ltdb->pid != pid) { + ldb_asprintf_errstring( + ldb_module_get_ctx(module), + __location__": Reusing ldb opend by pid %d in " + "process %d\n", + ltdb->pid, + pid); + return LDB_ERR_PROTOCOL_ERROR; + } if (!tdb_transaction_active(ltdb->tdb) && ltdb->read_lock_count == 1) { tdb_unlockall_read(ltdb->tdb); ltdb->read_lock_count--; @@ -1447,21 +1469,69 @@ static int ltdb_rename(struct ltdb_context *ctx) static int ltdb_tdb_transaction_start(struct ltdb_private *ltdb) { + pid_t pid = getpid(); + + if (ltdb->pid != pid) { + ldb_asprintf_errstring( + ldb_module_get_ctx(ltdb->module), + __location__": Reusing ldb opend by pid %d in " + "process %d\n", + ltdb->pid, + pid); + return LDB_ERR_PROTOCOL_ERROR; + } + return tdb_transaction_start(ltdb->tdb); } static int ltdb_tdb_transaction_cancel(struct ltdb_private *ltdb) { + pid_t pid = getpid(); + + if (ltdb->pid != pid) { + ldb_asprintf_errstring( + ldb_module_get_ctx(ltdb->module), + __location__": Reusing ldb opend by pid %d in " + "process %d\n", + ltdb->pid, + pid); + return LDB_ERR_PROTOCOL_ERROR; + } + return tdb_transaction_cancel(ltdb->tdb); } static int ltdb_tdb_transaction_prepare_commit(struct ltdb_private *ltdb) { + pid_t pid = getpid(); + + if (ltdb->pid != pid) { + ldb_asprintf_errstring( + ldb_module_get_ctx(ltdb->module), + __location__": Reusing ldb opend by pid %d in " + "process %d\n", + ltdb->pid, + pid); + return LDB_ERR_PROTOCOL_ERROR; + } + return tdb_transaction_prepare_commit(ltdb->tdb); } static int ltdb_tdb_transaction_commit(struct ltdb_private *ltdb) { + pid_t pid = getpid(); + + if (ltdb->pid != pid) { + ldb_asprintf_errstring( + ldb_module_get_ctx(ltdb->module), + __location__": Reusing ldb opend by pid %d in " + "process %d\n", + ltdb->pid, + pid); + return LDB_ERR_PROTOCOL_ERROR; + } + return tdb_transaction_commit(ltdb->tdb); } @@ -1470,6 +1540,18 @@ static int ltdb_start_trans(struct ldb_module *module) void *data = ldb_module_get_private(module); struct ltdb_private *ltdb = talloc_get_type(data, struct ltdb_private); + pid_t pid = getpid(); + + if (ltdb->pid != pid) { + ldb_asprintf_errstring( + ldb_module_get_ctx(ltdb->module), + __location__": Reusing ldb opend by pid %d in " + "process %d\n", + ltdb->pid, + pid); + return LDB_ERR_PROTOCOL_ERROR; + } + /* Do not take out the transaction lock on a read-only DB */ if (ltdb->read_only) { return LDB_ERR_UNWILLING_TO_PERFORM; @@ -1498,6 +1580,17 @@ static int ltdb_prepare_commit(struct ldb_module *module) int ret; void *data = ldb_module_get_private(module); struct ltdb_private *ltdb = talloc_get_type(data, struct ltdb_private); + pid_t pid = getpid(); + + if (ltdb->pid != pid) { + ldb_asprintf_errstring( + ldb_module_get_ctx(module), + __location__": Reusing ldb opend by pid %d in " + "process %d\n", + ltdb->pid, + pid); + return LDB_ERR_PROTOCOL_ERROR; + } if (!ltdb->kv_ops->transaction_active(ltdb)) { ldb_set_errstring(ldb_module_get_ctx(module), @@ -2138,8 +2231,6 @@ int init_store(struct ltdb_private *ltdb, const char *options[], struct ldb_module **_module) { - struct ldb_module *module; - if (getenv("LDB_WARN_UNINDEXED")) { ltdb->warn_unindexed = true; } @@ -2150,23 +2241,25 @@ int init_store(struct ltdb_private *ltdb, ltdb->sequence_number = 0; - module = ldb_module_new(ldb, ldb, name, <db_ops); - if (!module) { + ltdb->pid = getpid(); + + ltdb->module = ldb_module_new(ldb, ldb, name, <db_ops); + if (!ltdb->module) { ldb_oom(ldb); talloc_free(ltdb); return LDB_ERR_OPERATIONS_ERROR; } - ldb_module_set_private(module, ltdb); - talloc_steal(module, ltdb); + ldb_module_set_private(ltdb->module, ltdb); + talloc_steal(ltdb->module, ltdb); - if (ltdb_cache_load(module) != 0) { + if (ltdb_cache_load(ltdb->module) != 0) { ldb_asprintf_errstring(ldb, "Unable to load ltdb cache " "records for backend '%s'", name); - talloc_free(module); + talloc_free(ltdb->module); return LDB_ERR_OPERATIONS_ERROR; } - *_module = module; + *_module = ltdb->module; /* * Set or override the maximum key length * @@ -2262,6 +2355,7 @@ int ltdb_connect(struct ldb_context *ldb, const char *url, ltdb->kv_ops = &key_value_ops; + errno = 0; /* note that we use quite a large default hash size */ ltdb->tdb = ltdb_wrap_open(ltdb, path, 10000, tdb_flags, open_flags, diff --git a/lib/ldb/ldb_tdb/ldb_tdb.h b/lib/ldb/ldb_tdb/ldb_tdb.h index 4d53120..caaa67a 100644 --- a/lib/ldb/ldb_tdb/ldb_tdb.h +++ b/lib/ldb/ldb_tdb/ldb_tdb.h @@ -36,6 +36,7 @@ struct kv_db_ops { ldb_context */ struct ltdb_private { const struct kv_db_ops *kv_ops; + struct ldb_module *module; TDB_CONTEXT *tdb; unsigned int connect_flags; @@ -75,6 +76,12 @@ struct ltdb_private { * greater than this length will be rejected. */ unsigned max_key_length; + + /* + * The PID that opened this database so we don't work in a + * fork()ed child. + */ + pid_t pid; }; struct ltdb_context { diff --git a/lib/ldb/ldb_tdb/ldb_tdb_wrap.c b/lib/ldb/ldb_tdb/ldb_tdb_wrap.c index eb16809..bc702a2 100644 --- a/lib/ldb/ldb_tdb/ldb_tdb_wrap.c +++ b/lib/ldb/ldb_tdb/ldb_tdb_wrap.c @@ -74,6 +74,7 @@ struct ltdb_wrap { struct tdb_context *tdb; dev_t device; ino_t inode; + pid_t pid; }; static struct ltdb_wrap *tdb_list; @@ -105,9 +106,25 @@ struct tdb_context *ltdb_wrap_open(TALLOC_CTX *mem_ctx, if (stat(path, &st) == 0) { for (w=tdb_list;w;w=w->next) { if (st.st_dev == w->device && st.st_ino == w->inode) { + pid_t pid = getpid(); + int ret; if (!talloc_reference(mem_ctx, w)) { return NULL; } + if (w->pid != pid) { + ret = tdb_reopen(w->tdb); + if (ret != 0) { + /* + * Avoid use-after-free: + * on fail the TDB + * is closed! + */ + DLIST_REMOVE(tdb_list, + w); + return NULL; + } + w->pid = pid; + } return w->tdb; } } @@ -135,6 +152,7 @@ struct tdb_context *ltdb_wrap_open(TALLOC_CTX *mem_ctx, w->device = st.st_dev; w->inode = st.st_ino; + w->pid = getpid(); talloc_set_destructor(w, ltdb_wrap_destructor); diff --git a/lib/ldb/tests/ldb_mod_op_test.c b/lib/ldb/tests/ldb_mod_op_test.c index 1340f5e..67ac024 100644 --- a/lib/ldb/tests/ldb_mod_op_test.c +++ b/lib/ldb/tests/ldb_mod_op_test.c @@ -3820,6 +3820,270 @@ static void test_ldb_talloc_destructor_transaction_cleanup(void **state) } } +static void test_transaction_start_across_fork(void **state) +{ + struct ldb_context *ldb1 = NULL; + int ret; + struct ldbtest_ctx *test_ctx = NULL; + int pipes[2]; + char buf[2]; + int wstatus; + pid_t pid, child_pid; + + test_ctx = talloc_get_type_abort(*state, struct ldbtest_ctx); + + /* + * Open the database + */ + ldb1 = ldb_init(test_ctx, test_ctx->ev); + ret = ldb_connect(ldb1, test_ctx->dbpath, 0, NULL); + assert_int_equal(ret, 0); + + ret = pipe(pipes); + assert_int_equal(ret, 0); + + child_pid = fork(); + if (child_pid == 0) { + close(pipes[0]); + ret = ldb_transaction_start(ldb1); + if (ret != LDB_ERR_PROTOCOL_ERROR) { + print_error(__location__": ldb_transaction_start " + "returned (%d) %s\n", + ret, + ldb1->err_string); + exit(LDB_ERR_OTHER); + } + + ret = write(pipes[1], "GO", 2); + if (ret != 2) { + print_error(__location__ + " write returned (%d)", + ret); + exit(LDB_ERR_OPERATIONS_ERROR); + } + exit(LDB_SUCCESS); + } + close(pipes[1]); + ret = read(pipes[0], buf, 2); + assert_int_equal(ret, 2); + + pid = waitpid(child_pid, &wstatus, 0); + assert_int_equal(pid, child_pid); + + assert_true(WIFEXITED(wstatus)); + + assert_int_equal(WEXITSTATUS(wstatus), 0); +} + +static void test_transaction_commit_across_fork(void **state) +{ + struct ldb_context *ldb1 = NULL; + int ret; + struct ldbtest_ctx *test_ctx = NULL; + int pipes[2]; + char buf[2]; + int wstatus; + pid_t pid, child_pid; + + test_ctx = talloc_get_type_abort(*state, struct ldbtest_ctx); + + /* + * Open the database + */ + ldb1 = ldb_init(test_ctx, test_ctx->ev); + ret = ldb_connect(ldb1, test_ctx->dbpath, 0, NULL); + assert_int_equal(ret, 0); + + ret = ldb_transaction_start(ldb1); + assert_int_equal(ret, 0); + + ret = pipe(pipes); + assert_int_equal(ret, 0); + + child_pid = fork(); + if (child_pid == 0) { + close(pipes[0]); + ret = ldb_transaction_commit(ldb1); + + if (ret != LDB_ERR_PROTOCOL_ERROR) { + print_error(__location__": ldb_transaction_commit " + "returned (%d) %s\n", + ret, + ldb1->err_string); + exit(LDB_ERR_OTHER); + } + + ret = write(pipes[1], "GO", 2); + if (ret != 2) { + print_error(__location__ + " write returned (%d)", + ret); + exit(LDB_ERR_OPERATIONS_ERROR); + } + exit(LDB_SUCCESS); + } + close(pipes[1]); + ret = read(pipes[0], buf, 2); + assert_int_equal(ret, 2); + + pid = waitpid(child_pid, &wstatus, 0); + assert_int_equal(pid, child_pid); + + assert_true(WIFEXITED(wstatus)); + + assert_int_equal(WEXITSTATUS(wstatus), 0); +} + +static void test_lock_read_across_fork(void **state) +{ + struct ldb_context *ldb1 = NULL; + int ret; + struct ldbtest_ctx *test_ctx = NULL; + int pipes[2]; + char buf[2]; + int wstatus; + pid_t pid, child_pid; + + test_ctx = talloc_get_type_abort(*state, struct ldbtest_ctx); + + /* + * Open the database + */ + ldb1 = ldb_init(test_ctx, test_ctx->ev); + ret = ldb_connect(ldb1, test_ctx->dbpath, 0, NULL); + assert_int_equal(ret, 0); + + ret = pipe(pipes); + assert_int_equal(ret, 0); + + child_pid = fork(); + if (child_pid == 0) { + struct ldb_dn *basedn; + struct ldb_result *result = NULL; + + close(pipes[0]); + + basedn = ldb_dn_new_fmt(test_ctx, test_ctx->ldb, "dc=test"); + assert_non_null(basedn); + + ret = ldb_search(test_ctx->ldb, + test_ctx, + &result, + basedn, + LDB_SCOPE_BASE, + NULL, + NULL); + if (ret != LDB_ERR_PROTOCOL_ERROR) { + print_error(__location__": ldb_search " + "returned (%d) %s\n", + ret, + ldb1->err_string); + exit(LDB_ERR_OTHER); + } + + ret = write(pipes[1], "GO", 2); + if (ret != 2) { + print_error(__location__ + " write returned (%d)", + ret); + exit(LDB_ERR_OPERATIONS_ERROR); + } + exit(LDB_SUCCESS); + } + close(pipes[1]); + ret = read(pipes[0], buf, 2); + assert_int_equal(ret, 2); + + pid = waitpid(child_pid, &wstatus, 0); + assert_int_equal(pid, child_pid); + + assert_true(WIFEXITED(wstatus)); + + assert_int_equal(WEXITSTATUS(wstatus), 0); + + { + /* + * Ensure that the search actually succeeds on the opening + * pid + */ + struct ldb_dn *basedn; + struct ldb_result *result = NULL; + + close(pipes[0]); + + basedn = ldb_dn_new_fmt(test_ctx, test_ctx->ldb, "dc=test"); + assert_non_null(basedn); + + ret = ldb_search(test_ctx->ldb, + test_ctx, + &result, + basedn, + LDB_SCOPE_BASE, + NULL, + NULL); + assert_int_equal(0, ret); + } +} + +static void test_multiple_opens_across_fork(void **state) +{ + struct ldb_context *ldb1 = NULL; + struct ldb_context *ldb2 = NULL; + int ret; + struct ldbtest_ctx *test_ctx = NULL; + int pipes[2]; + char buf[2]; + int wstatus; + pid_t pid, child_pid; + + test_ctx = talloc_get_type_abort(*state, struct ldbtest_ctx); + + /* + * Open the database again + */ + ldb1 = ldb_init(test_ctx, test_ctx->ev); + ret = ldb_connect(ldb1, test_ctx->dbpath, LDB_FLG_RDONLY, NULL); + assert_int_equal(ret, 0); -- Samba Shared Repository