Author: stefan2
Date: Mon Jun 13 18:22:42 2011
New Revision: 1135210
URL: http://svn.apache.org/viewvc?rev=1135210&view=rev
Log:
Fuse read_long_string and read_string functionality in an attempt to
increase test code overage. Keep the overhead small for short strings.
* subversion/libsvn_ra_svn/marshal.c
(read_long_string): drop
(read_string): handle 32 bit overflows; limit initial string buffer size
Patch by: julianfoad
(with modifications by me)
Modified:
subversion/trunk/subversion/libsvn_ra_svn/marshal.c
Modified: subversion/trunk/subversion/libsvn_ra_svn/marshal.c
URL:
http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_ra_svn/marshal.c?rev=1135210&r1=1135209&r2=1135210&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_ra_svn/marshal.c (original)
+++ subversion/trunk/subversion/libsvn_ra_svn/marshal.c Mon Jun 13 18:22:42 2011
@@ -560,82 +560,65 @@ svn_error_t *svn_ra_svn_write_tuple(svn_
/* --- READING DATA ITEMS --- */
-/* Read LEN bytes from CONN into a supposedly empty STRINGBUF.
- * POOL will be used for temporary allocations. */
-static svn_error_t *
-read_long_string(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
- svn_stringbuf_t *stringbuf, apr_uint64_t len)
+/* Read LEN bytes from CONN into already-allocated structure ITEM.
+ * Afterwards, *ITEM is of type 'SVN_RA_SVN_STRING', and its string
+ * data is allocated in POOL. */
+static svn_error_t *read_string(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
+ svn_ra_svn_item_t *item, apr_uint64_t len64)
{
- char readbuf[4096];
+ svn_stringbuf_t *stringbuf;
+ apr_size_t len = (apr_size_t)len64;
apr_size_t readbuf_len;
+ char *dest;
/* We can't store strings longer than the maximum size of apr_size_t,
* so check for wrapping */
- if (((apr_size_t) len) < len)
+ if (len64 > APR_SIZE_MAX)
return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
_("String length larger than maximum"));
- while (len)
+ /* Read the string in chunks. The chunk size is large enough to avoid
+ * re-allocation in typical cases, and small enough to ensure we do not
+ * pre-allocate an unreasonable amount of memory if (perhaps due to
+ * network data corruption or a DOS attack), we receive a bogus claim that
+ * a very long string is going to follow. In that case, we start small
+ * and wait for all that data to actually show up. This does not fully
+ * prevent DOS attacks but makes them harder (you have to actually send
+ * gigabytes of data). */
+ readbuf_len = len < SUSPICIOUSLY_HUGE_STRING_SIZE_THRESHOLD
+ ? len
+ : SUSPICIOUSLY_HUGE_STRING_SIZE_THRESHOLD;
+ stringbuf = svn_stringbuf_create_ensure(readbuf_len, pool);
+ dest = stringbuf->data;
+
+ /* Read remaining string data directly into the string structure.
+ * Do it iteratively, if necessary. */
+ while (readbuf_len)
{
- readbuf_len = len > sizeof(readbuf) ? sizeof(readbuf) : (apr_size_t)len;
+ SVN_ERR(readbuf_read(conn, pool, dest, readbuf_len));
- SVN_ERR(readbuf_read(conn, pool, readbuf, readbuf_len));
- /* Read into a stringbuf_t to so we don't allow the sender to allocate
- * an arbitrary amount of memory without actually sending us that much
- * data */
- svn_stringbuf_appendbytes(stringbuf, readbuf, readbuf_len);
+ stringbuf->len += readbuf_len;
len -= readbuf_len;
- }
- return SVN_NO_ERROR;
-}
+ /* Early exit. In most cases, strings can be read in the first
+ * iteration. */
+ if (len == 0)
+ break;
-/* Read LEN bytes from CONN into already-allocated structure ITEM.
- * Afterwards, *ITEM is of type 'SVN_RA_SVN_STRING', and its string
- * data is allocated in POOL. */
-static svn_error_t *read_string(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
- svn_ra_svn_item_t *item, apr_uint64_t len)
-{
- svn_stringbuf_t *stringbuf;
+ /* Prepare next iteration: determine length of chunk to read
+ * and re-alloc the string buffer. */
+ readbuf_len
+ = len < SUSPICIOUSLY_HUGE_STRING_SIZE_THRESHOLD
+ ? len
+ : SUSPICIOUSLY_HUGE_STRING_SIZE_THRESHOLD;
- /* We should not use large strings in our protocol. However, we may
- * receive a claim that a very long string is going to follow. In that
- * case, we start small and wait for all that data to actually show up.
- * This does not fully prevent DOS attacs but makes them harder (you
- * have to actually send gigabytes of data).
- */
- if (len > SUSPICIOUSLY_HUGE_STRING_SIZE_THRESHOLD)
- {
- /* This string might take a large amount of memory. Don't allocate
- * the whole buffer at once, so to prevent OOM issues by corrupted
- * network data.
- */
- stringbuf = svn_stringbuf_create("", pool);
- SVN_ERR(read_long_string(conn, pool, stringbuf, len));
- }
- else
- {
- /* This is a reasonably sized string. So, provide a buffer large
- * enough to prevent re-allocation as long as the data transmission
- * is not flawed.
- */
- stringbuf = svn_stringbuf_create_ensure((apr_size_t)len, pool);
-
- /* Read the string data directly into the string structure.
- * Do it iteratively, if necessary.
- */
- while (len)
- {
- apr_size_t readbuf_len = (apr_size_t)len;
- char *dest = stringbuf->data + stringbuf->len;
- SVN_ERR(readbuf_read(conn, pool, dest, readbuf_len));
-
- stringbuf->len += readbuf_len;
- stringbuf->data[stringbuf->len] = '\0';
- len -= readbuf_len;
- }
+ svn_stringbuf_ensure(stringbuf, stringbuf->len + readbuf_len + 1);
+ dest = stringbuf->data + stringbuf->len;
}
+ /* zero-terminate the string */
+ stringbuf->data[stringbuf->len] = '\0';
+
/* Return the string properly wrapped into an RA_SVN item. */
item->kind = SVN_RA_SVN_STRING;
item->u.string = svn_stringbuf__morph_into_string(stringbuf);