summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorrpluem <rpluem@13f79535-47bb-0310-9956-ffa450edef68>2012-10-29 10:55:52 +0000
committerrpluem <rpluem@13f79535-47bb-0310-9956-ffa450edef68>2012-10-29 10:55:52 +0000
commitc9b158c0603268b9e1701f4f710cbac09d9b6f4a (patch)
tree6dad75c6dc7a75691deb00908ef4ec45e981b1a7
parentfd48503909477f5f9ab5c8aa82a5229f1f48be60 (diff)
downloadlibapr-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--CHANGES5
-rw-r--r--buckets/apr_brigade.c12
-rw-r--r--test/testbuckets.c20
3 files changed, 34 insertions, 3 deletions
diff --git a/CHANGES b/CHANGES
index f9da59dc..0a293d1c 100644
--- a/CHANGES
+++ b/CHANGES
@@ -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;
}