fix-ABI-breakage.patch 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224
  1. Subject: Fix ABI breakage introduced by accident in 2.8.1-1+deb10u1
  2. Author: Hilko Bengen <bengen@debian.org>
  3. Date: 2021-10-22
  4. Bug-Debian:
  5. https://bugs.debian.org/996460
  6. https://bugs.debian.org/996939
  7. https://bugs.debian.org/996997
  8. Comment: (by the http-parser maintainer)
  9. # Problem
  10. The fix for CVE-2019-15605 introduced an ABI break by changing the
  11. layout of struct http_parser - a change that was needed to store a
  12. ninth bit in the "flags" field. However, this affected offsets of
  13. fields declared as public, causing applications to break.
  14. # Workaround
  15. To restore the previous layout while still implementing the fix: Steal
  16. one bit from the (private) content_length field (the remaining 63
  17. are more than enough) to store the newly introduced flag.
  18. Also rename the related constant as a safeguard against applications
  19. that use it (they must not, see below).
  20. # Possible regressions
  21. A lot of work was done to avoid damage for well-behaving applications.
  22. It seems all applications in Debian built against http-parser fall
  23. into that category.
  24. Applications however that access fields in struct http_parser that are
  25. in the section marked "/** PRIVATE **/" may face issues. Such a
  26. behaviour is inacceptable anyway.
  27. If such a mis-behaving application ...
  28. * was built using an earlier version of http-parser, the code will
  29. assume content_length is a 64 bit value. Depending on endianess and
  30. status of the F_TRANSFER_ENCODING bit, things may work. Possibly
  31. they will not.
  32. * uses the private F_TRANSFER_ENCODING constant and was built using
  33. http-parser 2.8.1-1+deb10u1, it will not see the information it
  34. expects to see.
  35. Additionally, and re-build will fail. This is by design.
  36. Again, applications must not access fields declared private, and their
  37. authors should not expect pity if they encounter breakage any anything
  38. changes there.
  39. # Acknowledgments
  40. Thanks a lot to Hilko Bengen for the idea, implementation, a first
  41. round of tests and many suggestions.
  42. --- a/http_parser.c
  43. +++ b/http_parser.c
  44. @@ -25,9 +25,7 @@
  45. #include <string.h>
  46. #include <limits.h>
  47. -#ifndef ULLONG_MAX
  48. -# define ULLONG_MAX ((uint64_t) -1) /* 2^64-1 */
  49. -#endif
  50. +#define CONTENT_LENGTH_MAX (((uint64_t)-1) >> 1)
  51. #ifndef MIN
  52. # define MIN(a,b) ((a) < (b) ? (a) : (b))
  53. @@ -724,7 +722,8 @@
  54. if (ch == CR || ch == LF)
  55. break;
  56. parser->flags = 0;
  57. - parser->content_length = ULLONG_MAX;
  58. + parser->flags2 = 0;
  59. + parser->content_length = CONTENT_LENGTH_MAX;
  60. if (ch == 'H') {
  61. UPDATE_STATE(s_res_or_resp_H);
  62. @@ -759,7 +758,8 @@
  63. case s_start_res:
  64. {
  65. parser->flags = 0;
  66. - parser->content_length = ULLONG_MAX;
  67. + parser->flags2 = 0;
  68. + parser->content_length = CONTENT_LENGTH_MAX;
  69. switch (ch) {
  70. case 'H':
  71. @@ -923,7 +923,8 @@
  72. if (ch == CR || ch == LF)
  73. break;
  74. parser->flags = 0;
  75. - parser->content_length = ULLONG_MAX;
  76. + parser->flags2 = 0;
  77. + parser->content_length = CONTENT_LENGTH_MAX;
  78. if (UNLIKELY(!IS_ALPHA(ch))) {
  79. SET_ERRNO(HPE_INVALID_METHOD);
  80. @@ -1314,7 +1315,7 @@
  81. parser->header_state = h_general;
  82. } else if (parser->index == sizeof(TRANSFER_ENCODING)-2) {
  83. parser->header_state = h_transfer_encoding;
  84. - parser->flags |= F_TRANSFER_ENCODING;
  85. + parser->flags2 |= F_TRANSFER_ENCODING2;
  86. }
  87. break;
  88. @@ -1528,7 +1529,7 @@
  89. t += ch - '0';
  90. /* Overflow? Test against a conservative limit for simplicity. */
  91. - if (UNLIKELY((ULLONG_MAX - 10) / 10 < parser->content_length)) {
  92. + if (UNLIKELY((CONTENT_LENGTH_MAX - 10) / 10 < parser->content_length)) {
  93. SET_ERRNO(HPE_INVALID_CONTENT_LENGTH);
  94. parser->header_state = h_state;
  95. goto error;
  96. @@ -1765,7 +1766,7 @@
  97. /* Cannot us transfer-encoding and a content-length header together
  98. per the HTTP specification. (RFC 7230 Section 3.3.3) */
  99. - if ((parser->flags & F_TRANSFER_ENCODING) &&
  100. + if ((parser->flags2 & F_TRANSFER_ENCODING2) &&
  101. (parser->flags & F_CONTENTLENGTH)) {
  102. /* Allow it for lenient parsing as long as `Transfer-Encoding` is
  103. * not `chunked`
  104. @@ -1834,7 +1835,7 @@
  105. parser->nread = 0;
  106. hasBody = parser->flags & F_CHUNKED ||
  107. - (parser->content_length > 0 && parser->content_length != ULLONG_MAX);
  108. + (parser->content_length > 0 && parser->content_length != CONTENT_LENGTH_MAX);
  109. if (parser->upgrade && (parser->method == HTTP_CONNECT ||
  110. (parser->flags & F_SKIPBODY) || !hasBody)) {
  111. /* Exit, the rest of the message is in a different protocol. */
  112. @@ -1850,7 +1851,7 @@
  113. /* chunked encoding - ignore Content-Length header,
  114. * prepare for a chunk */
  115. UPDATE_STATE(s_chunk_size_start);
  116. - } else if (parser->flags & F_TRANSFER_ENCODING) {
  117. + } else if (parser->flags2 & F_TRANSFER_ENCODING2) {
  118. if (parser->type == HTTP_REQUEST && !lenient) {
  119. /* RFC 7230 3.3.3 */
  120. @@ -1877,7 +1878,7 @@
  121. /* Content-Length header given but zero: Content-Length: 0\r\n */
  122. UPDATE_STATE(NEW_MESSAGE());
  123. CALLBACK_NOTIFY(message_complete);
  124. - } else if (parser->content_length != ULLONG_MAX) {
  125. + } else if (parser->content_length != CONTENT_LENGTH_MAX) {
  126. /* Content-Length header given and non-zero */
  127. UPDATE_STATE(s_body_identity);
  128. } else {
  129. @@ -1901,7 +1902,7 @@
  130. (uint64_t) ((data + len) - p));
  131. assert(parser->content_length != 0
  132. - && parser->content_length != ULLONG_MAX);
  133. + && parser->content_length != CONTENT_LENGTH_MAX);
  134. /* The difference between advancing content_length and p is because
  135. * the latter will automaticaly advance on the next loop iteration.
  136. @@ -1991,7 +1992,7 @@
  137. t += unhex_val;
  138. /* Overflow? Test against a conservative limit for simplicity. */
  139. - if (UNLIKELY((ULLONG_MAX - 16) / 16 < parser->content_length)) {
  140. + if (UNLIKELY((CONTENT_LENGTH_MAX - 16) / 16 < parser->content_length)) {
  141. SET_ERRNO(HPE_INVALID_CONTENT_LENGTH);
  142. goto error;
  143. }
  144. @@ -2035,7 +2036,7 @@
  145. assert(parser->flags & F_CHUNKED);
  146. assert(parser->content_length != 0
  147. - && parser->content_length != ULLONG_MAX);
  148. + && parser->content_length != CONTENT_LENGTH_MAX);
  149. /* See the explanation in s_body_identity for why the content
  150. * length and data pointers are managed this way.
  151. @@ -2124,12 +2125,12 @@
  152. }
  153. /* RFC 7230 3.3.3, see `s_headers_almost_done` */
  154. - if ((parser->flags & F_TRANSFER_ENCODING) &&
  155. + if ((parser->flags2 & F_TRANSFER_ENCODING2) &&
  156. (parser->flags & F_CHUNKED) == 0) {
  157. return 1;
  158. }
  159. - if ((parser->flags & F_CHUNKED) || parser->content_length != ULLONG_MAX) {
  160. + if ((parser->flags & F_CHUNKED) || parser->content_length != CONTENT_LENGTH_MAX) {
  161. return 0;
  162. }
  163. --- a/http_parser.h
  164. +++ b/http_parser.h
  165. @@ -225,7 +225,7 @@
  166. , F_UPGRADE = 1 << 5
  167. , F_SKIPBODY = 1 << 6
  168. , F_CONTENTLENGTH = 1 << 7
  169. - , F_TRANSFER_ENCODING = 1 << 8
  170. + , F_TRANSFER_ENCODING2 = 1 << 0 /* this goes into http_parser.flags2 */
  171. };
  172. @@ -296,14 +296,15 @@
  173. struct http_parser {
  174. /** PRIVATE **/
  175. unsigned int type : 2; /* enum http_parser_type */
  176. + unsigned int flags : 8; /* F_* values from 'flags' enum; semi-public */
  177. unsigned int state : 7; /* enum state from http_parser.c */
  178. unsigned int header_state : 7; /* enum header_state from http_parser.c */
  179. unsigned int index : 7; /* index into current matcher */
  180. unsigned int lenient_http_headers : 1;
  181. - unsigned int flags : 16; /* F_* values from 'flags' enum; semi-public */
  182. uint32_t nread; /* # bytes read in various scenarios */
  183. - uint64_t content_length; /* # bytes in body (0 if no Content-Length header) */
  184. + uint64_t flags2 : 1; /* one bit another flag */
  185. + uint64_t content_length : 63; /* # bytes in body (0 if no Content-Length header) */
  186. /** READ-ONLY **/
  187. unsigned short http_major;