diff options
author | rpluem <rpluem@13f79535-47bb-0310-9956-ffa450edef68> | 2012-10-29 10:55:52 +0000 |
---|---|---|
committer | rpluem <rpluem@13f79535-47bb-0310-9956-ffa450edef68> | 2012-10-29 10:55:52 +0000 |
commit | c9b158c0603268b9e1701f4f710cbac09d9b6f4a (patch) | |
tree | 6dad75c6dc7a75691deb00908ef4ec45e981b1a7 | |
parent | fd48503909477f5f9ab5c8aa82a5229f1f48be60 (diff) | |
download | libapr-util-c9b158c0603268b9e1701f4f710cbac09d9b6f4a.tar.gz |
Merge r1402897 from trunk:
Fix potential data corruption in apr_brigade_write() and friends if
the last bucket of the brigade is a heap bucket that has been split,
and there are still references to the next part of the original bucket
in use.
Submitted by: sf
Reviewed by: rpluem
git-svn-id: http://svn.apache.org/repos/asf/apr/apr-util/branches/1.3.x@1403250 13f79535-47bb-0310-9956-ffa450edef68
-rw-r--r-- | CHANGES | 5 | ||||
-rw-r--r-- | buckets/apr_brigade.c | 12 | ||||
-rw-r--r-- | test/testbuckets.c | 20 |
3 files changed, 34 insertions, 3 deletions
@@ -1,6 +1,11 @@ -*- coding: utf-8 -*- Changes with APR-util 1.3.13 + *) Fix potential data corruption in apr_brigade_write() and friends if + the last bucket of the brigade is a heap bucket that has been split, + and there are still references to the next part of the original bucket + in use. [Stefan Fritsch] + *) Remove duplicated logic in apr_brigade_puts(). PR 53740. [Christophe Jaillet <christophe jaillet wanadoo fr>] diff --git a/buckets/apr_brigade.c b/buckets/apr_brigade.c index 18c8f125..01e25c68 100644 --- a/buckets/apr_brigade.c +++ b/buckets/apr_brigade.c @@ -422,7 +422,12 @@ APU_DECLARE(apr_status_t) apr_brigade_write(apr_bucket_brigade *b, apr_size_t remaining = APR_BUCKET_BUFF_SIZE; char *buf = NULL; - if (!APR_BRIGADE_EMPTY(b) && APR_BUCKET_IS_HEAP(e)) { + /* + * If the last bucket is a heap bucket and its buffer is not shared with + * another bucket, we may write into that bucket. + */ + if (!APR_BRIGADE_EMPTY(b) && APR_BUCKET_IS_HEAP(e) + && ((apr_bucket_heap *)(e->data))->refcount.refcount == 1) { apr_bucket_heap *h = e->data; /* HEAP bucket start offsets are always in-memory, safe to cast */ @@ -512,10 +517,11 @@ APU_DECLARE(apr_status_t) apr_brigade_writev(apr_bucket_brigade *b, i = 0; /* If there is a heap bucket at the end of the brigade - * already, copy into the existing bucket. + * already, and its refcount is 1, copy into the existing bucket. */ e = APR_BRIGADE_LAST(b); - if (!APR_BRIGADE_EMPTY(b) && APR_BUCKET_IS_HEAP(e)) { + if (!APR_BRIGADE_EMPTY(b) && APR_BUCKET_IS_HEAP(e) + && ((apr_bucket_heap *)(e->data))->refcount.refcount == 1) { apr_bucket_heap *h = e->data; apr_size_t remaining = h->alloc_len - (e->length + (apr_size_t)e->start); diff --git a/test/testbuckets.c b/test/testbuckets.c index 6c3653ff..3a5df24d 100644 --- a/test/testbuckets.c +++ b/test/testbuckets.c @@ -469,6 +469,25 @@ static void test_partition(abts_case *tc, void *data) apr_bucket_alloc_destroy(ba); } +static void test_write_split(abts_case *tc, void *data) +{ + apr_bucket_alloc_t *ba = apr_bucket_alloc_create(p); + apr_bucket_brigade *bb1 = apr_brigade_create(p, ba); + apr_bucket_brigade *bb2; + apr_bucket *e; + + e = apr_bucket_heap_create(hello, strlen(hello), NULL, ba); + APR_BRIGADE_INSERT_HEAD(bb1, e); + apr_bucket_split(e, strlen("hello, ")); + bb2 = apr_brigade_split(bb1, APR_BRIGADE_LAST(bb1)); + apr_brigade_write(bb1, NULL, NULL, "foo", strlen("foo")); + test_bucket_content(tc, APR_BRIGADE_FIRST(bb2), "world", 5); + + apr_brigade_destroy(bb1); + apr_brigade_destroy(bb2); + apr_bucket_alloc_destroy(ba); +} + abts_suite *testbuckets(abts_suite *suite) { suite = ADD_SUITE(suite); @@ -484,6 +503,7 @@ abts_suite *testbuckets(abts_suite *suite) abts_run_test(suite, test_manyfile, NULL); abts_run_test(suite, test_truncfile, NULL); abts_run_test(suite, test_partition, NULL); + abts_run_test(suite, test_write_split, NULL); return suite; } |