Bug 41143

Summary: Incorrect ap_getline assumption in mod_proxy_http
Product: Apache httpd-2 Reporter: Davi Arnaut <davi>
Component: mod_proxyAssignee: Apache HTTPD Bugs Mailing List <bugs>
Status: REOPENED ---    
Severity: normal Keywords: PatchAvailable
Priority: P2    
Version: 2.2-HEAD   
Target Milestone: ---   
Hardware: All   
OS: All   
Attachments: patch against the 2.2.x branch

Description Davi Arnaut 2006-12-10 15:03:32 UTC
The misplaced ap_getline may discard a valid header after a too long header, ap_getline already discards 
extra data.
The patch (side effect) also reduces the stack usage considerably (by 8KB).
Comment 1 Davi Arnaut 2006-12-10 15:04:36 UTC
Created attachment 19239 [details]
patch against the 2.2.x branch
Comment 2 jfclere 2007-06-28 00:02:41 UTC
Fixed in 2.2.4
Comment 3 Davi Arnaut 2007-06-28 04:16:45 UTC
Are you sure? which commit fixed it? I fail to see how this was solved. The code in
question is still present on the 2.2.x branch and trunk.
Comment 4 Ruediger Pluem 2007-06-28 14:16:20 UTC
(In reply to comment #0)
> The misplaced ap_getline may discard a valid header after a too long header,
ap_getline already discards 
> extra data.

I am no sure if this is true in the case that the header is larger than 16KB,
because in this case ap_rgetline_core will be left after the second
ap_get_brigade (in the case we only have the ap_core_input_filter in the input
filter chain, not checked the SSL case) which only read 16KB from the socket.

> The patch (side effect) also reduces the stack usage considerably (by 8KB).

Comment 5 Davi Arnaut 2007-06-28 14:50:44 UTC
(In reply to comment #4)
> (In reply to comment #0)
> > The misplaced ap_getline may discard a valid header after a too long header,
> > ap_getline already discards extra data.
> 
> I am no sure if this is true in the case that the header is larger than 16KB,
> because in this case ap_rgetline_core will be left after the second
> ap_get_brigade (in the case we only have the ap_core_input_filter in the input
> filter chain, not checked the SSL case) which only read 16KB from the socket.
> 

Bear with me:

ap_proxy_read_headers() calls ap_getline() with a buffer of 8192 bytes. ap_getline()
creates a brigade and calls ap_rgetline() to get a line of protocol input (and to copy
the line to the buffer). ap_rgetline_core() calls ap_get_brigade(AP_MODE_GETLINE)
which returns a line (brigade) of 8200 bytes (for the sake of example). If all data
is in the first bucket, the line size could overflow the buffer, then APR_ENOSPC
is returned. ap_getline sees the APR_ENOSPC and returns the buffer size.

We have two problems here. First, if the data of the first bucket overflows the buffer,
the data is not copied but ap_getline returns the size of the buffer (meaning that the
data was copied). Second, ap_proxy_read_headers checks if the returned len is >= than
the buffer size-1, if so, it calls ap_getline again, but the line was already read by
ap_get_brigade and discarded.

Makes sense now?
Comment 6 Ruediger Pluem 2007-06-28 15:06:26 UTC
(In reply to comment #5)

> Bear with me:
> 
> ap_proxy_read_headers() calls ap_getline() with a buffer of 8192 bytes.
ap_getline()
> creates a brigade and calls ap_rgetline() to get a line of protocol input (and
to copy
> the line to the buffer). ap_rgetline_core() calls ap_get_brigade(AP_MODE_GETLINE)
> which returns a line (brigade) of 8200 bytes (for the sake of example). If all
data

But ap_get_brigade(AP_MODE_GETLINE) returns 8192 bytes at max and it is not
guaranteed that this data contains a LF. It is only guaranteed that
ap_get_brigade(AP_MODE_GETLINE) does not read *past* a LF. See
ap_core_input_filter and apr_brigade_split_line for why.


> 
> Makes sense now?

I agree that current code does it wrong, but I fear that there are other cases
where your patch will do it wrong.
Comment 7 Davi Arnaut 2007-06-28 16:18:24 UTC
(In reply to comment #6)
> (In reply to comment #5)
> 
> 
> But ap_get_brigade(AP_MODE_GETLINE) returns 8192 bytes at max and it is not
> guaranteed that this data contains a LF.

No! ap_get_brigade(AP_MODE_GETLINE) may return much more than 8192 bytes.
Suppose a brigade has two buckets of APR_BUCKET_BUFF_SIZE (8000) bytes each
and the line break is on the second bucket, at position 4000. In this case, 
apr_brigade_split_line() will read the first bucket, and will proceed to read the
next one since it's size is not >= HUGE_STRING_LEN. It will find a LF on the second
one and return a brigade of 12000 bytes.

> I agree that current code does it wrong, but I fear that there are other cases
> where your patch will do it wrong.

Would you care to elaborate? Also, i'm not sure too if it's the best fix, but it can't
get any worse (famous last words).
Comment 8 Davi Arnaut 2007-06-28 16:30:25 UTC
I forgot to mention, but but if my memory serves well I spotted this issue while playing with
large cookies. It's not a critical real-world issue, yet :-)

Thanks for you comments.
Comment 9 Ruediger Pluem 2007-06-29 07:02:44 UTC
(In reply to comment #7)
> (In reply to comment #6)

> > But ap_get_brigade(AP_MODE_GETLINE) returns 8192 bytes at max and it is not
> > guaranteed that this data contains a LF.
> 
> No! ap_get_brigade(AP_MODE_GETLINE) may return much more than 8192 bytes.

Ok. You are correct with this. Sorry, I misread the loop. 

> Suppose a brigade has two buckets of APR_BUCKET_BUFF_SIZE (8000) bytes each
> and the line break is on the second bucket, at position 4000. In this case, 
> apr_brigade_split_line() will read the first bucket, and will proceed to read the
> next one since it's size is not >= HUGE_STRING_LEN. It will find a LF on the
second
> one and return a brigade of 12000 bytes.

So apr_brigade_split_line() will not return more than maxbytes-1 + the size of
the following bucket. So the brigade returned by ap_get_brigade(AP_MODE_GETLINE)
may not contain the LF if the header is long enough (I would suppose that 16KB =
2 * 8000 (APR_BUCKET_BUFF_SIZE) + 384 should be sufficient to get there).
I admit that this is a constructed edge case that might not happen in real life :-).

> 
> > I agree that current code does it wrong, but I fear that there are other cases
> > where your patch will do it wrong.
> 
> Would you care to elaborate? Also, i'm not sure too if it's the best fix, but
it can't
> get any worse (famous last words).

It is not getting worse, only different :-).