Opened 4 years ago

Closed 4 years ago

Last modified 4 years ago

#1928 closed defect (invalid)

browser cannot correctly decode http response headers when http2 is used

Reported by: val-kulkov@… Owned by:
Priority: major Milestone:
Component: nginx-module Version: 1.17.x
Keywords: http2 OpenWrt Cc: val-kulkov@…
uname -a: Linux eg19 4.19.84 #0 SMP Tue Nov 19 13:59:03 2019 x86_64 GNU/Linux
nginx -V: nginx version: nginx/1.17.5 (x86_64-pc-linux-gnu)
built with OpenSSL 1.1.1d 10 Sep 2019
TLS SNI support enabled
configure arguments: --target=x86_64-openwrt-linux --host=x86_64-openwrt-linux --build=x86_64-pc-linux-gnu --program-prefix= --program-suffix= --prefix=/usr --exec-prefix=/usr --bindir=/usr/bin --sbindir=/usr/sbin --libexecdir=/usr/lib --sysconfdir=/etc --datadir=/usr/share --localstatedir=/var --mandir=/usr/man --infodir=/usr/info --crossbuild=Linux::x86_64 --prefix=/usr --conf-path=/etc/nginx/nginx.conf --with-http_ssl_module --add-module=/home/val/Development/openwrt/targets/nuc_x86_64/build_dir/target-x86_64_musl/nginx-ssl/nginx-1.17.5/nginx-naxsi/naxsi_src --add-module=/home/val/Development/openwrt/targets/nuc_x86_64/build_dir/target-x86_64_musl/nginx-ssl/nginx-1.17.5/lua-nginx --with-ipv6 --add-module=/home/val/Development/openwrt/targets/nuc_x86_64/build_dir/target-x86_64_musl/nginx-ssl/nginx-1.17.5/nginx-ubus-module --with-http_auth_request_module --with-http_v2_module --with-http_realip_module --with-http_secure_link_module --with-http_sub_module --with-stream --with-stream_ssl_module --with-stream_ssl_preread_module --add-module=/home/val/Development/openwrt/targets/nuc_x86_64/build_dir/target-x86_64_musl/nginx-ssl/nginx-1.17.5/nginx-headers-more --add-module=/home/val/Development/openwrt/targets/nuc_x86_64/build_dir/target-x86_64_musl/nginx-ssl/nginx-1.17.5/nginx-brotli --add-module=/home/val/Development/openwrt/targets/nuc_x86_64/build_dir/target-x86_64_musl/nginx-ssl/nginx-1.17.5/nginx-rtmp --add-module=/home/val/Development/openwrt/targets/nuc_x86_64/build_dir/target-x86_64_musl/nginx-ssl/nginx-1.17.5/nginx-ts --error-log-path=/var/log/nginx/error.log --pid-path=/var/run/nginx.pid --lock-path=/var/lock/nginx.lock --http-log-path=/var/log/nginx/access.log --http-client-body-temp-path=/var/lib/nginx/body --http-proxy-temp-path=/var/lib/nginx/proxy --http-fastcgi-temp-path=/var/lib/nginx/fastcgi --with-cc=x86_64-openwrt-linux-musl-gcc --with-cc-opt='-I/home/val/Development/openwrt/targets/nuc_x86_64/staging_dir/target-x86_64_musl/usr/include -I/home/val/Development/openwrt/targets/nuc_x86_64/staging_dir/toolchain-x86_64_gcc-7.5.0_musl/usr/include -I/home/val/Development/openwrt/targets/nuc_x86_64/staging_dir/toolchain-x86_64_gcc-7.5.0_musl/include/fortify -I/home/val/Development/openwrt/targets/nuc_x86_64/staging_dir/toolchain-x86_64_gcc-7.5.0_musl/include -Os -pipe -fno-caller-saves -fno-plt -fhonour-copts -Wno-error=unused-but-set-variable -Wno-error=unused-result -iremap/home/val/Development/openwrt/targets/nuc_x86_64/build_dir/target-x86_64_musl/nginx-ssl/nginx-1.17.5:nginx-1.17.5 -Wformat -Werror=format-security -fstack-protector -D_FORTIFY_SOURCE=1 -Wl,-z,now -Wl,-z,relro -fvisibility=hidden -ffunction-sections -fdata-sections -DNGX_LUA_NO_BY_LUA_BLOCK' --with-ld-opt='-L/home/val/Development/openwrt/targets/nuc_x86_64/staging_dir/target-x86_64_musl/usr/lib -L/home/val/Development/openwrt/targets/nuc_x86_64/staging_dir/target-x86_64_musl/lib -L/home/val/Development/openwrt/targets/nuc_x86_64/staging_dir/toolchain-x86_64_gcc-7.5.0_musl/usr/lib -L/home/val/Development/openwrt/targets/nuc_x86_64/staging_dir/toolchain-x86_64_gcc-7.5.0_musl/lib -znow -zrelro -Wl,--gc-sections' --without-http_upstream_zone_module

Description

When http2 is enabled, Google Chrome and Firefox cannot correctly decode http2 response headers received from Nginx. The response headers appear scrambled. At the same time, both browsers decode the body of the response correctly.

Since the browser cannot correctly decode http2 response headers, the browser displays "ERR_SPDY_COMPRESSION_ERROR" error ("gzip on;" in nginx.conf) or the "ERR_HTTP2_PROTOCOL_ERROR" ("gzip off;").

This issue has been observed in OpenWrt by multiple users: https://github.com/openwrt/packages/issues/8988

A Wireshark packet capture of a conversation between Google Chrome and Nginx (with SSLKEYLOGFILE environment variable set to capture SSL keys) is attached. To decode and view the captured SSL stream in Wireshark, right-click on a TLSv1.3 packet in the packets pane, select "Protocol Preferences" -> "(Pre)-Master-Secret log filename...", click on "Browse..." button next to "(Pre)-Master-Secret log filename..." and select the attached "session_keys.log" file.

Packet 36 is the browser's http2 request to Nginx. Everything looks normal in it. Packet 39 contains the http2 response from Nginx, and that is where things are not normal. The first header in the response looks good: ":status: 200 OK", but then all remaining headers except content-type and content-length appear scrambled.

It is very odd that the body of http2 response from Nginx in packet 41 appears just fine: the browser is able to correctly decode the body of http2 response.

This problem manifests itself on the OpenWrt x86_64 platform, as noted in the OpenWrt issue mentioned above. Nginx on non-x86_64 OpenWrt platforms appear to work fine.

Change History (10)

comment:1 by val-kulkov@…, 4 years ago

Hello admins,

I cannot attach any file to this ticket. I get this error message when uploading a file:

Error: Bad Request
Missing or invalid form token. Do you have cookies enabled?
TracGuide — The Trac User and Administration Guide

Please can someone fix Trac and let me know so that I can attach two files to this ticket, one with Wireshark packet capture and one with SSL keys?

comment:2 by val-kulkov@…, 4 years ago

As a faster alternative, I can email both files to someone who can manually update this ticket at Trac. In this case, I simply need an email address.

comment:3 by Maxim Dounin, 4 years ago

Are you able to reproduce the problem without 3rd party patches? Configure arguments clearly show that you are using a patched nginx version, and 3rd party crossbuild patches as seen elsewhere were reported to cause incorrect builds more than once. Please try a clean version as available from http://nginx.org/en/download.html instead, and build it natively.

(I've reported issues with attachments, it should be fixed soon. But in this particular case there is no need to attach anything unless there is a confirmation that the problem exists without any 3rd party patches.)

comment:4 by val-kulkov@…, 4 years ago

There are only six patches that are applied to Nginx while cross-compiling for OpenWrt. All of them appear fairly trivial:

https://github.com/openwrt/packages/tree/master/net/nginx/patches

For example, 101-feature_test_fix.patch disables run tests which routinely fail in a cross-compilation environment.

I will check if all these patches are still necessary and report the results back here.

comment:5 by val-kulkov@…, 4 years ago

Hi Maxim,

Patch 102-sizeof_test_fix.patch (see the link above) is required because Nginx's configure attempts to compile $NGX_AUTOTEST.c (line 32 in auto/types/sizeof) and then execute it which makes no sense in a cross-compilation environment. The host cannot execute the target's binary code without an emulator.

If you have any suggestion how I should determine the size of $ngx_type on the target platform without executing the target platform's binary code, I would much appreciate it.

Last edited 4 years ago by val-kulkov@… (previous) (diff)

comment:6 by Maxim Dounin, 4 years ago

To make things clear: nginx does not support cross-compilation. If you are trying to cross-compile nginx, it is your responsibility to ensure configure results are correct. Given that cross-compilation results in a misbehaving binary, the first thing to test is a native compilation, without any patches. Are you able to reproduce the problem with a native build of vanilla nginx, without any patches?

comment:7 by val-kulkov@…, 4 years ago

Hi Maxim,

It is not possible to compile Nginx for OpenWrt without these patches:

101-feature_test_fix.patch
102-sizeof_test_fix.patch
103-sys_nerr.patch

This is because the host platform is unable to execute the target platform binaries.

The way I see it, 101-feature_test_fix.patch and 103-sys_nerr.patch are harmless and IMO they could not possibly be the culprits. The 200 and 300 series patches do not modify the code. They only modify configuration files and therefore they are not suspects.

102-sizeof_test_fix.patch is a possible suspect, but it looks okay to me. It uses a clever hack to determine the size of $ngx_type. Here is the relevant part of auto/types/sizeof after applying the patch:

cat << END > $NGX_AUTOTEST.c

#include <sys/types.h>
#include <sys/time.h>
$NGX_INCLUDE_UNISTD_H
#include <signal.h>
#include <stdio.h>
#include <sys/resource.h>
$NGX_INCLUDE_INTTYPES_H
$NGX_INCLUDE_AUTO_CONFIG_H

char object_code_block[] = {
	'\n', 'e', '4', 'V', 'A',
	'0', 'x', ('0' + sizeof($ngx_type)),
	'Y', '3', 'p', 'M', '\n'
};

int main(void) {
    printf("dummy use of object_code_block to avoid gc-section: %c", object_code_block[0]);
    return 0;
}

END


ngx_test="$CC $CC_TEST_FLAGS $CC_AUX_FLAGS \
          -o $NGX_AUTOTEST $NGX_AUTOTEST.c $NGX_LD_OPT $ngx_feature_libs"

eval "$ngx_test >> $NGX_AUTOCONF_ERR 2>&1"


if [ -x $NGX_AUTOTEST ]; then
    ngx_size=`sed -ne 's/^e4VA0x\(.\)Y3pM$/\1/p' < $NGX_AUTOTEST`
    echo " $ngx_size bytes"
fi

Note that it is the ('0' + sizeof($ngx_type)) construct in object_code_block[] that is used to determine the size of $ngx_type. "sed" utility then looks for the sequence of characters defined in object_code_block and obtains value of ('0' + sizeof($ngx_type)). Do you see any issue with this approach?

Last edited 4 years ago by val-kulkov@… (previous) (diff)

comment:8 by val-kulkov@…, 4 years ago

Hi Maxim,

It seems that I found the root cause of the current problem. The endianness of the target platform cannot be correctly determined in auto/endianness because the current test relies on the execution of the code on the target platform during cross-compilation. The "big endian" is the fallback option and if the target platform is "big endian" then everything works. The "little endian" platforms predictably fail.

I am working with the OpenWrt community to see if we can propose a method to reliably identify endianness of the target platform during cross-compilation.

comment:9 by Maxim Dounin, 4 years ago

Resolution: invalid
Status: newclosed

Certainly it is possible to compile nginx for OpenWrt without any patches. You have do it natively, using a native toolchain. On the target platform itself, without using cross-compilation.

Anyway, thanks for confirming that the problem is due to incorrect cross-compilation patches. Closing this.

comment:10 by val-kulkov@…, 4 years ago

OpenWrt is a platform for embedded devices using minimal amounts of RAM and non-volatile storage space. On many of these devices there is simply not enough RAM or storage space to install and use a native toolchain. For this reason, OpenWrt targets are normally built by cross-compilation on host workstations that have enough RAM and disk space.

Anyway, thank you for your consideration.

Note: See TracTickets for help on using tickets.