You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

166 lines
4.3 KiB

  1. From b7466e31e4bee160d82a68fca11b1f61d46debae Mon Sep 17 00:00:00 2001
  2. From: Ben Noordhuis <info@bnoordhuis.nl>
  3. Date: Fri, 21 May 2021 11:23:36 +0200
  4. Subject: [PATCH] idna: fix OOB read in punycode decoder
  5. libuv was vulnerable to out-of-bounds reads in the uv__idna_toascii()
  6. function which is used to convert strings to ASCII. This is called by
  7. the DNS resolution function and can lead to information disclosures or
  8. crashes.
  9. Reported by Eric Sesterhenn in collaboration with Cure53 and ExpressVPN.
  10. Reported-By: Eric Sesterhenn <eric.sesterhenn@x41-dsec.de>
  11. Fixes: https://github.com/libuv/libuv/issues/3147
  12. PR-URL: https://github.com/libuv/libuv-private/pull/1
  13. Refs: https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-22918
  14. Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
  15. Reviewed-By: Richard Lau <riclau@uk.ibm.com>
  16. ---
  17. src/idna.c | 49 +++++++++++++++++++++++++++++++++++-------------
  18. test/test-idna.c | 19 +++++++++++++++++++
  19. test/test-list.h | 2 ++
  20. 3 files changed, 57 insertions(+), 13 deletions(-)
  21. --- a/src/idna.c
  22. +++ b/src/idna.c
  23. @@ -19,6 +19,7 @@
  24. #include "uv.h"
  25. #include "idna.h"
  26. +#include <assert.h>
  27. #include <string.h>
  28. static unsigned uv__utf8_decode1_slow(const char** p,
  29. @@ -32,7 +33,7 @@ static unsigned uv__utf8_decode1_slow(co
  30. if (a > 0xF7)
  31. return -1;
  32. - switch (*p - pe) {
  33. + switch (pe - *p) {
  34. default:
  35. if (a > 0xEF) {
  36. min = 0x10000;
  37. @@ -62,6 +63,8 @@ static unsigned uv__utf8_decode1_slow(co
  38. a = 0;
  39. break;
  40. }
  41. + /* Fall through. */
  42. + case 0:
  43. return -1; /* Invalid continuation byte. */
  44. }
  45. @@ -88,6 +91,8 @@ static unsigned uv__utf8_decode1_slow(co
  46. unsigned uv__utf8_decode1(const char** p, const char* pe) {
  47. unsigned a;
  48. + assert(*p < pe);
  49. +
  50. a = (unsigned char) *(*p)++;
  51. if (a < 128)
  52. @@ -96,9 +101,6 @@ unsigned uv__utf8_decode1(const char** p
  53. return uv__utf8_decode1_slow(p, pe, a);
  54. }
  55. -#define foreach_codepoint(c, p, pe) \
  56. - for (; (void) (*p <= pe && (c = uv__utf8_decode1(p, pe))), *p <= pe;)
  57. -
  58. static int uv__idna_toascii_label(const char* s, const char* se,
  59. char** d, char* de) {
  60. static const char alphabet[] = "abcdefghijklmnopqrstuvwxyz0123456789";
  61. @@ -121,15 +123,22 @@ static int uv__idna_toascii_label(const
  62. ss = s;
  63. todo = 0;
  64. - foreach_codepoint(c, &s, se) {
  65. + /* Note: after this loop we've visited all UTF-8 characters and know
  66. + * they're legal so we no longer need to check for decode errors.
  67. + */
  68. + while (s < se) {
  69. + c = uv__utf8_decode1(&s, se);
  70. +
  71. + if (c == -1u)
  72. + return UV_EINVAL;
  73. +
  74. if (c < 128)
  75. h++;
  76. - else if (c == (unsigned) -1)
  77. - return UV_EINVAL;
  78. else
  79. todo++;
  80. }
  81. + /* Only write "xn--" when there are non-ASCII characters. */
  82. if (todo > 0) {
  83. if (*d < de) *(*d)++ = 'x';
  84. if (*d < de) *(*d)++ = 'n';
  85. @@ -137,9 +146,13 @@ static int uv__idna_toascii_label(const
  86. if (*d < de) *(*d)++ = '-';
  87. }
  88. + /* Write ASCII characters. */
  89. x = 0;
  90. s = ss;
  91. - foreach_codepoint(c, &s, se) {
  92. + while (s < se) {
  93. + c = uv__utf8_decode1(&s, se);
  94. + assert(c != -1u);
  95. +
  96. if (c > 127)
  97. continue;
  98. @@ -166,10 +179,15 @@ static int uv__idna_toascii_label(const
  99. while (todo > 0) {
  100. m = -1;
  101. s = ss;
  102. - foreach_codepoint(c, &s, se)
  103. +
  104. + while (s < se) {
  105. + c = uv__utf8_decode1(&s, se);
  106. + assert(c != -1u);
  107. +
  108. if (c >= n)
  109. if (c < m)
  110. m = c;
  111. + }
  112. x = m - n;
  113. y = h + 1;
  114. @@ -181,7 +199,10 @@ static int uv__idna_toascii_label(const
  115. n = m;
  116. s = ss;
  117. - foreach_codepoint(c, &s, se) {
  118. + while (s < se) {
  119. + c = uv__utf8_decode1(&s, se);
  120. + assert(c != -1u);
  121. +
  122. if (c < n)
  123. if (++delta == 0)
  124. return UV_E2BIG; /* Overflow. */
  125. @@ -245,8 +266,6 @@ static int uv__idna_toascii_label(const
  126. return 0;
  127. }
  128. -#undef foreach_codepoint
  129. -
  130. long uv__idna_toascii(const char* s, const char* se, char* d, char* de) {
  131. const char* si;
  132. const char* st;
  133. @@ -256,10 +275,14 @@ long uv__idna_toascii(const char* s, con
  134. ds = d;
  135. - for (si = s; si < se; /* empty */) {
  136. + si = s;
  137. + while (si < se) {
  138. st = si;
  139. c = uv__utf8_decode1(&si, se);
  140. + if (c == -1u)
  141. + return UV_EINVAL;
  142. +
  143. if (c != '.')
  144. if (c != 0x3002) /* 。 */
  145. if (c != 0xFF0E) /* . */