Mercurial > hg > nginx
comparison src/http/v2/ngx_http_v2.c @ 6954:052305810ca4
HTTP/2: fix flow control with padded DATA frames.
Previously, flow control didn't account for padding in DATA frames,
which meant that its view of the world could drift from peer's view
by up to 256 bytes per received padded DATA frame, which could lead
to a deadlock.
Signed-off-by: Piotr Sikora <piotrsikora@google.com>
author | Piotr Sikora <piotrsikora@google.com> |
---|---|
date | Sun, 26 Mar 2017 01:25:04 -0700 |
parents | e02f1977846b |
children | d38161da62cd |
comparison
equal
deleted
inserted
replaced
6953:663e6a48bfcb | 6954:052305810ca4 |
---|---|
781 | 781 |
782 | 782 |
783 static u_char * | 783 static u_char * |
784 ngx_http_v2_state_data(ngx_http_v2_connection_t *h2c, u_char *pos, u_char *end) | 784 ngx_http_v2_state_data(ngx_http_v2_connection_t *h2c, u_char *pos, u_char *end) |
785 { | 785 { |
786 size_t size; | |
786 ngx_http_v2_node_t *node; | 787 ngx_http_v2_node_t *node; |
787 ngx_http_v2_stream_t *stream; | 788 ngx_http_v2_stream_t *stream; |
789 | |
790 size = h2c->state.length; | |
788 | 791 |
789 if (h2c->state.flags & NGX_HTTP_V2_PADDED_FLAG) { | 792 if (h2c->state.flags & NGX_HTTP_V2_PADDED_FLAG) { |
790 | 793 |
791 if (h2c->state.length == 0) { | 794 if (h2c->state.length == 0) { |
792 ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0, | 795 ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0, |
800 return ngx_http_v2_state_save(h2c, pos, end, | 803 return ngx_http_v2_state_save(h2c, pos, end, |
801 ngx_http_v2_state_data); | 804 ngx_http_v2_state_data); |
802 } | 805 } |
803 | 806 |
804 h2c->state.padding = *pos++; | 807 h2c->state.padding = *pos++; |
805 h2c->state.length--; | 808 |
806 | 809 if (h2c->state.padding >= size) { |
807 if (h2c->state.padding > h2c->state.length) { | |
808 ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0, | 810 ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0, |
809 "client sent padded DATA frame " | 811 "client sent padded DATA frame " |
810 "with incorrect length: %uz, padding: %uz", | 812 "with incorrect length: %uz, padding: %uz", |
811 h2c->state.length, h2c->state.padding); | 813 size, h2c->state.padding); |
812 | 814 |
813 return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_SIZE_ERROR); | 815 return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_SIZE_ERROR); |
814 } | 816 } |
815 | 817 |
816 h2c->state.length -= h2c->state.padding; | 818 h2c->state.length -= 1 + h2c->state.padding; |
817 } | 819 } |
818 | 820 |
819 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0, | 821 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0, |
820 "http2 DATA frame"); | 822 "http2 DATA frame"); |
821 | 823 |
822 if (h2c->state.length > h2c->recv_window) { | 824 if (size > h2c->recv_window) { |
823 ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0, | 825 ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0, |
824 "client violated connection flow control: " | 826 "client violated connection flow control: " |
825 "received DATA frame length %uz, available window %uz", | 827 "received DATA frame length %uz, available window %uz", |
826 h2c->state.length, h2c->recv_window); | 828 size, h2c->recv_window); |
827 | 829 |
828 return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_FLOW_CTRL_ERROR); | 830 return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_FLOW_CTRL_ERROR); |
829 } | 831 } |
830 | 832 |
831 h2c->recv_window -= h2c->state.length; | 833 h2c->recv_window -= size; |
832 | 834 |
833 if (h2c->recv_window < NGX_HTTP_V2_MAX_WINDOW / 4) { | 835 if (h2c->recv_window < NGX_HTTP_V2_MAX_WINDOW / 4) { |
834 | 836 |
835 if (ngx_http_v2_send_window_update(h2c, 0, NGX_HTTP_V2_MAX_WINDOW | 837 if (ngx_http_v2_send_window_update(h2c, 0, NGX_HTTP_V2_MAX_WINDOW |
836 - h2c->recv_window) | 838 - h2c->recv_window) |
852 return ngx_http_v2_state_skip_padded(h2c, pos, end); | 854 return ngx_http_v2_state_skip_padded(h2c, pos, end); |
853 } | 855 } |
854 | 856 |
855 stream = node->stream; | 857 stream = node->stream; |
856 | 858 |
857 if (h2c->state.length > stream->recv_window) { | 859 if (size > stream->recv_window) { |
858 ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0, | 860 ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0, |
859 "client violated flow control for stream %ui: " | 861 "client violated flow control for stream %ui: " |
860 "received DATA frame length %uz, available window %uz", | 862 "received DATA frame length %uz, available window %uz", |
861 node->id, h2c->state.length, stream->recv_window); | 863 node->id, size, stream->recv_window); |
862 | 864 |
863 if (ngx_http_v2_terminate_stream(h2c, stream, | 865 if (ngx_http_v2_terminate_stream(h2c, stream, |
864 NGX_HTTP_V2_FLOW_CTRL_ERROR) | 866 NGX_HTTP_V2_FLOW_CTRL_ERROR) |
865 == NGX_ERROR) | 867 == NGX_ERROR) |
866 { | 868 { |
869 } | 871 } |
870 | 872 |
871 return ngx_http_v2_state_skip_padded(h2c, pos, end); | 873 return ngx_http_v2_state_skip_padded(h2c, pos, end); |
872 } | 874 } |
873 | 875 |
874 stream->recv_window -= h2c->state.length; | 876 stream->recv_window -= size; |
875 | 877 |
876 if (stream->no_flow_control | 878 if (stream->no_flow_control |
877 && stream->recv_window < NGX_HTTP_V2_MAX_WINDOW / 4) | 879 && stream->recv_window < NGX_HTTP_V2_MAX_WINDOW / 4) |
878 { | 880 { |
879 if (ngx_http_v2_send_window_update(h2c, node->id, | 881 if (ngx_http_v2_send_window_update(h2c, node->id, |