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,