[svn.haxx.se] · SVN Dev · SVN Users · SVN Org · TSVN Dev · TSVN Users · Subclipse Dev · Subclipse Users · this month's index

Re: Buffer overflow in apr_brigade_vprintf() ?

From: Jeff Trawick <trawick_at_gmail.com>
Date: Fri, 24 Apr 2009 16:48:27 -0400

On Fri, Apr 24, 2009 at 4:10 PM, C. Michael Pilato <cmpilato_at_collab.net>wrote:

> [Please Cc: me in responses -- I think I still have APR commit privs, but
> I'm not active here and not subscribed to the mailing lists.]
>
> In the past couple of weeks, I've seen two different reports of what
> appears
> to be corruption in the stream of data transmitted by Subversion's
> mod_dav_svn through Apache and back to the Subversion client. What is seen
> client-side is an opening XML tag, a truncated bit of CDATA "inside" the
> tag, and then a missing XML closing tag. The problem seems to occur with
> magically sized chunks of data, so it can be hard to reproduce[1].
>
> Here are the relevant pieces of the call stack:
>
> mod_dav_svn/reports/replay.c's change_file_or_dir_prop() function contains
> the following (which is base64-encoding Subversion file and directory
> properties, and tossing them into an XML REPORT request response):
>
> const svn_string_t *enc_value = svn_base64_encode_string2(value, TRUE,
> pool);
> SVN_ERR(dav_svn__send_xml
> (eb->bb, eb->output,
> "<S:change-%s-prop name=\"%s\">%s</S:change-%s-prop>" DEBUG_CR,
> file_or_dir, qname, enc_value->data, file_or_dir));
>
> dav_svn__send_xml() is a wrapper around apr_brigade_vprintf().
>
> As you know, apr_brigade_vprintf() (in buckets/apr_brigade.c) looks like
> so:
>
> APU_DECLARE(apr_status_t) apr_brigade_vprintf(apr_bucket_brigade *b,
> apr_brigade_flush flush,
> void *ctx,
> const char *fmt, va_list va)
> {
> /* the cast, in order of appearance */
> struct brigade_vprintf_data_t vd;
> char buf[APR_BUCKET_BUFF_SIZE];
> int written;
>
> vd.vbuff.curpos = buf;
> vd.vbuff.endpos = buf + APR_BUCKET_BUFF_SIZE;
> vd.b = b;
> vd.flusher = &flush;
> vd.ctx = ctx;
> vd.cbuff = buf;
>
> written = apr_vformatter(brigade_flush, &vd.vbuff, fmt, va);
>
> if (written == -1) {
> return -1;
> }
>
> /* tack on null terminator to remaining string */
> *(vd.vbuff.curpos) = '\0';
>
> /* write out what remains in the buffer */
> return apr_brigade_write(b, flush, ctx, buf, vd.vbuff.curpos - buf);
> }
>
> The function apr_vformatter() uses the buffer "buf" to format the string.
> This function in turn uses the macro INS_CHAR to add characters to the
> buffer. INS_CHAR is defined like this:
>
> #define INS_CHAR(c, sp, bep, cc) \
> { \
> if (sp) { \
> if (sp >= bep) { \
> vbuff->curpos = sp; \
> if (flush_func(vbuff)) \
> return -1; \
> sp = vbuff->curpos; \
> bep = vbuff->endpos; \
> } \
> *sp++ = (c); \
> } \
> cc++; \
> }
>
> So, when the macro is executed to add a new character to the buffer and the
> buffer is full, the flush function is called to make room for the new
> character, and then the character is added. Of course, if the buffer has
> room for exactly one more character, it is not flushed, the character is
> added, and the current position of the buffer is at its end (which is
> actually one byte beyond the space allocated for the buffer).
>
> After the call to apr_vformatter(), there will be stuff in the buffer. In
> the special case above, the buffer may be perfectly full (perhaps after
> having been flushed one or more times, but still full now). Then, without
> checking for that condition, this line is executed:
>
> /* tack on null terminator to remaining string */
> *(vd.vbuff.curpos) = '\0';
>
> Uh-oh. Buffer overflow!
>
> Our CollabNet engineer is proposing a simple fix: defining 'buf' inside
> apr_brigade_vprintf() like so:
>
> char buf[APR_BUCKET_BUFF_SIZE + 1]
>
> (Note the "+ 1" to make room for that pesky NULL byte.)
>
> But I'm wondering if an equally correct fix would be to simply not tack the
> '\0' onto the buffer at all. Doesn't apr_brigade_write() accept both the
> buffer and the number of bytes to write? Does it really need a
> null-terminated string, especially considering that its input could be
> arbitrary binary data? Other calls to it pass things like "str" and
> "strlen(str)", which would ignore the NULL byte in "str".

I agree; it won't have the terminating '\0' once it is written to the
brigade, and apr_brigade_write() doesn't need it.

------------------------------------------------------
http://subversion.tigris.org/ds/viewMessage.do?dsForumId=462&dsMessageId=1897522
Received on 2009-04-25 03:02:10 CEST

This is an archived mail posted to the Subversion Dev mailing list.

This site is subject to the Apache Privacy Policy and the Apache Public Forum Archive Policy.