[Core] Improve HPACK (#33597)

create-pull-request/patch-6303798
Esun Kim 1 year ago committed by GitHub
parent 523d9a2135
commit 415f2f9ee3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 32
      BUILD
  2. 3
      CMakeLists.txt
  3. 2
      Makefile
  4. 2
      Package.swift
  5. 6
      build_autogenerated.yaml
  6. 1
      config.m4
  7. 1
      config.w32
  8. 2
      gRPC-C++.podspec
  9. 3
      gRPC-Core.podspec
  10. 2
      grpc.gemspec
  11. 2
      grpc.gyp
  12. 2
      package.xml
  13. 176
      src/core/ext/transport/chttp2/transport/hpack_parse_result.cc
  14. 325
      src/core/ext/transport/chttp2/transport/hpack_parse_result.h
  15. 1110
      src/core/ext/transport/chttp2/transport/hpack_parser.cc
  16. 159
      src/core/ext/transport/chttp2/transport/hpack_parser.h
  17. 78
      src/core/ext/transport/chttp2/transport/hpack_parser_table.cc
  18. 23
      src/core/ext/transport/chttp2/transport/hpack_parser_table.h
  19. 24
      src/core/ext/transport/chttp2/transport/parsing.cc
  20. 5
      src/core/lib/backoff/random_early_detection.h
  21. 59
      src/core/lib/surface/validate_metadata.cc
  22. 16
      src/core/lib/surface/validate_metadata.h
  23. 1
      src/python/grpcio/grpc_core_dependencies.py
  24. 3
      test/core/transport/chttp2/BUILD
  25. 8513
      test/core/transport/chttp2/hpack_parser_corpus/clusterfuzz-testcase-minimized-hpack_parser_fuzzer-4865932715229184
  26. 12
      test/core/transport/chttp2/hpack_parser_corpus/clusterfuzz-testcase-minimized-hpack_parser_fuzzer-5365031688536064
  27. 5
      test/core/transport/chttp2/hpack_parser_corpus/crash-06c4093ab80184d59150773f5c19e8e3851aa584
  28. 5
      test/core/transport/chttp2/hpack_parser_corpus/crash-073bba832e73c8f524714e179236756e4854e08f
  29. 18
      test/core/transport/chttp2/hpack_parser_corpus/crash-0865baf4eceb5220cdf0528cff5520f97ed7fbf8
  30. 34
      test/core/transport/chttp2/hpack_parser_corpus/crash-1082c8e03fb43912053db34d510246ffe6aab388
  31. 20
      test/core/transport/chttp2/hpack_parser_corpus/crash-1bda156d5b247fec3b19381d40519e65bdde40de
  32. 86
      test/core/transport/chttp2/hpack_parser_corpus/crash-24437bf259f238cb404aad47b93345a4d5161a37
  33. 4
      test/core/transport/chttp2/hpack_parser_corpus/crash-4271e5059b58be25d0845678c8b1c3f12c0040c3
  34. 99
      test/core/transport/chttp2/hpack_parser_corpus/crash-592efde536e0c000a56776eeb180ae3b6f8ebe70
  35. 34
      test/core/transport/chttp2/hpack_parser_corpus/crash-659b65287a1437d74ef1f334b0148102a461a430
  36. 22
      test/core/transport/chttp2/hpack_parser_corpus/crash-6c53549fc13aab69e2ddf334275ebeb4bdab4234
  37. 27
      test/core/transport/chttp2/hpack_parser_corpus/crash-90dcc8d762ca0b68b8ff7900e0b4856ac19de75f
  38. 19
      test/core/transport/chttp2/hpack_parser_corpus/crash-9d380436e8412e3d4f8f2bc9b6a008c57b777afe
  39. 32
      test/core/transport/chttp2/hpack_parser_corpus/crash-afc9191b4dce8c40516945fb5dc2c98b628a2430
  40. 28
      test/core/transport/chttp2/hpack_parser_corpus/crash-c5c7476698873398f29a0ba0b7cdc7b85fa63173
  41. 22
      test/core/transport/chttp2/hpack_parser_corpus/crash-c88faaa07f7f4fbaf734b9be038ec7daa0bf92fb
  42. 16
      test/core/transport/chttp2/hpack_parser_corpus/crash-d47bb8c8c9640faf351068480844e1be20cfdbb7
  43. 238
      test/core/transport/chttp2/hpack_parser_corpus/crash-d72bb43a581e489916a85216cb6c4fbb3ad844e2
  44. 5
      test/core/transport/chttp2/hpack_parser_corpus/crash-ff4557ffdb266e8c086c05f3da8f7a1bddd6f906
  45. 78
      test/core/transport/chttp2/hpack_parser_fuzzer_test.cc
  46. BIN
      test/core/transport/chttp2/hpack_parser_input_size_corpus/clusterfuzz-testcase-minimized-hpack_parser_input_size_fuzzer-4883111703609344
  47. BIN
      test/core/transport/chttp2/hpack_parser_input_size_corpus/crash-2af1d6b2934f168a7dc4768b93e447802f9ecedf
  48. BIN
      test/core/transport/chttp2/hpack_parser_input_size_corpus/crash-81591b690179358fd403e4730b601c5c0b427491
  49. BIN
      test/core/transport/chttp2/hpack_parser_input_size_corpus/slow-unit-015113bc4dd37f528f1cb0c820660d4011960dab
  50. BIN
      test/core/transport/chttp2/hpack_parser_input_size_corpus/slow-unit-03de8592bf6baa2a62e5542fa96ac2d6ee69b6b9
  51. BIN
      test/core/transport/chttp2/hpack_parser_input_size_corpus/slow-unit-058e8ece1a01b9087308f12521c1109f9d30e573
  52. BIN
      test/core/transport/chttp2/hpack_parser_input_size_corpus/slow-unit-07558bf39768c209ce786612c724f889d1e8891b
  53. BIN
      test/core/transport/chttp2/hpack_parser_input_size_corpus/slow-unit-0ab36d0cfe24b0ddbd4c583c74882df16c3b9627
  54. BIN
      test/core/transport/chttp2/hpack_parser_input_size_corpus/slow-unit-0b09f05105d317412b7c827d3686f5fa32bebf49
  55. BIN
      test/core/transport/chttp2/hpack_parser_input_size_corpus/slow-unit-0ca5bea44d2bc9166b4734e0716b8b6da5ec2029
  56. BIN
      test/core/transport/chttp2/hpack_parser_input_size_corpus/slow-unit-0efc2b21ea49b7721ce2b20e13effe7c96c64498
  57. BIN
      test/core/transport/chttp2/hpack_parser_input_size_corpus/slow-unit-123f67a0e63101e7898f2a8d23d2f588ebde4a98
  58. BIN
      test/core/transport/chttp2/hpack_parser_input_size_corpus/slow-unit-1298ff90dade093f5481f6bc80777326938cf4b4
  59. BIN
      test/core/transport/chttp2/hpack_parser_input_size_corpus/slow-unit-135681321b796d8f765261672da373db518d538b
  60. BIN
      test/core/transport/chttp2/hpack_parser_input_size_corpus/slow-unit-152f69bc17f458569744172a8c3d6971ac66dff8
  61. BIN
      test/core/transport/chttp2/hpack_parser_input_size_corpus/slow-unit-1bd14ffddb4215b0c81272d73d78e4d444a851ee
  62. BIN
      test/core/transport/chttp2/hpack_parser_input_size_corpus/slow-unit-1ec7975001ad03530af0b84e6527752b863ad594
  63. BIN
      test/core/transport/chttp2/hpack_parser_input_size_corpus/slow-unit-1f3013b5c089a3affcae856ec1b0a0f70d217740
  64. BIN
      test/core/transport/chttp2/hpack_parser_input_size_corpus/slow-unit-1f9c5fcdcf7348d5c165eb9a8b9755da60e7da5c
  65. BIN
      test/core/transport/chttp2/hpack_parser_input_size_corpus/slow-unit-20bff712090e0c110511522a6aec43f50ced4da9
  66. BIN
      test/core/transport/chttp2/hpack_parser_input_size_corpus/slow-unit-20c88a592c8cb36ab342c77a75c23964dd9ac09e
  67. BIN
      test/core/transport/chttp2/hpack_parser_input_size_corpus/slow-unit-214ef1f403047786f5aae50aae0bef64243ea423
  68. BIN
      test/core/transport/chttp2/hpack_parser_input_size_corpus/slow-unit-245a90a2668df8a643d9b8922dbc34825f3e5569
  69. BIN
      test/core/transport/chttp2/hpack_parser_input_size_corpus/slow-unit-31bc6369c08034fde2683229bc30fd15bb767158
  70. BIN
      test/core/transport/chttp2/hpack_parser_input_size_corpus/slow-unit-3b2fb306d57d5e9f7bcd0bcb1cbf34fcec88a30b
  71. BIN
      test/core/transport/chttp2/hpack_parser_input_size_corpus/slow-unit-41fb06b30a8237efa627ed4eb0afaa3221ac4abe
  72. BIN
      test/core/transport/chttp2/hpack_parser_input_size_corpus/slow-unit-44af09a92eeb9143f1055d66a3a98d7e89b65e07
  73. BIN
      test/core/transport/chttp2/hpack_parser_input_size_corpus/slow-unit-4b54c31cc4232038a9561c0c52dcf4f99317bab5
  74. BIN
      test/core/transport/chttp2/hpack_parser_input_size_corpus/slow-unit-4cd819b2b070f7cd5c287d4f1a6d04ea466e15ae
  75. BIN
      test/core/transport/chttp2/hpack_parser_input_size_corpus/slow-unit-51d2744484cadc664760e331a4c91e7a4c43feb1
  76. BIN
      test/core/transport/chttp2/hpack_parser_input_size_corpus/slow-unit-57702140b51ec7c39b73e68519c5dc235e79098c
  77. BIN
      test/core/transport/chttp2/hpack_parser_input_size_corpus/slow-unit-588c49bc604a6231b9638ad628626a0665f3d3ba
  78. BIN
      test/core/transport/chttp2/hpack_parser_input_size_corpus/slow-unit-5a12ad90db4e144bd286002fe5f9f862afb7a429
  79. BIN
      test/core/transport/chttp2/hpack_parser_input_size_corpus/slow-unit-5f2ae06e8068c973de09d9bbff0b83deddbf654c
  80. BIN
      test/core/transport/chttp2/hpack_parser_input_size_corpus/slow-unit-60118614a0ee78fe56eb10631d11fd04a6e54348
  81. BIN
      test/core/transport/chttp2/hpack_parser_input_size_corpus/slow-unit-6148d1287ad4f3612a69e27f00c23ca5bd76a2e4
  82. BIN
      test/core/transport/chttp2/hpack_parser_input_size_corpus/slow-unit-6b3d64423a6fedf8fd0da585e3ef259793575e0a
  83. BIN
      test/core/transport/chttp2/hpack_parser_input_size_corpus/slow-unit-6fca8b6d71567434d128919f5e5b087a95e8dc36
  84. BIN
      test/core/transport/chttp2/hpack_parser_input_size_corpus/slow-unit-79f273f547f27661fb507887e2de99fb846a38d3
  85. BIN
      test/core/transport/chttp2/hpack_parser_input_size_corpus/slow-unit-7bccb0c6f828e36f42e92dc95e7bc1194c2e384f
  86. BIN
      test/core/transport/chttp2/hpack_parser_input_size_corpus/slow-unit-829cb6b69fd6874ba62b1d9e81c1db4f2810578c
  87. BIN
      test/core/transport/chttp2/hpack_parser_input_size_corpus/slow-unit-8b9927241b1f8cd86bf7aa947c22a38ac1a02822
  88. BIN
      test/core/transport/chttp2/hpack_parser_input_size_corpus/slow-unit-8c0937c405509b25e563d9eedcf6a8cf1ae6e7e7
  89. BIN
      test/core/transport/chttp2/hpack_parser_input_size_corpus/slow-unit-8e319a9f1aaa99eb12086568efbcb8fe9d4f45c4
  90. BIN
      test/core/transport/chttp2/hpack_parser_input_size_corpus/slow-unit-8f05e81342c302bcb97a31c93d42c690deda4f2d
  91. BIN
      test/core/transport/chttp2/hpack_parser_input_size_corpus/slow-unit-8f419c102e129ebbfc7093b88f9da53e41672100
  92. BIN
      test/core/transport/chttp2/hpack_parser_input_size_corpus/slow-unit-9baf490049fb6d5374dcf2a21d89589c07b50c85
  93. BIN
      test/core/transport/chttp2/hpack_parser_input_size_corpus/slow-unit-9ea07de715a1deafee30c3ae13b7882d13adc133
  94. BIN
      test/core/transport/chttp2/hpack_parser_input_size_corpus/slow-unit-9fbf404499c65b338e66fbdbb7f392eee25b58d8
  95. BIN
      test/core/transport/chttp2/hpack_parser_input_size_corpus/slow-unit-a49f0308e5ca8aa2eeb640a9d4b3a9b38200200c
  96. BIN
      test/core/transport/chttp2/hpack_parser_input_size_corpus/slow-unit-b00a8d8fe0c4f5f624fee74404eadf3fcc8506b5
  97. BIN
      test/core/transport/chttp2/hpack_parser_input_size_corpus/slow-unit-b592ca99a4503d5e148bc434d22e2bb8c54fa67f
  98. BIN
      test/core/transport/chttp2/hpack_parser_input_size_corpus/slow-unit-baa7a814fc03b9d814fb62d6faf3b6f878ef8ed2
  99. BIN
      test/core/transport/chttp2/hpack_parser_input_size_corpus/slow-unit-c10a17f4b8404e97ea04d5f80ec6f0c854e6c722
  100. BIN
      test/core/transport/chttp2/hpack_parser_input_size_corpus/slow-unit-cc8fd4207cf949f1508099c4f292e641fc016fcb
  101. Some files were not shown because too many files have changed in this diff Show More

32
BUILD

@ -3726,23 +3726,47 @@ grpc_cc_library(
"//src/core:ext/transport/chttp2/transport/hpack_parser_table.h",
],
external_deps = [
"absl/functional:function_ref",
"absl/status",
"absl/strings",
"absl/strings:str_format",
],
deps = [
"gpr",
"gpr_platform",
"grpc_base",
"grpc_trace",
"hpack_parse_result",
"http_trace",
"//src/core:error",
"//src/core:hpack_constants",
"//src/core:no_destruct",
"//src/core:slice",
],
)
grpc_cc_library(
name = "hpack_parse_result",
srcs = [
"//src/core:ext/transport/chttp2/transport/hpack_parse_result.cc",
],
hdrs = [
"//src/core:ext/transport/chttp2/transport/hpack_parse_result.h",
],
external_deps = [
"absl/status",
"absl/strings",
"absl/strings:str_format",
"absl/types:optional",
],
deps = [
"gpr",
"grpc_base",
"//src/core:error",
"//src/core:hpack_constants",
"//src/core:slice",
"//src/core:status_helper",
],
)
grpc_cc_library(
name = "hpack_parser",
srcs = [
@ -3755,7 +3779,6 @@ grpc_cc_library(
"absl/base:core_headers",
"absl/status",
"absl/strings",
"absl/strings:str_format",
"absl/types:optional",
"absl/types:span",
"absl/types:variant",
@ -3767,16 +3790,17 @@ grpc_cc_library(
"grpc_base",
"grpc_public_hdrs",
"grpc_trace",
"hpack_parse_result",
"hpack_parser_table",
"stats",
"//src/core:decode_huff",
"//src/core:error",
"//src/core:hpack_constants",
"//src/core:match",
"//src/core:random_early_detection",
"//src/core:slice",
"//src/core:slice_refcount",
"//src/core:stats_data",
"//src/core:status_helper",
],
)

3
CMakeLists.txt generated

@ -1775,6 +1775,7 @@ add_library(grpc
src/core/ext/transport/chttp2/transport/frame_window_update.cc
src/core/ext/transport/chttp2/transport/hpack_encoder.cc
src/core/ext/transport/chttp2/transport/hpack_encoder_table.cc
src/core/ext/transport/chttp2/transport/hpack_parse_result.cc
src/core/ext/transport/chttp2/transport/hpack_parser.cc
src/core/ext/transport/chttp2/transport/hpack_parser_table.cc
src/core/ext/transport/chttp2/transport/http2_settings.cc
@ -2800,6 +2801,7 @@ add_library(grpc_unsecure
src/core/ext/transport/chttp2/transport/frame_window_update.cc
src/core/ext/transport/chttp2/transport/hpack_encoder.cc
src/core/ext/transport/chttp2/transport/hpack_encoder_table.cc
src/core/ext/transport/chttp2/transport/hpack_parse_result.cc
src/core/ext/transport/chttp2/transport/hpack_parser.cc
src/core/ext/transport/chttp2/transport/hpack_parser_table.cc
src/core/ext/transport/chttp2/transport/http2_settings.cc
@ -12460,6 +12462,7 @@ add_executable(frame_test
src/core/ext/transport/chttp2/transport/decode_huff.cc
src/core/ext/transport/chttp2/transport/hpack_encoder.cc
src/core/ext/transport/chttp2/transport/hpack_encoder_table.cc
src/core/ext/transport/chttp2/transport/hpack_parse_result.cc
src/core/ext/transport/chttp2/transport/hpack_parser.cc
src/core/ext/transport/chttp2/transport/hpack_parser_table.cc
src/core/ext/transport/chttp2/transport/http_trace.cc

2
Makefile generated

@ -1058,6 +1058,7 @@ LIBGRPC_SRC = \
src/core/ext/transport/chttp2/transport/frame_window_update.cc \
src/core/ext/transport/chttp2/transport/hpack_encoder.cc \
src/core/ext/transport/chttp2/transport/hpack_encoder_table.cc \
src/core/ext/transport/chttp2/transport/hpack_parse_result.cc \
src/core/ext/transport/chttp2/transport/hpack_parser.cc \
src/core/ext/transport/chttp2/transport/hpack_parser_table.cc \
src/core/ext/transport/chttp2/transport/http2_settings.cc \
@ -1936,6 +1937,7 @@ LIBGRPC_UNSECURE_SRC = \
src/core/ext/transport/chttp2/transport/frame_window_update.cc \
src/core/ext/transport/chttp2/transport/hpack_encoder.cc \
src/core/ext/transport/chttp2/transport/hpack_encoder_table.cc \
src/core/ext/transport/chttp2/transport/hpack_parse_result.cc \
src/core/ext/transport/chttp2/transport/hpack_parser.cc \
src/core/ext/transport/chttp2/transport/hpack_parser_table.cc \
src/core/ext/transport/chttp2/transport/http2_settings.cc \

2
Package.swift generated

@ -294,6 +294,8 @@ let package = Package(
"src/core/ext/transport/chttp2/transport/hpack_encoder.h",
"src/core/ext/transport/chttp2/transport/hpack_encoder_table.cc",
"src/core/ext/transport/chttp2/transport/hpack_encoder_table.h",
"src/core/ext/transport/chttp2/transport/hpack_parse_result.cc",
"src/core/ext/transport/chttp2/transport/hpack_parse_result.h",
"src/core/ext/transport/chttp2/transport/hpack_parser.cc",
"src/core/ext/transport/chttp2/transport/hpack_parser.h",
"src/core/ext/transport/chttp2/transport/hpack_parser_table.cc",

@ -299,6 +299,7 @@ libs:
- src/core/ext/transport/chttp2/transport/hpack_constants.h
- src/core/ext/transport/chttp2/transport/hpack_encoder.h
- src/core/ext/transport/chttp2/transport/hpack_encoder_table.h
- src/core/ext/transport/chttp2/transport/hpack_parse_result.h
- src/core/ext/transport/chttp2/transport/hpack_parser.h
- src/core/ext/transport/chttp2/transport/hpack_parser_table.h
- src/core/ext/transport/chttp2/transport/http2_settings.h
@ -1114,6 +1115,7 @@ libs:
- src/core/ext/transport/chttp2/transport/frame_window_update.cc
- src/core/ext/transport/chttp2/transport/hpack_encoder.cc
- src/core/ext/transport/chttp2/transport/hpack_encoder_table.cc
- src/core/ext/transport/chttp2/transport/hpack_parse_result.cc
- src/core/ext/transport/chttp2/transport/hpack_parser.cc
- src/core/ext/transport/chttp2/transport/hpack_parser_table.cc
- src/core/ext/transport/chttp2/transport/http2_settings.cc
@ -2012,6 +2014,7 @@ libs:
- src/core/ext/transport/chttp2/transport/hpack_constants.h
- src/core/ext/transport/chttp2/transport/hpack_encoder.h
- src/core/ext/transport/chttp2/transport/hpack_encoder_table.h
- src/core/ext/transport/chttp2/transport/hpack_parse_result.h
- src/core/ext/transport/chttp2/transport/hpack_parser.h
- src/core/ext/transport/chttp2/transport/hpack_parser_table.h
- src/core/ext/transport/chttp2/transport/http2_settings.h
@ -2430,6 +2433,7 @@ libs:
- src/core/ext/transport/chttp2/transport/frame_window_update.cc
- src/core/ext/transport/chttp2/transport/hpack_encoder.cc
- src/core/ext/transport/chttp2/transport/hpack_encoder_table.cc
- src/core/ext/transport/chttp2/transport/hpack_parse_result.cc
- src/core/ext/transport/chttp2/transport/hpack_parser.cc
- src/core/ext/transport/chttp2/transport/hpack_parser_table.cc
- src/core/ext/transport/chttp2/transport/http2_settings.cc
@ -8092,6 +8096,7 @@ targets:
- src/core/ext/transport/chttp2/transport/hpack_constants.h
- src/core/ext/transport/chttp2/transport/hpack_encoder.h
- src/core/ext/transport/chttp2/transport/hpack_encoder_table.h
- src/core/ext/transport/chttp2/transport/hpack_parse_result.h
- src/core/ext/transport/chttp2/transport/hpack_parser.h
- src/core/ext/transport/chttp2/transport/hpack_parser_table.h
- src/core/ext/transport/chttp2/transport/http_trace.h
@ -8375,6 +8380,7 @@ targets:
- src/core/ext/transport/chttp2/transport/decode_huff.cc
- src/core/ext/transport/chttp2/transport/hpack_encoder.cc
- src/core/ext/transport/chttp2/transport/hpack_encoder_table.cc
- src/core/ext/transport/chttp2/transport/hpack_parse_result.cc
- src/core/ext/transport/chttp2/transport/hpack_parser.cc
- src/core/ext/transport/chttp2/transport/hpack_parser_table.cc
- src/core/ext/transport/chttp2/transport/http_trace.cc

1
config.m4 generated

@ -137,6 +137,7 @@ if test "$PHP_GRPC" != "no"; then
src/core/ext/transport/chttp2/transport/frame_window_update.cc \
src/core/ext/transport/chttp2/transport/hpack_encoder.cc \
src/core/ext/transport/chttp2/transport/hpack_encoder_table.cc \
src/core/ext/transport/chttp2/transport/hpack_parse_result.cc \
src/core/ext/transport/chttp2/transport/hpack_parser.cc \
src/core/ext/transport/chttp2/transport/hpack_parser_table.cc \
src/core/ext/transport/chttp2/transport/http2_settings.cc \

1
config.w32 generated

@ -102,6 +102,7 @@ if (PHP_GRPC != "no") {
"src\\core\\ext\\transport\\chttp2\\transport\\frame_window_update.cc " +
"src\\core\\ext\\transport\\chttp2\\transport\\hpack_encoder.cc " +
"src\\core\\ext\\transport\\chttp2\\transport\\hpack_encoder_table.cc " +
"src\\core\\ext\\transport\\chttp2\\transport\\hpack_parse_result.cc " +
"src\\core\\ext\\transport\\chttp2\\transport\\hpack_parser.cc " +
"src\\core\\ext\\transport\\chttp2\\transport\\hpack_parser_table.cc " +
"src\\core\\ext\\transport\\chttp2\\transport\\http2_settings.cc " +

2
gRPC-C++.podspec generated

@ -369,6 +369,7 @@ Pod::Spec.new do |s|
'src/core/ext/transport/chttp2/transport/hpack_constants.h',
'src/core/ext/transport/chttp2/transport/hpack_encoder.h',
'src/core/ext/transport/chttp2/transport/hpack_encoder_table.h',
'src/core/ext/transport/chttp2/transport/hpack_parse_result.h',
'src/core/ext/transport/chttp2/transport/hpack_parser.h',
'src/core/ext/transport/chttp2/transport/hpack_parser_table.h',
'src/core/ext/transport/chttp2/transport/http2_settings.h',
@ -1414,6 +1415,7 @@ Pod::Spec.new do |s|
'src/core/ext/transport/chttp2/transport/hpack_constants.h',
'src/core/ext/transport/chttp2/transport/hpack_encoder.h',
'src/core/ext/transport/chttp2/transport/hpack_encoder_table.h',
'src/core/ext/transport/chttp2/transport/hpack_parse_result.h',
'src/core/ext/transport/chttp2/transport/hpack_parser.h',
'src/core/ext/transport/chttp2/transport/hpack_parser_table.h',
'src/core/ext/transport/chttp2/transport/http2_settings.h',

3
gRPC-Core.podspec generated

@ -395,6 +395,8 @@ Pod::Spec.new do |s|
'src/core/ext/transport/chttp2/transport/hpack_encoder.h',
'src/core/ext/transport/chttp2/transport/hpack_encoder_table.cc',
'src/core/ext/transport/chttp2/transport/hpack_encoder_table.h',
'src/core/ext/transport/chttp2/transport/hpack_parse_result.cc',
'src/core/ext/transport/chttp2/transport/hpack_parse_result.h',
'src/core/ext/transport/chttp2/transport/hpack_parser.cc',
'src/core/ext/transport/chttp2/transport/hpack_parser.h',
'src/core/ext/transport/chttp2/transport/hpack_parser_table.cc',
@ -2145,6 +2147,7 @@ Pod::Spec.new do |s|
'src/core/ext/transport/chttp2/transport/hpack_constants.h',
'src/core/ext/transport/chttp2/transport/hpack_encoder.h',
'src/core/ext/transport/chttp2/transport/hpack_encoder_table.h',
'src/core/ext/transport/chttp2/transport/hpack_parse_result.h',
'src/core/ext/transport/chttp2/transport/hpack_parser.h',
'src/core/ext/transport/chttp2/transport/hpack_parser_table.h',
'src/core/ext/transport/chttp2/transport/http2_settings.h',

2
grpc.gemspec generated

@ -300,6 +300,8 @@ Gem::Specification.new do |s|
s.files += %w( src/core/ext/transport/chttp2/transport/hpack_encoder.h )
s.files += %w( src/core/ext/transport/chttp2/transport/hpack_encoder_table.cc )
s.files += %w( src/core/ext/transport/chttp2/transport/hpack_encoder_table.h )
s.files += %w( src/core/ext/transport/chttp2/transport/hpack_parse_result.cc )
s.files += %w( src/core/ext/transport/chttp2/transport/hpack_parse_result.h )
s.files += %w( src/core/ext/transport/chttp2/transport/hpack_parser.cc )
s.files += %w( src/core/ext/transport/chttp2/transport/hpack_parser.h )
s.files += %w( src/core/ext/transport/chttp2/transport/hpack_parser_table.cc )

2
grpc.gyp generated

@ -362,6 +362,7 @@
'src/core/ext/transport/chttp2/transport/frame_window_update.cc',
'src/core/ext/transport/chttp2/transport/hpack_encoder.cc',
'src/core/ext/transport/chttp2/transport/hpack_encoder_table.cc',
'src/core/ext/transport/chttp2/transport/hpack_parse_result.cc',
'src/core/ext/transport/chttp2/transport/hpack_parser.cc',
'src/core/ext/transport/chttp2/transport/hpack_parser_table.cc',
'src/core/ext/transport/chttp2/transport/http2_settings.cc',
@ -1180,6 +1181,7 @@
'src/core/ext/transport/chttp2/transport/frame_window_update.cc',
'src/core/ext/transport/chttp2/transport/hpack_encoder.cc',
'src/core/ext/transport/chttp2/transport/hpack_encoder_table.cc',
'src/core/ext/transport/chttp2/transport/hpack_parse_result.cc',
'src/core/ext/transport/chttp2/transport/hpack_parser.cc',
'src/core/ext/transport/chttp2/transport/hpack_parser_table.cc',
'src/core/ext/transport/chttp2/transport/http2_settings.cc',

2
package.xml generated

@ -282,6 +282,8 @@
<file baseinstalldir="/" name="src/core/ext/transport/chttp2/transport/hpack_encoder.h" role="src" />
<file baseinstalldir="/" name="src/core/ext/transport/chttp2/transport/hpack_encoder_table.cc" role="src" />
<file baseinstalldir="/" name="src/core/ext/transport/chttp2/transport/hpack_encoder_table.h" role="src" />
<file baseinstalldir="/" name="src/core/ext/transport/chttp2/transport/hpack_parse_result.cc" role="src" />
<file baseinstalldir="/" name="src/core/ext/transport/chttp2/transport/hpack_parse_result.h" role="src" />
<file baseinstalldir="/" name="src/core/ext/transport/chttp2/transport/hpack_parser.cc" role="src" />
<file baseinstalldir="/" name="src/core/ext/transport/chttp2/transport/hpack_parser.h" role="src" />
<file baseinstalldir="/" name="src/core/ext/transport/chttp2/transport/hpack_parser_table.cc" role="src" />

@ -0,0 +1,176 @@
// Copyright 2023 gRPC authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <grpc/support/port_platform.h>
#include "src/core/ext/transport/chttp2/transport/hpack_parse_result.h"
#include <stddef.h>
#include <initializer_list>
#include "absl/strings/str_format.h"
#include "src/core/ext/transport/chttp2/transport/hpack_constants.h"
#include "src/core/lib/gprpp/status_helper.h"
#include "src/core/lib/iomgr/error.h"
#include "src/core/lib/slice/slice.h"
namespace grpc_core {
namespace {
class MetadataSizeLimitExceededEncoder {
public:
explicit MetadataSizeLimitExceededEncoder(std::string& summary)
: summary_(summary) {}
void Encode(const Slice& key, const Slice& value) {
AddToSummary(key.as_string_view(), value.size());
}
template <typename Key, typename Value>
void Encode(Key, const Value& value) {
AddToSummary(Key::key(), EncodedSizeOfKey(Key(), value));
}
private:
void AddToSummary(absl::string_view key,
size_t value_length) GPR_ATTRIBUTE_NOINLINE {
absl::StrAppend(&summary_, " ", key, ":",
hpack_constants::SizeForEntry(key.size(), value_length),
"B");
}
std::string& summary_;
};
absl::Status MakeStreamError(absl::Status error) {
GPR_DEBUG_ASSERT(!error.ok());
return grpc_error_set_int(std::move(error), StatusIntProperty::kStreamId, 0);
}
} // namespace
absl::Status HpackParseResult::Materialize() const {
if (materialized_status_.has_value()) return *materialized_status_;
materialized_status_ = BuildMaterialized();
return *materialized_status_;
}
absl::Status HpackParseResult::BuildMaterialized() const {
switch (status_.get()) {
case HpackParseStatus::kOk:
return absl::OkStatus();
case HpackParseStatus::kEof:
Crash("Materialize() called on EOF");
break;
case HpackParseStatus::kMovedFrom:
Crash("Materialize() called on moved-from object");
break;
case HpackParseStatus::kInvalidMetadata:
if (key_.empty()) {
return MakeStreamError(absl::InternalError(
ValidateMetadataResultToString(validate_metadata_result_)));
} else {
return MakeStreamError(absl::InternalError(absl::StrCat(
ValidateMetadataResultToString(validate_metadata_result_), ": ",
key_)));
}
case HpackParseStatus::kSoftMetadataLimitExceeded:
case HpackParseStatus::kHardMetadataLimitExceeded: {
const auto& e = metadata_limit_exceeded_;
// Collect a summary of sizes so far for debugging
// Do not collect contents, for fear of exposing PII.
std::string summary;
if (e.prior != nullptr) {
MetadataSizeLimitExceededEncoder encoder(summary);
e.prior->Encode(&encoder);
}
return MakeStreamError(absl::ResourceExhaustedError(absl::StrCat(
"received metadata size exceeds ",
status_.get() == HpackParseStatus::kSoftMetadataLimitExceeded
? "soft"
: "hard",
" limit (", e.frame_length, " vs. ", e.limit, ")",
summary.empty() ? "" : "; ", summary)));
}
case HpackParseStatus::kHardMetadataLimitExceededByKey: {
const auto& e = metadata_limit_exceeded_by_atom_;
return MakeStreamError(absl::ResourceExhaustedError(
absl::StrCat("received metadata size exceeds hard limit (key length ",
e.atom_length, " vs. ", e.limit, ")")));
}
case HpackParseStatus::kHardMetadataLimitExceededByValue: {
const auto& e = metadata_limit_exceeded_by_atom_;
return MakeStreamError(absl::ResourceExhaustedError(absl::StrCat(
"received metadata size exceeds hard limit (value length ",
e.atom_length, " vs. ", e.limit, ")")));
}
case HpackParseStatus::kMetadataParseError:
if (!key_.empty()) {
return MakeStreamError(absl::InternalError(
absl::StrCat("Error parsing '", key_, "' metadata")));
} else {
return MakeStreamError(absl::InternalError("Error parsing metadata"));
}
case HpackParseStatus::kUnbase64Failed:
if (!key_.empty()) {
return MakeStreamError(absl::InternalError(absl::StrCat(
"Error parsing '", key_, "' metadata: illegal base64 encoding")));
} else {
return MakeStreamError(absl::InternalError(
absl::StrCat("Failed base64 decoding metadata")));
}
case HpackParseStatus::kIncompleteHeaderAtBoundary:
return absl::InternalError(
"Incomplete header at the end of a header/continuation sequence");
case HpackParseStatus::kVarintOutOfRange:
return absl::InternalError(absl::StrFormat(
"integer overflow in hpack integer decoding: have 0x%08x, "
"got byte 0x%02x",
varint_out_of_range_.value, varint_out_of_range_.last_byte));
case HpackParseStatus::kIllegalTableSizeChange:
return absl::InternalError(absl::StrCat(
"Attempt to make hpack table ", illegal_table_size_change_.new_size,
" bytes when max is ", illegal_table_size_change_.max_size,
" bytes"));
case HpackParseStatus::kAddBeforeTableSizeUpdated:
return absl::InternalError(
absl::StrCat("HPACK max table size reduced to ",
illegal_table_size_change_.new_size,
" but not reflected by hpack stream (still at ",
illegal_table_size_change_.max_size, ")"));
case HpackParseStatus::kParseHuffFailed:
if (!key_.empty()) {
return absl::InternalError(
absl::StrCat("Failed huffman decoding '", key_, "' metadata"));
} else {
return absl::InternalError(
absl::StrCat("Failed huffman decoding metadata"));
}
break;
case HpackParseStatus::kTooManyDynamicTableSizeChanges:
return absl::InternalError(
"More than two max table size changes in a single frame");
case HpackParseStatus::kMaliciousVarintEncoding:
return absl::InternalError(
"Malicious varint encoding detected in HPACK stream");
case HpackParseStatus::kInvalidHpackIndex:
return absl::InternalError(absl::StrFormat(
"Invalid HPACK index received (%d)", invalid_hpack_index_));
case HpackParseStatus::kIllegalHpackOpCode:
return absl::InternalError("Illegal hpack op code");
}
GPR_UNREACHABLE_CODE(return absl::UnknownError("Should never reach here"));
}
} // namespace grpc_core

@ -0,0 +1,325 @@
// Copyright 2023 gRPC authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef GRPC_SRC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_HPACK_PARSE_RESULT_H
#define GRPC_SRC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_HPACK_PARSE_RESULT_H
#include <grpc/support/port_platform.h>
#include <stdint.h>
#include <string>
#include <utility>
#include "absl/status/status.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/string_view.h"
#include "absl/types/optional.h"
#include <grpc/support/log.h>
#include "src/core/lib/gprpp/crash.h"
#include "src/core/lib/surface/validate_metadata.h"
#include "src/core/lib/transport/metadata_batch.h"
namespace grpc_core {
// Result of parsing
// Makes it trivial to identify stream vs connection errors (via a range check)
enum class HpackParseStatus : uint8_t {
///////////////////////////////////////////////////////
// Non-Errors
// Parsed OK
kOk,
// Parse reached end of the current frame
kEof,
// Moved from - used to denote a HpackParseResult that has been moved into a
// different object, and so the original should be deemed invalid.
kMovedFrom,
///////////////////////////////////////////////////////////////////
// Stream Errors - result in a stream cancellation
// Sentinel value used to denote the first error that is a stream error.
// All stream errors are hence >= kFirstStreamError and <
// kFirstConnectionError.
// Should not be used in switch statements, instead the first error message
// after this one should be assigned the same value.
kFirstStreamError,
kInvalidMetadata = kFirstStreamError,
// Hard metadata limit exceeded by the total set of metadata
kHardMetadataLimitExceeded,
kSoftMetadataLimitExceeded,
// Hard metadata limit exceeded by a single key string
kHardMetadataLimitExceededByKey,
// Hard metadata limit exceeded by a single value string
kHardMetadataLimitExceededByValue,
kMetadataParseError,
// Parse failed due to a base64 decode error
kUnbase64Failed,
///////////////////////////////////////////////////////////////////
// Connection Errors - result in the tcp connection closing
// Sentinel value used to denote the first error that is a connection error.
// All connection errors are hence >= kFirstConnectionError.
// Should not be used in switch statements, instead the first error message
// after this one should be assigned the same value.
kFirstConnectionError,
// Incomplete header at end of header boundary
kIncompleteHeaderAtBoundary = kFirstConnectionError,
// Varint out of range
kVarintOutOfRange,
// Invalid HPACK index
kInvalidHpackIndex,
// Illegal HPACK table size change
kIllegalTableSizeChange,
// Trying to add to the hpack table prior to reducing after a settings change
kAddBeforeTableSizeUpdated,
// Parse failed due to a huffman decode error
kParseHuffFailed,
// Too many dynamic table size changes in one frame
kTooManyDynamicTableSizeChanges,
// Maliciously long varint encoding
// We don't read past 16 repeated 0x80 prefixes on a varint (all zeros)
// because no reasonable varint encoder would emit that (16 is already quite
// generous!)
// Because we stop reading we don't parse the rest of the bytes and so we
// can't recover parsing and would end up with a hpack table desync if we
// tried, so this is a connection error.
kMaliciousVarintEncoding,
// Illegal hpack op code
kIllegalHpackOpCode,
};
inline bool IsStreamError(HpackParseStatus status) {
return status >= HpackParseStatus::kFirstStreamError &&
status < HpackParseStatus::kFirstConnectionError;
}
inline bool IsConnectionError(HpackParseStatus status) {
return status >= HpackParseStatus::kFirstConnectionError;
}
inline bool IsEphemeralError(HpackParseStatus status) {
switch (status) {
case HpackParseStatus::kSoftMetadataLimitExceeded:
case HpackParseStatus::kHardMetadataLimitExceeded:
return true;
default:
return false;
}
}
class HpackParseResult {
public:
HpackParseResult() : HpackParseResult{HpackParseStatus::kOk} {}
bool ok() const { return status_.get() == HpackParseStatus::kOk; }
bool stream_error() const { return IsStreamError(status_.get()); }
bool connection_error() const { return IsConnectionError(status_.get()); }
bool ephemeral() const { return IsEphemeralError(status_.get()); }
HpackParseResult PersistentStreamErrorOrOk() const {
if (connection_error() || ephemeral()) return HpackParseResult();
return *this;
}
static HpackParseResult FromStatus(HpackParseStatus status) {
// Most statuses need some payloads, and we only need this functionality
// rarely - so allow list the statuses that we can include here.
switch (status) {
case HpackParseStatus::kUnbase64Failed:
case HpackParseStatus::kParseHuffFailed:
return HpackParseResult{status};
default:
Crash(
absl::StrCat("Invalid HpackParseStatus for FromStatus: ", status));
}
}
static HpackParseResult FromStatusWithKey(HpackParseStatus status,
absl::string_view key) {
auto r = FromStatus(status);
r.key_ = std::string(key);
return r;
}
static HpackParseResult MetadataParseError(absl::string_view key) {
HpackParseResult r{HpackParseStatus::kMetadataParseError};
r.key_ = std::string(key);
return r;
}
static HpackParseResult AddBeforeTableSizeUpdated(uint32_t current_size,
uint32_t max_size) {
HpackParseResult p{HpackParseStatus::kAddBeforeTableSizeUpdated};
p.illegal_table_size_change_ =
IllegalTableSizeChange{current_size, max_size};
return p;
}
static HpackParseResult MaliciousVarintEncodingError() {
return HpackParseResult{HpackParseStatus::kMaliciousVarintEncoding};
}
static HpackParseResult IllegalHpackOpCode() {
return HpackParseResult{HpackParseStatus::kIllegalHpackOpCode};
}
static HpackParseResult InvalidMetadataError(ValidateMetadataResult result,
absl::string_view key) {
GPR_DEBUG_ASSERT(result != ValidateMetadataResult::kOk);
HpackParseResult p{HpackParseStatus::kInvalidMetadata};
p.key_ = std::string(key);
p.validate_metadata_result_ = result;
return p;
}
static HpackParseResult IncompleteHeaderAtBoundaryError() {
return HpackParseResult{HpackParseStatus::kIncompleteHeaderAtBoundary};
}
static HpackParseResult VarintOutOfRangeError(uint32_t value,
uint8_t last_byte) {
HpackParseResult p{HpackParseStatus::kVarintOutOfRange};
p.varint_out_of_range_ = VarintOutOfRange{last_byte, value};
return p;
}
static HpackParseResult InvalidHpackIndexError(uint32_t index) {
HpackParseResult p{HpackParseStatus::kInvalidHpackIndex};
p.invalid_hpack_index_ = index;
return p;
}
static HpackParseResult IllegalTableSizeChangeError(uint32_t new_size,
uint32_t max_size) {
HpackParseResult p{HpackParseStatus::kIllegalTableSizeChange};
p.illegal_table_size_change_ = IllegalTableSizeChange{new_size, max_size};
return p;
}
static HpackParseResult TooManyDynamicTableSizeChangesError() {
return HpackParseResult{HpackParseStatus::kTooManyDynamicTableSizeChanges};
}
static HpackParseResult SoftMetadataLimitExceededError(
grpc_metadata_batch* metadata, uint32_t frame_length, uint32_t limit) {
HpackParseResult p{HpackParseStatus::kSoftMetadataLimitExceeded};
p.metadata_limit_exceeded_ =
MetadataLimitExceeded{frame_length, limit, metadata};
return p;
}
static HpackParseResult HardMetadataLimitExceededError(
grpc_metadata_batch* metadata, uint32_t frame_length, uint32_t limit) {
HpackParseResult p{HpackParseStatus::kHardMetadataLimitExceeded};
p.metadata_limit_exceeded_ =
MetadataLimitExceeded{frame_length, limit, metadata};
return p;
}
static HpackParseResult HardMetadataLimitExceededByKeyError(
uint32_t key_length, uint32_t limit) {
HpackParseResult p{HpackParseStatus::kHardMetadataLimitExceededByKey};
p.metadata_limit_exceeded_by_atom_ =
MetadataLimitExceededByAtom{key_length, limit};
return p;
}
static HpackParseResult HardMetadataLimitExceededByValueError(
absl::string_view key, uint32_t value_length, uint32_t limit) {
HpackParseResult p{HpackParseStatus::kHardMetadataLimitExceededByValue};
p.metadata_limit_exceeded_by_atom_ =
MetadataLimitExceededByAtom{value_length, limit};
p.key_ = std::string(key);
return p;
}
// Compute the absl::Status that goes along with this HpackParseResult.
// (may be cached, so this is not thread safe)
absl::Status Materialize() const;
private:
explicit HpackParseResult(HpackParseStatus status) : status_(status) {}
absl::Status BuildMaterialized() const;
struct VarintOutOfRange {
uint8_t last_byte;
uint32_t value;
};
struct MetadataLimitExceeded {
uint32_t frame_length;
uint32_t limit;
grpc_metadata_batch* prior;
};
// atom here means one of either a key or a value - so this is used for when a
// metadata limit is consumed by either of these.
struct MetadataLimitExceededByAtom {
uint32_t atom_length;
uint32_t limit;
};
struct IllegalTableSizeChange {
uint32_t new_size;
uint32_t max_size;
};
class StatusWrapper {
public:
explicit StatusWrapper(HpackParseStatus status) : status_(status) {}
StatusWrapper(const StatusWrapper&) = default;
StatusWrapper& operator=(const StatusWrapper&) = default;
StatusWrapper(StatusWrapper&& other) noexcept
: status_(std::exchange(other.status_, HpackParseStatus::kMovedFrom)) {}
StatusWrapper& operator=(StatusWrapper&& other) noexcept {
status_ = std::exchange(other.status_, HpackParseStatus::kMovedFrom);
return *this;
}
HpackParseStatus get() const { return status_; }
private:
HpackParseStatus status_;
};
StatusWrapper status_;
union {
// Set if status == kInvalidMetadata
ValidateMetadataResult validate_metadata_result_;
// Set if status == kVarintOutOfRange
VarintOutOfRange varint_out_of_range_;
// Set if status == kInvalidHpackIndex
uint32_t invalid_hpack_index_;
// Set if status == kHardMetadataLimitExceeded or
// kSoftMetadataLimitExceeded
MetadataLimitExceeded metadata_limit_exceeded_;
// Set if status == kHardMetadataLimitExceededByKey or
// kHardMetadataLimitExceededByValue
MetadataLimitExceededByAtom metadata_limit_exceeded_by_atom_;
// Set if status == kIllegalTableSizeChange
IllegalTableSizeChange illegal_table_size_change_;
};
std::string key_;
mutable absl::optional<absl::Status> materialized_status_;
};
} // namespace grpc_core
#endif // GRPC_SRC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_HPACK_PARSE_RESULT_H

File diff suppressed because it is too large Load Diff

@ -21,16 +21,28 @@
#include <grpc/support/port_platform.h>
#include <stddef.h>
#include <stdint.h>
#include <string>
#include <utility>
#include <vector>
#include "absl/strings/str_cat.h"
#include "absl/strings/string_view.h"
#include "absl/types/optional.h"
#include "absl/types/span.h"
#include "absl/types/variant.h"
#include <grpc/slice.h>
#include "src/core/ext/transport/chttp2/transport/frame.h"
#include "src/core/ext/transport/chttp2/transport/hpack_parse_result.h"
#include "src/core/ext/transport/chttp2/transport/hpack_parser_table.h"
#include "src/core/lib/backoff/random_early_detection.h"
#include "src/core/lib/iomgr/error.h"
#include "src/core/lib/slice/slice.h"
#include "src/core/lib/slice/slice_refcount.h"
#include "src/core/lib/transport/metadata_batch.h"
// IWYU pragma: no_include <type_traits>
@ -92,17 +104,150 @@ class HPackParser {
void FinishFrame();
// Retrieve the associated hpack table (for tests, debugging)
HPackTable* hpack_table() { return &table_; }
HPackTable* hpack_table() { return &state_.hpack_table; }
// Is the current frame a boundary of some sort
bool is_boundary() const { return boundary_ != Boundary::None; }
// Is the current frame the end of a stream
bool is_eof() const { return boundary_ == Boundary::EndOfStream; }
// How many bytes are buffered (for tests to assert on)
size_t buffered_bytes() const { return unparsed_bytes_.size(); }
private:
// Helper classes: see implementation
class Parser;
class Input;
class String;
// Helper to parse a string and turn it into a slice with appropriate memory
// management characteristics
class String {
public:
// StringResult carries both a HpackParseStatus and the parsed string
struct StringResult;
String() : value_(absl::Span<const uint8_t>()) {}
String(const String&) = delete;
String& operator=(const String&) = delete;
String(String&& other) noexcept : value_(std::move(other.value_)) {
other.value_ = absl::Span<const uint8_t>();
}
String& operator=(String&& other) noexcept {
value_ = std::move(other.value_);
other.value_ = absl::Span<const uint8_t>();
return *this;
}
// Take the value and leave this empty
Slice Take();
// Return a reference to the value as a string view
absl::string_view string_view() const;
// Parse a non-binary string
static StringResult Parse(Input* input, bool is_huff, size_t length);
// Parse a binary string
static StringResult ParseBinary(Input* input, bool is_huff, size_t length);
private:
void AppendBytes(const uint8_t* data, size_t length);
explicit String(std::vector<uint8_t> v) : value_(std::move(v)) {}
explicit String(absl::Span<const uint8_t> v) : value_(v) {}
String(grpc_slice_refcount* r, const uint8_t* begin, const uint8_t* end)
: value_(Slice::FromRefcountAndBytes(r, begin, end)) {}
// Parse some huffman encoded bytes, using output(uint8_t b) to emit each
// decoded byte.
template <typename Out>
static HpackParseStatus ParseHuff(Input* input, uint32_t length,
Out output);
// Parse some uncompressed string bytes.
static StringResult ParseUncompressed(Input* input, uint32_t length,
uint32_t wire_size);
// Turn base64 encoded bytes into not base64 encoded bytes.
static StringResult Unbase64(String s);
// Main loop for Unbase64
static absl::optional<std::vector<uint8_t>> Unbase64Loop(
const uint8_t* cur, const uint8_t* end);
absl::variant<Slice, absl::Span<const uint8_t>, std::vector<uint8_t>>
value_;
};
// Prefix for a string
struct StringPrefix {
// Number of bytes in input for string
uint32_t length;
// Is it huffman compressed
bool huff;
std::string ToString() const {
return absl::StrCat(length, " bytes ",
huff ? "huffman compressed" : "uncompressed");
}
};
// Current parse state
// ┌───┐
// │Top│
// └┬─┬┘
// │┌▽────────────────┐
// ││ParsingKeyLength │
// │└┬───────────────┬┘
// │┌▽─────────────┐┌▽──────────────┐
// ││ParsingKeyBody││SkippingKeyBody│
// │└┬─────────────┘└───┬───────────┘
// ┌▽─▽────────────────┐┌▽──────────────────┐
// │ParsingValueLength ││SkippingValueLength│
// └┬─────────────────┬┘└┬──────────────────┘
// ┌▽───────────────┐┌▽──▽─────────────┐
// │ParsingValueBody││SkippingValueBody│
// └────────────────┘└─────────────────┘
enum class ParseState : uint8_t {
// Start of one opcode
kTop,
// Parsing a literal keys length
kParsingKeyLength,
// Parsing a literal key
kParsingKeyBody,
// Skipping a literal key
kSkippingKeyBody,
// Parsing a literal value length
kParsingValueLength,
// Parsing a literal value
kParsingValueBody,
// Reading a literal value length (so we can skip it)
kSkippingValueLength,
// Skipping a literal value
kSkippingValueBody,
};
// Shared state for Parser instances between slices.
struct InterSliceState {
HPackTable hpack_table;
// Error so far for this frame (set by class Input)
HpackParseResult frame_error;
// Length of frame so far.
uint32_t frame_length = 0;
// Length of the string being parsed
uint32_t string_length;
// How many more dynamic table updates are allowed
uint8_t dynamic_table_updates_allowed;
// Current parse state
ParseState parse_state = ParseState::kTop;
// RED for overly large metadata sets
RandomEarlyDetection metadata_early_detection;
// Should the current header be added to the hpack table?
bool add_to_table;
// Is the string being parsed huffman compressed?
bool is_string_huff_compressed;
// Is the value being parsed binary?
bool is_binary_header;
absl::variant<const HPackTable::Memento*, Slice> key;
};
grpc_error_handle ParseInput(Input input, bool is_last);
void ParseInputInner(Input* input);
@ -114,6 +259,8 @@ class HPackParser {
// Bytes that could not be parsed last parsing round
std::vector<uint8_t> unparsed_bytes_;
// How many bytes would be needed before progress could be made?
size_t min_progress_size_ = 0;
// Buffer kind of boundary
// TODO(ctiller): see if we can move this argument to Parse, and avoid
// buffering.
@ -122,15 +269,9 @@ class HPackParser {
// TODO(ctiller): see if we can move this argument to Parse, and avoid
// buffering.
Priority priority_;
uint8_t dynamic_table_updates_allowed_;
// Length of frame so far.
uint32_t frame_length_;
RandomEarlyDetection metadata_early_detection_;
// Information for logging
LogInfo log_info_;
// hpack table
HPackTable table_;
InterSliceState state_;
};
} // namespace grpc_core

@ -25,16 +25,16 @@
#include <algorithm>
#include <cstddef>
#include <cstring>
#include <initializer_list>
#include <utility>
#include "absl/status/status.h"
#include "absl/strings/str_format.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/string_view.h"
#include <grpc/support/log.h>
#include "src/core/ext/transport/chttp2/transport/hpack_constants.h"
#include "src/core/ext/transport/chttp2/transport/hpack_parse_result.h"
#include "src/core/ext/transport/chttp2/transport/http_trace.h"
#include "src/core/lib/debug/trace.h"
#include "src/core/lib/slice/slice.h"
@ -80,6 +80,14 @@ void HPackTable::MementoRingBuffer::Rebuild(uint32_t max_entries) {
entries_.swap(entries);
}
void HPackTable::MementoRingBuffer::ForEach(
absl::FunctionRef<void(uint32_t, const Memento&)> f) const {
uint32_t index = 0;
while (auto* m = Lookup(index++)) {
f(index, *m);
}
}
// Evict one element from the table
void HPackTable::EvictOne() {
auto first_entry = entries_.PopOne();
@ -100,15 +108,9 @@ void HPackTable::SetMaxBytes(uint32_t max_bytes) {
max_bytes_ = max_bytes;
}
grpc_error_handle HPackTable::SetCurrentTableSize(uint32_t bytes) {
if (current_table_bytes_ == bytes) {
return absl::OkStatus();
}
if (bytes > max_bytes_) {
return absl::InternalError(absl::StrFormat(
"Attempt to make hpack table %d bytes when max is %d bytes", bytes,
max_bytes_));
}
bool HPackTable::SetCurrentTableSize(uint32_t bytes) {
if (current_table_bytes_ == bytes) return true;
if (bytes > max_bytes_) return false;
if (GRPC_TRACE_FLAG_ENABLED(grpc_http_trace)) {
gpr_log(GPR_INFO, "Update hpack parser table size to %d", bytes);
}
@ -119,30 +121,16 @@ grpc_error_handle HPackTable::SetCurrentTableSize(uint32_t bytes) {
uint32_t new_cap = std::max(hpack_constants::EntriesForBytes(bytes),
hpack_constants::kInitialTableEntries);
entries_.Rebuild(new_cap);
return absl::OkStatus();
return true;
}
grpc_error_handle HPackTable::Add(Memento md) {
if (current_table_bytes_ > max_bytes_) {
return GRPC_ERROR_CREATE(absl::StrFormat(
"HPACK max table size reduced to %d but not reflected by hpack "
"stream (still at %d)",
max_bytes_, current_table_bytes_));
}
bool HPackTable::Add(Memento md) {
if (current_table_bytes_ > max_bytes_) return false;
// we can't add elements bigger than the max table size
if (md.md.transport_size() > current_table_bytes_) {
// HPACK draft 10 section 4.4 states:
// If the size of the new entry is less than or equal to the maximum
// size, that entry is added to the table. It is not an error to
// attempt to add an entry that is larger than the maximum size; an
// attempt to add an entry larger than the entire table causes
// the table to be emptied of all existing entries, and results in an
// empty table.
while (entries_.num_entries()) {
EvictOne();
}
return absl::OkStatus();
AddLargerThanCurrentTableSize();
return true;
}
// evict entries to ensure no overflow
@ -154,7 +142,33 @@ grpc_error_handle HPackTable::Add(Memento md) {
// copy the finalized entry in
mem_used_ += md.md.transport_size();
entries_.Put(std::move(md));
return absl::OkStatus();
return true;
}
void HPackTable::AddLargerThanCurrentTableSize() {
// HPACK draft 10 section 4.4 states:
// If the size of the new entry is less than or equal to the maximum
// size, that entry is added to the table. It is not an error to
// attempt to add an entry that is larger than the maximum size; an
// attempt to add an entry larger than the entire table causes
// the table to be emptied of all existing entries, and results in an
// empty table.
while (entries_.num_entries()) {
EvictOne();
}
}
std::string HPackTable::TestOnlyDynamicTableAsString() const {
std::string out;
entries_.ForEach([&out](uint32_t i, const Memento& m) {
if (m.parse_status.ok()) {
absl::StrAppend(&out, i, ": ", m.md.DebugString(), "\n");
} else {
absl::StrAppend(&out, i, ": ", m.parse_status.Materialize().ToString(),
"\n");
}
});
return out;
}
namespace {
@ -236,7 +250,7 @@ HPackTable::Memento MakeMemento(size_t i) {
[](absl::string_view, const Slice&) {
abort(); // not expecting to see this
}),
absl::OkStatus()};
HpackParseResult()};
}
} // namespace

@ -23,13 +23,14 @@
#include <stdint.h>
#include <string>
#include <vector>
#include "absl/status/status.h"
#include "absl/functional/function_ref.h"
#include "src/core/ext/transport/chttp2/transport/hpack_constants.h"
#include "src/core/ext/transport/chttp2/transport/hpack_parse_result.h"
#include "src/core/lib/gprpp/no_destruct.h"
#include "src/core/lib/iomgr/error.h"
#include "src/core/lib/transport/metadata_batch.h"
#include "src/core/lib/transport/parsed_metadata.h"
@ -45,11 +46,12 @@ class HPackTable {
HPackTable& operator=(const HPackTable&) = delete;
void SetMaxBytes(uint32_t max_bytes);
grpc_error_handle SetCurrentTableSize(uint32_t bytes);
bool SetCurrentTableSize(uint32_t bytes);
uint32_t current_table_size() { return current_table_bytes_; }
struct Memento {
ParsedMetadata<grpc_metadata_batch> md;
absl::Status parse_status;
HpackParseResult parse_status;
};
// Lookup, but don't ref.
@ -68,7 +70,8 @@ class HPackTable {
}
// add a table entry to the index
grpc_error_handle Add(Memento md) GRPC_MUST_USE_RESULT;
bool Add(Memento md) GRPC_MUST_USE_RESULT;
void AddLargerThanCurrentTableSize();
// Current entry count in the table.
uint32_t num_entries() const { return entries_.num_entries(); }
@ -76,6 +79,13 @@ class HPackTable {
// Current size of the table.
uint32_t test_only_table_size() const { return mem_used_; }
// Maximum allowed size of the table currently
uint32_t max_bytes() const { return max_bytes_; }
uint32_t current_table_bytes() const { return current_table_bytes_; }
// Dynamic table entries, stringified
std::string TestOnlyDynamicTableAsString() const;
private:
struct StaticMementos {
StaticMementos();
@ -98,6 +108,9 @@ class HPackTable {
// Lookup the entry at index, or return nullptr if none exists.
const Memento* Lookup(uint32_t index) const;
void ForEach(absl::FunctionRef<void(uint32_t dynamic_index, const Memento&)>
f) const;
uint32_t max_entries() const { return max_entries_; }
uint32_t num_entries() const { return num_entries_; }

@ -23,7 +23,6 @@
#include <initializer_list>
#include <string>
#include <utility>
#include "absl/base/attributes.h"
#include "absl/status/status.h"
@ -473,8 +472,8 @@ static HPackParser::LogInfo hpack_parser_log_info(
}
static grpc_error_handle init_header_skip_frame_parser(
grpc_chttp2_transport* t, HPackParser::Priority priority_type) {
bool is_eoh = t->expect_continuation_stream_id != 0;
grpc_chttp2_transport* t, HPackParser::Priority priority_type,
bool is_eoh) {
t->parser = grpc_chttp2_transport::Parser{
"header", grpc_chttp2_header_parser_parse, &t->hpack_parser};
t->hpack_parser.BeginFrame(
@ -598,7 +597,7 @@ static grpc_error_handle init_header_frame_parser(grpc_chttp2_transport* t,
GRPC_CHTTP2_IF_TRACING(
gpr_log(GPR_ERROR,
"grpc_chttp2_stream disbanded before CONTINUATION received"));
return init_header_skip_frame_parser(t, priority_type);
return init_header_skip_frame_parser(t, priority_type, is_eoh);
}
if (t->is_client) {
if (GPR_LIKELY((t->incoming_stream_id & 1) &&
@ -608,7 +607,7 @@ static grpc_error_handle init_header_frame_parser(grpc_chttp2_transport* t,
GRPC_CHTTP2_IF_TRACING(gpr_log(
GPR_ERROR, "ignoring new grpc_chttp2_stream creation on client"));
}
return init_header_skip_frame_parser(t, priority_type);
return init_header_skip_frame_parser(t, priority_type, is_eoh);
} else if (GPR_UNLIKELY(t->last_new_stream_id >= t->incoming_stream_id)) {
GRPC_CHTTP2_IF_TRACING(gpr_log(
GPR_ERROR,
@ -616,13 +615,13 @@ static grpc_error_handle init_header_frame_parser(grpc_chttp2_transport* t,
"last grpc_chttp2_stream "
"id=%d, new grpc_chttp2_stream id=%d",
t->last_new_stream_id, t->incoming_stream_id));
return init_header_skip_frame_parser(t, priority_type);
return init_header_skip_frame_parser(t, priority_type, is_eoh);
} else if (GPR_UNLIKELY((t->incoming_stream_id & 1) == 0)) {
GRPC_CHTTP2_IF_TRACING(gpr_log(
GPR_ERROR,
"ignoring grpc_chttp2_stream with non-client generated index %d",
t->incoming_stream_id));
return init_header_skip_frame_parser(t, priority_type);
return init_header_skip_frame_parser(t, priority_type, is_eoh);
} else if (GPR_UNLIKELY(
grpc_chttp2_stream_map_size(&t->stream_map) >=
t->settings[GRPC_ACKED_SETTINGS]
@ -637,7 +636,7 @@ static grpc_error_handle init_header_frame_parser(grpc_chttp2_transport* t,
"grpc_chttp2_stream request id=%d, last grpc_chttp2_stream id=%d",
t, std::string(t->peer_string.as_string_view()).c_str(),
t->incoming_stream_id, t->last_new_stream_id));
return init_header_skip_frame_parser(t, priority_type);
return init_header_skip_frame_parser(t, priority_type, is_eoh);
}
t->last_new_stream_id = t->incoming_stream_id;
s = t->incoming_stream =
@ -645,7 +644,7 @@ static grpc_error_handle init_header_frame_parser(grpc_chttp2_transport* t,
if (GPR_UNLIKELY(s == nullptr)) {
GRPC_CHTTP2_IF_TRACING(
gpr_log(GPR_ERROR, "grpc_chttp2_stream not accepted"));
return init_header_skip_frame_parser(t, priority_type);
return init_header_skip_frame_parser(t, priority_type, is_eoh);
}
if (GRPC_TRACE_FLAG_ENABLED(grpc_http_trace) ||
GRPC_TRACE_FLAG_ENABLED(grpc_trace_chttp2_new_stream)) {
@ -665,7 +664,7 @@ static grpc_error_handle init_header_frame_parser(grpc_chttp2_transport* t,
GRPC_CHTTP2_IF_TRACING(gpr_log(
GPR_ERROR, "skipping already closed grpc_chttp2_stream header"));
t->incoming_stream = nullptr;
return init_header_skip_frame_parser(t, priority_type);
return init_header_skip_frame_parser(t, priority_type, is_eoh);
}
t->parser = grpc_chttp2_transport::Parser{
"header", grpc_chttp2_header_parser_parse, &t->hpack_parser};
@ -698,7 +697,7 @@ static grpc_error_handle init_header_frame_parser(grpc_chttp2_transport* t,
break;
case 2:
gpr_log(GPR_ERROR, "too many header frames received");
return init_header_skip_frame_parser(t, priority_type);
return init_header_skip_frame_parser(t, priority_type, is_eoh);
}
if (frame_type == HPackParser::LogInfo::kTrailers && !t->header_eof) {
return GRPC_ERROR_CREATE(
@ -826,8 +825,9 @@ static grpc_error_handle parse_frame_slice(grpc_chttp2_transport* t,
&unused)) {
grpc_chttp2_parsing_become_skip_parser(t);
if (s) {
grpc_chttp2_cancel_stream(t, s, std::exchange(err, absl::OkStatus()));
grpc_chttp2_cancel_stream(t, s, err);
}
return absl::OkStatus();
}
return err;
}

@ -43,6 +43,11 @@ class RandomEarlyDetection {
uint64_t soft_limit() const { return soft_limit_; }
uint64_t hard_limit() const { return hard_limit_; }
void SetLimits(uint64_t soft_limit, uint64_t hard_limit) {
soft_limit_ = soft_limit;
hard_limit_ = hard_limit;
}
private:
// The soft limit is the size at which we start rejecting items with a
// probability that increases linearly to 1 as the size approaches the hard

@ -21,8 +21,6 @@
#include "src/core/lib/surface/validate_metadata.h"
#include "absl/status/status.h"
#include "absl/strings/escaping.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/string_view.h"
#include <grpc/grpc.h>
@ -46,32 +44,49 @@ class LegalHeaderKeyBits : public BitSet<256> {
};
constexpr LegalHeaderKeyBits g_legal_header_key_bits;
GPR_ATTRIBUTE_NOINLINE
absl::Status DoesNotConformTo(absl::string_view x, const char* err_desc) {
return absl::InternalError(absl::StrCat(err_desc, ": ", x, " (hex ",
absl::BytesToHexString(x), ")"));
}
absl::Status ConformsTo(absl::string_view x, const BitSet<256>& legal_bits,
const char* err_desc) {
ValidateMetadataResult ConformsTo(absl::string_view x,
const BitSet<256>& legal_bits,
ValidateMetadataResult error) {
for (uint8_t c : x) {
if (!legal_bits.is_set(c)) {
return DoesNotConformTo(x, err_desc);
return error;
}
}
return absl::OkStatus();
return ValidateMetadataResult::kOk;
}
absl::Status UpgradeToStatus(ValidateMetadataResult result) {
if (result == ValidateMetadataResult::kOk) return absl::OkStatus();
return absl::InternalError(ValidateMetadataResultToString(result));
}
} // namespace
absl::Status ValidateHeaderKeyIsLegal(absl::string_view key) {
ValidateMetadataResult ValidateHeaderKeyIsLegal(absl::string_view key) {
if (key.empty()) {
return absl::InternalError("Metadata keys cannot be zero length");
return ValidateMetadataResult::kCannotBeZeroLength;
}
if (key.size() > UINT32_MAX) {
return absl::InternalError(
"Metadata keys cannot be larger than UINT32_MAX");
return ValidateMetadataResult::kTooLong;
}
return ConformsTo(key, g_legal_header_key_bits,
ValidateMetadataResult::kIllegalHeaderKey);
}
const char* ValidateMetadataResultToString(ValidateMetadataResult result) {
switch (result) {
case ValidateMetadataResult::kOk:
return "Ok";
case ValidateMetadataResult::kCannotBeZeroLength:
return "Metadata keys cannot be zero length";
case ValidateMetadataResult::kTooLong:
return "Metadata keys cannot be larger than UINT32_MAX";
case ValidateMetadataResult::kIllegalHeaderKey:
return "Illegal header key";
case ValidateMetadataResult::kIllegalHeaderValue:
return "Illegal header value";
}
return ConformsTo(key, g_legal_header_key_bits, "Illegal header key");
GPR_UNREACHABLE_CODE(return "Unknown");
}
} // namespace grpc_core
@ -82,8 +97,8 @@ static int error2int(grpc_error_handle error) {
}
grpc_error_handle grpc_validate_header_key_is_legal(const grpc_slice& slice) {
return grpc_core::ValidateHeaderKeyIsLegal(
grpc_core::StringViewFromSlice(slice));
return grpc_core::UpgradeToStatus(grpc_core::ValidateHeaderKeyIsLegal(
grpc_core::StringViewFromSlice(slice)));
}
int grpc_header_key_is_legal(grpc_slice slice) {
@ -104,9 +119,9 @@ constexpr LegalHeaderNonBinValueBits g_legal_header_non_bin_value_bits;
grpc_error_handle grpc_validate_header_nonbin_value_is_legal(
const grpc_slice& slice) {
return grpc_core::ConformsTo(grpc_core::StringViewFromSlice(slice),
g_legal_header_non_bin_value_bits,
"Illegal header value");
return grpc_core::UpgradeToStatus(grpc_core::ConformsTo(
grpc_core::StringViewFromSlice(slice), g_legal_header_non_bin_value_bits,
grpc_core::ValidateMetadataResult::kIllegalHeaderValue));
}
int grpc_header_nonbin_value_is_legal(grpc_slice slice) {

@ -25,7 +25,6 @@
#include <cstring>
#include "absl/status/status.h"
#include "absl/strings/string_view.h"
#include <grpc/slice.h>
@ -35,9 +34,20 @@
namespace grpc_core {
absl::Status ValidateHeaderKeyIsLegal(absl::string_view key);
enum class ValidateMetadataResult : uint8_t {
kOk,
kCannotBeZeroLength,
kTooLong,
kIllegalHeaderKey,
kIllegalHeaderValue
};
}
const char* ValidateMetadataResultToString(ValidateMetadataResult result);
// Returns nullopt if the key is legal, otherwise returns an error message.
ValidateMetadataResult ValidateHeaderKeyIsLegal(absl::string_view key);
} // namespace grpc_core
grpc_error_handle grpc_validate_header_key_is_legal(const grpc_slice& slice);
grpc_error_handle grpc_validate_header_nonbin_value_is_legal(

@ -111,6 +111,7 @@ CORE_SOURCE_FILES = [
'src/core/ext/transport/chttp2/transport/frame_window_update.cc',
'src/core/ext/transport/chttp2/transport/hpack_encoder.cc',
'src/core/ext/transport/chttp2/transport/hpack_encoder_table.cc',
'src/core/ext/transport/chttp2/transport/hpack_parse_result.cc',
'src/core/ext/transport/chttp2/transport/hpack_parser.cc',
'src/core/ext/transport/chttp2/transport/hpack_parser_table.cc',
'src/core/ext/transport/chttp2/transport/http2_settings.cc',

@ -26,6 +26,7 @@ grpc_proto_fuzzer(
corpus = "hpack_parser_corpus",
proto = "hpack_parser_fuzzer.proto",
tags = ["no_windows"],
uses_polling = False,
deps = [
"//:grpc",
"//test/core/util:grpc_test_util",
@ -38,6 +39,7 @@ grpc_proto_fuzzer(
corpus = "hpack_sync_corpus",
proto = "hpack_sync_fuzzer.proto",
tags = ["no_windows"],
uses_polling = False,
deps = [
"//:grpc",
"//test/core/util:grpc_test_util",
@ -71,6 +73,7 @@ grpc_fuzzer(
"absl/status",
],
tags = ["no_windows"],
uses_polling = False,
deps = [
"//:grpc",
"//test/core/util:grpc_test_util",

@ -0,0 +1,5 @@
frames {
max_metadata_length: 4096
parse: "\244\244\020\007\360\244\017-bin\213#Z)\244(-\244\016b\244\t\020\007\360\244\017-bin?\360\213c[)(-\177ni\'\'bin!!\t!/\244!?\037\'\360!"
absolute_max_metadata_length: 1073741824
}

@ -0,0 +1,5 @@
frames {
max_metadata_length: 4096
parse: "\244\037!\203\360\361\244\037!\203\333\360\261\360c"
absolute_max_metadata_length: 1073741824
}

@ -0,0 +1,16 @@
frames {
parse: "*\244\020\007\360\244\017-bin\203c\035\037\000\'[\360i(bn-!?\244\037\333\360!(!\\\360\tc"
parse: "\244\244\020\007\360\244\017-bin\213c[)(-\'bin\t;!!?\244\037\333\360!\020\007\360{(-bin\360\t!\\\t!\345\037\351\033;?G\355[((!!\\\360"
}
frames {
max_metadata_length: 12
parse: "*\244\020\007\360\244\017-bin\203c\035\037\000\'[\360i(bn-!?\244\037\333\360!(!\\\360\tc"
parse: "\244\244\020\007\360\244\017-bin\213c[)(-\'bin\t;!!?\244\037\333\360!\020\007\360{(-bin\360\t!\\\t!\345\037\351\033;?G\355[((!!\\\360"
parse: "\244\244\020\007\360\244\017-bin\213c[)(-\'bin\t;!!?\244\037\333\360!\020\007\360{(-bin\360\t!\\\t!\345\037\351\033;?G\355[((!!\\\360"
parse: "\244\244\020\007\360\244\017-bin\213c[)(-\'bin\t;!!?\244\037\333\360!\020\007\360{(-bin\360\t!\\\t!\345\037\351\033;?G\355[((!!\\\360"
absolute_max_metadata_length: 12
}
frames {
parse: "*\244\020\007\360\244\017-bin\203c\035\037\000\'[\360i(bn-!?\244\037\333\360!(!\\\360\tc"
parse: "\244\244\020\007\360\244\017-bin\213c[)(-\'bin\t;!!?\244\037\333\360!\020\007\360{(-bin\360\t!\\\t!\345\037\351\033;?G\355[((!!\\\360"
}

@ -0,0 +1,5 @@
frames {
max_metadata_length: 4096
parse: "(??\201;[(\244(\'?\244\007"
absolute_max_metadata_length: 1073741824
}

@ -16,17 +16,23 @@
//
//
#include <stdint.h>
#include <algorithm>
#include <memory>
#include <string>
#include <utility>
#include "absl/cleanup/cleanup.h"
#include <grpc/grpc.h>
#include <grpc/slice.h>
#include <grpc/support/log.h>
#include "src/core/ext/transport/chttp2/transport/hpack_parser.h"
#include "src/core/lib/gprpp/ref_counted_ptr.h"
#include "src/core/lib/gprpp/status_helper.h"
#include "src/core/lib/iomgr/error.h"
#include "src/core/lib/iomgr/exec_ctx.h"
#include "src/core/lib/resource_quota/arena.h"
#include "src/core/lib/resource_quota/memory_quota.h"
@ -45,40 +51,55 @@ static void dont_log(gpr_log_func_args* /*args*/) {}
DEFINE_PROTO_FUZZER(const hpack_parser_fuzzer::Msg& msg) {
if (squelch) gpr_set_log_function(dont_log);
grpc_init();
auto cleanup = absl::MakeCleanup(grpc_shutdown);
auto memory_allocator = grpc_core::ResourceQuota::Default()
->memory_quota()
->CreateMemoryAllocator("test-allocator");
{
std::unique_ptr<grpc_core::HPackParser> parser(new grpc_core::HPackParser);
int max_length = 1024;
int absolute_max_length = 1024;
bool can_update_max_length = true;
bool can_add_priority = true;
for (int i = 0; i < msg.frames_size(); i++) {
auto arena = grpc_core::MakeScopedArena(1024, &memory_allocator);
grpc_core::ExecCtx exec_ctx;
grpc_metadata_batch b(arena.get());
const auto& frame = msg.frames(i);
if (frame.parse_size() == 0) continue;
// we can only update max length after a frame boundary
// so simulate that here
if (can_update_max_length) {
if (frame.max_metadata_length() != 0) {
max_length = std::max(0, frame.max_metadata_length());
}
if (frame.absolute_max_metadata_length() != 0) {
absolute_max_length =
std::max(0, frame.absolute_max_metadata_length());
}
if (absolute_max_length < max_length) {
std::swap(absolute_max_length, max_length);
}
}
// priority only makes sense on the first frame of a stream
grpc_core::HPackParser::Priority priority =
grpc_core::HPackParser::Priority::None;
if (can_add_priority && frame.priority()) {
priority = grpc_core::HPackParser::Priority::Included;
}
grpc_core::HPackParser::Boundary boundary =
grpc_core::HPackParser::Boundary::None;
can_update_max_length = false;
can_add_priority = false;
if (frame.end_of_headers()) {
boundary = grpc_core::HPackParser::Boundary::EndOfHeaders;
can_update_max_length = true;
}
if (frame.end_of_stream()) {
boundary = grpc_core::HPackParser::Boundary::EndOfStream;
}
grpc_core::HPackParser::Priority priority =
grpc_core::HPackParser::Priority::None;
if (frame.priority()) {
priority = grpc_core::HPackParser::Priority::Included;
}
int max_length = 1024;
int absolute_max_length = 1024;
if (absolute_max_length < max_length) {
std::swap(absolute_max_length, max_length);
}
if (frame.max_metadata_length() != 0) {
max_length = frame.max_metadata_length();
}
if (frame.absolute_max_metadata_length() != 0) {
absolute_max_length = frame.absolute_max_metadata_length();
can_update_max_length = true;
can_add_priority = true;
}
parser->BeginFrame(
@ -87,16 +108,33 @@ DEFINE_PROTO_FUZZER(const hpack_parser_fuzzer::Msg& msg) {
1, grpc_core::HPackParser::LogInfo::kHeaders, false});
int stop_buffering_ctr =
std::max(-1, frame.stop_buffering_after_segments());
for (const auto& parse : frame.parse()) {
for (int idx = 0; idx < frame.parse_size(); idx++) {
const auto& parse = frame.parse(idx);
grpc_slice buffer =
grpc_slice_from_copied_buffer(parse.data(), parse.size());
(void)parser->Parse(buffer, i == msg.frames_size() - 1);
auto err = parser->Parse(buffer, idx == frame.parse_size() - 1);
grpc_slice_unref(buffer);
stop_buffering_ctr--;
if (0 == stop_buffering_ctr) parser->StopBufferingFrame();
// Ensure we never take on more than four times the absolute limit in
// buffer size.
// (This is incredibly generous, but having a bound nevertheless means
// we don't accidentally flow to infinity, which would be crossing the
// streams level bad).
GPR_ASSERT(static_cast<int>(parser->buffered_bytes() / 4) <
std::max(1024, absolute_max_length));
if (!err.ok()) {
intptr_t unused;
if (grpc_error_get_int(err, grpc_core::StatusIntProperty::kStreamId,
&unused)) {
// This is a stream error, we ignore it
} else {
// This is a connection error, we don't try to parse anymore
return;
}
}
}
parser->FinishFrame();
}
}
grpc_shutdown();
}

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save