Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Guard against OA_GZIPBITS streaming race #2904

Merged
merged 2 commits into from
May 21, 2019
Merged

Conversation

rezan
Copy link
Member

@rezan rezan commented Feb 13, 2019

Ran into an issue where Varnish reports an incorrect Content-Length when streaming a backend response (gzip) and running that thru a gunzip VDP. Turns out that OA_GZIPBITS is being unsafely used. The patch simply forces Varnish to do a chunked response when streaming.

Reproducer VTC is here.

Just to explain, when we write the gunzip length of 3167 here:

vbe64enc(p + 24, vg->vz.total_out);

We end up here:

p[0] = (u >> 24) & 0xff;
p[1] = (u >> 16) & 0xff;
p[2] = (u >> 8) & 0xff;
p[3] = u & 0xff;

And because we are writing single bytes, the delivery thread can incorrectly read 3167 as 3072 or 95, which would be the good value missing 8 of its bits.

I talked this over with @mbgrydeland and we ran into a few issues. First, there is no good VTC for this. This is because the VTC will either pass with the race in place or it can possibly fail with the patch in place due to the way a boc is grabbed late in the client delivery. Basically, the VTC checks that a gunzip streamed response has no Content-Length, but there are cases where this is both true and false before and after the patch. So no VTC unless everyone is ok with a racy VTC.

The other possibly bigger issue is that when we cache miss, get a boc, but its cleared by the time we grab it in transmit, we could have stale memory in cache, which could in theory give us a partial read. I believe this is due to not hitting a memory barrier anywhere in this code path. I left this issue for Martin to explain (and fix) as this represents a much bigger issue than this patch can handle.

@nigoroll
Copy link
Member

nigoroll commented Feb 17, 2019

nice catch, @rezan

here's the vtc without the annoying 503: https://gist.github.com/nigoroll/75468b9a0848dca31a743d001ae01c2c
The trick is to run the vtc server with -keepalive

nigoroll added a commit to nigoroll/varnish-cache that referenced this pull request Feb 17, 2019
If accepted, the same technique should be applied to all the vend.h
macros

Fixes varnishcache#2904
@nigoroll
Copy link
Member

#2909 contains a suggestion how to fix the root cause

@bsdphk
Copy link
Contributor

bsdphk commented Feb 18, 2019

Fix proposed in #2909 is a nogo, the code does not ensure atomicity in the face of optimizing compilers and vend.h methods are specifically meant to work on unaligned storage.

To fix this, we need some kind of reliable indicator that an attribute has in fact been set.

oa->present may be that indicator, or if not, could become it.

@rezan
Copy link
Member Author

rezan commented Apr 30, 2019

Do we want to do anything here? The PR basically forces a gzip stream when streaming. Not sure if we got anywhere in proving that the boc is unsafe to read in this state.

We do not hold a reference, the magic can be unstable.
@rezan
Copy link
Member Author

rezan commented May 15, 2019

Turns out the magic check on the boc was racy too in that it can be zero'ed away at anytime. I discussed with @mbgrydeland, we decided to just remove the check and continue to stream if the boc is non null. The other option would be to grab a reference.

@bsdphk bsdphk merged commit ea3c109 into varnishcache:master May 21, 2019
dridi added a commit to dridi/varnish-cache that referenced this pull request Jan 10, 2020
Under load, client c4 from g00005.vtc may fail with a 200 response
instead of the expected 206 partial response.

There is a window during which we might still see a boc, but because
c4 sets beresp.do_stream to false, the fetch has to be over. To close
this race we can instead reference the boc as suggested in varnishcache#2904 and
keep track of the boc state.
dridi added a commit that referenced this pull request Jan 13, 2020
Under load, client c4 from g00005.vtc may fail with a 200 response
instead of the expected 206 partial response.

There is a window during which we might still see a boc, but because
c4 sets beresp.do_stream to false, the fetch has to be over. To close
this race we can instead reference the boc as suggested in #2904 and
keep track of the boc state.
dridi added a commit that referenced this pull request Mar 2, 2020
Under load, client c4 from g00005.vtc may fail with a 200 response
instead of the expected 206 partial response.

There is a window during which we might still see a boc, but because
c4 sets beresp.do_stream to false, the fetch has to be over. To close
this race we can instead reference the boc as suggested in #2904 and
keep track of the boc state.

Refs #3235
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants