Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Conversation

@zlm2012
Copy link
Contributor

@zlm2012 zlm2012 commented Dec 10, 2017

t/50unexpected-upstream-body.t would trigger an AddressSanitizer heap-use-after-free error though the test still passes. It is caused by http1.c where memory pool is released when last chunck of data is sent, but we still do some check on struct rp_generator_t which is already freed. This PR fix the problem by switching the order of the check mentioned above and do_send.

@deweerdt
Copy link
Member

Hello @zlm2012 thanks for taking a look at this. I have two questions regarding the PR as it currently stands:

  • Wouldn't this call site also be affected https://github.com/h2o/h2o/pull/1534/files#diff-302e03bf5939f9d70777e2647bdb940aR364 ?
  • We want to set the await_send callback if we didn’t manage to send data: self->client->sock->input->size > overrides->max_buffer_size, so do_send has to be called before the check, wouldn't the PR cause stopping reading from the client unnecessarily in the case we did manage to send the whole buffer?

@zlm2012
Copy link
Contributor Author

zlm2012 commented Dec 14, 2017

Thanks for replying. I just happened to get to know this issue then tried to fix it. So for the first question, it could be. There's just no evidence now. For the second question, it could. I will investigate this issue more thoroughly then update this PR.

@zlm2012
Copy link
Contributor Author

zlm2012 commented Jan 6, 2018

After some investigation, heep use after free only happens when finalostream_send() is called with inbufcnt == 0 after header already sent in lib/http1.c. It is because on_send_complete() is delayed as a callback of h2o_socket_write() so do_proceed() could stay safe. So currently on_send_complete() is changed to be always delayed. I don't know if that could cause other issues or impact on performance. cc @kazuho

@kazuho
Copy link
Member

kazuho commented Jan 11, 2018

Thank you for the changes.

So now we fix the issue in http1.c instead of the original approach that fixed the issue in the caller. Generally speaking I prefer doing things synchronously, but I have no objection for fixing this particular issue this way.

That said, I do not think that libuv allows passing a zero-length array of buffers to uv_write. See https://github.com/libuv/libuv/blob/v1.x/src/unix/stream.c#L1405.

So I think that we need to make changes to our libuv binding to address the issue.

@zlm2012
Copy link
Contributor Author

zlm2012 commented Jan 11, 2018

Generally speaking I prefer doing things synchronously, but I have no objection for fixing this particular issue this way.

Of course doing things synchronously is much more clear & straightforward. IMHO heap use after free could also be triggered somewhere like https://github.com/h2o/h2o/blob/master/lib/handler/file.c#L204 (though highly unlikely go there) mainly during error handling. I'm not sure if there're more points like that. So maybe current approach is more suitable according to logic simplicity of handlers but not so good for maintainance? I'm not sure.

That said, I do not think that libuv allows passing a zero-length array of buffers to uv_write. See https://github.com/libuv/libuv/blob/v1.x/src/unix/stream.c#L1405.

So I think that we need to make changes to our libuv binding to address the issue.

Or maybe we could change http1.c in a way that only make on_send_complete() calling asynchronously? Of course we need change libuv binding if making do_write() accept zero-length array is necessary enough. If so, should we add uv_timer_t to struct st_h2o_uv_socket_t for bypassing uv_write() or in another approach?

@kazuho
Copy link
Member

kazuho commented Jan 11, 2018

@zlm2012 Thank you for pointing out another scary piece of code. I am now totally convinced that we should fix the issue in the protocol handler (or the event loop).

Or maybe we could change http1.c in a way that only make on_send_complete() calling asynchronously?

I believe that that is the way to go. Can we use the set_timeout function defined in http1.c for the purpose?

I do not think that modifying the libuv binding to handle zero-element write is a good idea. OTOH I would prefer noticing such mistake being made also when evloop is being used. How about adding an assertion in h2o_socket_write that checks that inbufcnt is not zero?

@zlm2012
Copy link
Contributor Author

zlm2012 commented Jan 12, 2018

Suggestions applied.

@kazuho
Copy link
Member

kazuho commented Jan 13, 2018

Thank you for the changes. The code looks good to me.

@kazuho kazuho merged commit 6030030 into h2o:master Jan 13, 2018
@kazuho
Copy link
Member

kazuho commented Jan 16, 2018

Note: this has been a long-standing bug but became a real issue when #1358 got merged.

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.

3 participants