Re: API inconsistency in HttpHeader.cc?

From: Amos Jeffries <squid3_at_treenet.co.nz>
Date: Tue, 04 Oct 2011 14:18:08 +1300

 On Mon, 03 Oct 2011 17:31:30 -0600, Alex Rousskov wrote:
> On 10/03/2011 04:42 PM, Amos Jeffries wrote:
>> On Mon, 03 Oct 2011 10:09:42 -0600, Alex Rousskov wrote:
>>> On 10/03/2011 08:37 AM, Kinkie wrote:
>>>> Hi all,
>>>> while working on playground (HttpHdrCc c++-ification, mostly), I
>>>> stumbled upon something which worries me a bit, and I wonder why
>>>> it is
>>>> not causing issues.
>>>>
>>>> HttpHeader.cc defines a few functions which add headers, I'm
>>>> zoning in
>>>> onto HttpHeader::putCc as is the one I've been looking at the
>>>> most.
>>>> Here it is:
>>>>
>>>> void
>>>> HttpHeader::putCc(const HttpHdrCc * cc)
>>>> {
>>>> MemBuf mb;
>>>> Packer p;
>>>> assert(cc);
>>>> /* remove old directives if any */
>>>> delById(HDR_CACHE_CONTROL);
>>>> /* pack into mb */
>>>> mb.init();
>>>> packerToMemInit(&p, &mb);
>>>> cc->packInto(&p);
>>>> /* put */
>>>> addEntry(new HttpHeaderEntry(HDR_CACHE_CONTROL, NULL,
>>>> mb.buf));
>>>> /* cleanup */
>>>> packerClean(&p);
>>>> mb.clean();
>>>> }
>>>>
>>>> The problem is that addEntry is initialized from the raw storage
>>>> of a
>>>> MemBuf (filled-in via packer), expecting a NULL-terminated
>>>> c-string
>>>> value.
>>>> Which may very well not be the case. For instance, if noone as
>>>> written
>>>> anything to the MemBuf.
>>>
>>> I suspect it was impossible for the old code to call putCc with an
>>> empty
>>> Cache-Control header under normal conditions. Abnormal conditions
>>> were
>>> rare, [nearly] never fatal because the buffer is [usually] zeroed
>>> on
>>> allocation, and so the bug was never noticed.
>
> I should have made my comments less specific to the empty CC header
> case. What Kinkie is asking about is null-termination which is not
> specific to empty CC. Kinkie just used an empty CC as an obvious
> example
> of a not explicitly terminated mb.buf.
>

 Ah, Right.

>
>> Empty CC is invalid HTTP. If you are creating an empty CC header
>> something is wrong elsewhere in the code.
>
> While it is true that empty CC on the wire is syntactically invalid,
> I
> can think of three cases where an object representing it may appear
> inside Squid:
>
> 1. Squid trying to preserve original headers instead of trying to
> "fix" them. I do not think we do that to CC now, but many proxy
> admins
> want their proxies to be minimally invasive. Yes, this creates
> smuggling-like exploitation opportunities but so is fixing headers
> (it
> is essentially two sides of the same coin).

 For this case I think we want to have a header-blob construct.
 Essentially what I'm expecting the HeaderParser to produce after SBuf
 conversion:
  - a 'parent' SBuf pointing to the start of the header line and running
 to terminal '\n'.
  - a 'label' SBuf pointing to the start of the header name and running
 to terminal ':'.
  - a 'header-value' SBuf pointing to the start of the non-whitespace
 header values and running to terminal '\n'.

 When preserving originals we decide whether to output the second two as
 a pair, like we are supposed to for compliant syntax cleaning, resulting
 in trimmed whitespace garbage. Or for fully transparent, dump the first
 back onto the wire.

>
> 2. Squid obeying configuration options or some kind of internal
> logic
> that removes an existing CC directive. I am not sure there are cases
> like that now, but that is not important for this thread. There is no
> code that checks that removing a directive does not lead to an empty
> cache control header. Moreover, I do not think we need such code as
> it
> would make callers life difficult.

 I think we may have this as a unreported problem. There was a comment
 in HTTPbis about proxies emitting empty headers questioning what to do
 about storage in that case. So likely there is a portion of the user
 base causing or experiencing this brokenness.

>
> 3. Debugging. We may print an "under construction" CC header that
> is
> currently empty. I doubt we do that now, but I doubt an audit would
> catch this if we start doing it later. And debugging code is not the
> place to check for header validity anyway.
>
> Until we start doing #1, we can simply check for an empty CC header
> when
> packing it to solve #2 and #3 (but this is not what Kinkie is asking
> about, I think).
>
>
>> Same for range and SC headers.
>>
>> In the case of SC replacing a CC with nothing there will still be a
>> '
>> field="" ' string value to set.
>>
>>>
>>>> A possible solution could be to mb.terminate() just before
>>>> addEntry,
>>>> but that has its own problems: if the MemBuf doesn't have the
>>>> space to
>>>> grow for appending the trailing \0, it will assert (see XXX on
>>>> MemBuff::terminate() ).
>>>>
>>>> putContRange, putRange, putSc all share the same blueprint and,
>>>> potentially, issue.
>>>>
>>>> I wonder why this is not biting us, and how to best address this.
>>>> Any
>>>> ideas or suggestions?
>>
>> I think the code which needs to just clear the header is being
>> compliant
>> and using delById() API instead of the put*() API with no value.
>>
>>>
>>> I suggest the following:
>>>
>>> 1) Add a safe convertion from MemBuf to String (the new explicit
>>> String constructor or a global function will have access to MemBuf
>>> length so it will not need to terminate the MemBuf).
>>>
>>> 2) Add a HttpHeaderEntry constructor that accepts const MemBuf
>>> reference as header value.
>>>
>>> This approach will not increase the number of copies we make, will
>>> not
>>> change overall header handling logic, and will avoid unterminated
>>> buffer
>>> use.
>>>
>>
>> This may be needed anyway, and/or a good idea. But I think its a fix
>> for
>> the wrong problem here.
>
> If I interpreted Kinkie's concern correctly, the situation is
> reversed:
> Empty CC is the "wrong problem" as he is concerned about not
> explicitly
> terminated buffer which may happen even if CC is not empty as packing
> API does not guarantee termination, IIRC. My suggestions are meant to
> address that concern only.

 Okay. I stand corrected.

 Amos
Received on Tue Oct 04 2011 - 01:18:12 MDT

This archive was generated by hypermail 2.2.0 : Tue Oct 04 2011 - 12:00:05 MDT