[PATCH 1/2] tdb: don't corrupt database if we go overlength due to transaction expand.
Rusty Russell
rusty at rustcorp.com.au
Tue May 28 19:03:16 MDT 2013
Volker Lendecke <Volker.Lendecke at SerNet.DE> writes:
> Hi, Rusty!
>
> On Tue, May 28, 2013 at 10:18:37AM +0200, Volker Lendecke wrote:
>> Ignore me. Sorry for the noise.
>
> Attached find a different patchset where Stefan and myself
> went through the tdb_expand routine and (as we believe)
> fixed some more overflow conditions. I need to take a
> further look at the transaction stuff, but I do not want
> this to get lost.
Yes, thanks for the more thorough look. Comments inline:
> From 8313a101525cb089017dc9dad968b8f696a07065 Mon Sep 17 00:00:00 2001
> From: Stefan Metzmacher <metze at samba.org>
> Date: Tue, 28 May 2013 12:56:57 +0200
> Subject: [PATCH 1/5] tdb: add a 'new_size' helper variable to tdb_expand_file()
>
> Pair-Programmed-With: Volker Lendecke <vl at samba.org>
>
> Signed-off-by: Stefan Metzmacher <metze at samba.org>
> Signed-off-by: Volker Lendecke <vl at samba.org>
> ---
> lib/tdb/common/io.c | 11 +++++++----
> 1 files changed, 7 insertions(+), 4 deletions(-)
Pretty trivial, but ok.
Reviewed-by: Rusty Russell <rusty at rustcorp.com.au>
>
> diff --git a/lib/tdb/common/io.c b/lib/tdb/common/io.c
> index a477fb5..44ef728 100644
> --- a/lib/tdb/common/io.c
> +++ b/lib/tdb/common/io.c
> @@ -287,18 +287,21 @@ int tdb_mmap(struct tdb_context *tdb)
> static int tdb_expand_file(struct tdb_context *tdb, tdb_off_t size, tdb_off_t addition)
> {
> char buf[8192];
> + tdb_off_t new_size;
>
> if (tdb->read_only || tdb->traverse_read) {
> tdb->ecode = TDB_ERR_RDONLY;
> return -1;
> }
>
> - if (ftruncate(tdb->fd, size+addition) == -1) {
> + new_size = size + addition;
> +
> + if (ftruncate(tdb->fd, new_size) == -1) {
> char b = 0;
> - ssize_t written = pwrite(tdb->fd, &b, 1, (size+addition) - 1);
> + ssize_t written = pwrite(tdb->fd, &b, 1, new_size - 1);
> if (written == 0) {
> /* try once more, potentially revealing errno */
> - written = pwrite(tdb->fd, &b, 1, (size+addition) - 1);
> + written = pwrite(tdb->fd, &b, 1, new_size - 1);
> }
> if (written == 0) {
> /* again - give up, guessing errno */
> @@ -306,7 +309,7 @@ static int tdb_expand_file(struct tdb_context *tdb, tdb_off_t size, tdb_off_t ad
> }
> if (written != 1) {
> TDB_LOG((tdb, TDB_DEBUG_FATAL, "expand_file to %u failed (%s)\n",
> - size+addition, strerror(errno)));
> + (unsigned)new_size, strerror(errno)));
> return -1;
> }
> }
> --
> 1.7.3.4
>
>
> From 73de3aa3fc1ff7698e063de738e3f8df81560b0a Mon Sep 17 00:00:00 2001
> From: Stefan Metzmacher <metze at samba.org>
> Date: Tue, 28 May 2013 12:59:32 +0200
> Subject: [PATCH 2/5] tdb: add overflow/ENOSPC handling to tdb_expand_file()
>
> Pair-Programmed-With: Volker Lendecke <vl at samba.org>
>
> Signed-off-by: Stefan Metzmacher <metze at samba.org>
> Signed-off-by: Volker Lendecke <vl at samba.org>
> ---
> lib/tdb/common/io.c | 12 ++++++++++++
> 1 files changed, 12 insertions(+), 0 deletions(-)
>
> diff --git a/lib/tdb/common/io.c b/lib/tdb/common/io.c
> index 44ef728..53f7030 100644
> --- a/lib/tdb/common/io.c
> +++ b/lib/tdb/common/io.c
> @@ -295,6 +295,15 @@ static int tdb_expand_file(struct tdb_context *tdb, tdb_off_t size, tdb_off_t ad
> }
>
> new_size = size + addition;
> + if ((new_size < size) || (new_size < addition)) {
> + /* overflow */
> + tdb->ecode = TDB_ERR_OOM;
> + TDB_LOG((tdb, TDB_DEBUG_FATAL, "expand_file write "
> + "overflow detected current size[%u] addition[%u]!\n",
> + (unsigned)size, (unsigned)addition));
> + errno = ENOSPC;
> + return -1;
> + }
This is nice, too.
Reviewed-by: Rusty Russell <rusty at rustcorp.com.au>
>
> if (ftruncate(tdb->fd, new_size) == -1) {
> char b = 0;
> @@ -308,6 +317,7 @@ static int tdb_expand_file(struct tdb_context *tdb, tdb_off_t size, tdb_off_t ad
> errno = ENOSPC;
> }
> if (written != 1) {
> + tdb->ecode = TDB_ERR_OOM;
> TDB_LOG((tdb, TDB_DEBUG_FATAL, "expand_file to %u failed (%s)\n",
> (unsigned)new_size, strerror(errno)));
> return -1;
> @@ -327,12 +337,14 @@ static int tdb_expand_file(struct tdb_context *tdb, tdb_off_t size, tdb_off_t ad
> }
> if (written == 0) {
> /* give up, trying to provide a useful errno */
> + tdb->ecode = TDB_ERR_OOM;
> TDB_LOG((tdb, TDB_DEBUG_FATAL, "expand_file write "
> "returned 0 twice: giving up!\n"));
> errno = ENOSPC;
> return -1;
> }
> if (written == -1) {
> + tdb->ecode = TDB_ERR_OOM;
> TDB_LOG((tdb, TDB_DEBUG_FATAL, "expand_file write of "
> "%u bytes failed (%s)\n", (int)n,
> strerror(errno)));
> --
> 1.7.3.4
>
>
> From 5e1bc63b9444e730c9c17b4a4d3e47e835c2d92e Mon Sep 17 00:00:00 2001
> From: Stefan Metzmacher <metze at samba.org>
> Date: Tue, 28 May 2013 13:01:27 +0200
> Subject: [PATCH 3/5] tdb: add overflow detection to tdb_expand_adjust()
>
> We round up at maximun to a new size of 4GB,
> but still return at least the given size.
I think I prefer a bool return here when we fail. That has the nice
property of doing our overflow checking in one place.
Hmm, not quite: tdb_recovery_size() could theoretically overflow. So
could the addition of sizeof(rec). I think a bool tdb_overflow() helper
might be a good idea:
bool tdb_overflow(tdb_off_t *ret, tdb_off_t a, tdb_off_t b)
{
if (a + b < a)
return false;
*ret = a + b;
return true;
}
We can then make tdb_recovery_size() return a bool, too...
Cheers,
Rusty,
More information about the samba-technical
mailing list