ipv4.c 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239
  1. // SPDX-License-Identifier: GPL-2.0 or GPL-3.0
  2. // Copyright © 2019 Ariadne Devos
  3. #include <stdint.h>
  4. #include <stddef.h>
  5. #include <stdlib.h>
  6. #include <sHT/lex/ipv4-addr.h>
  7. #include <sHT/lex/nat.h>
  8. #include <sHT/logic/failbit.h>
  9. #include <sHT/nospec.h>
  10. #include <sHT/test.h>
  11. /* TODO: possible optimisations:
  12. - nonspeculative architectures: do a length check at the beginning,
  13. if the length exceeds the maximum length of an IPv4Address,
  14. don't do any further checks
  15. - speculative architectures: combine index masking with bounds checking
  16. (requires asm goto output operands or compiler support)
  17. TODO: readability
  18. - try to shorten proofs */
  19. /* TODO: leaks IP addresses */
  20. struct s2_ipv4_maybe
  21. s2_parse_ipv4(size_t length, const uint8_t from[length])
  22. {
  23. /* invariant: (i) i ≤ length
  24. sometimes: (i2) i < length
  25. (a, definition `i`) ⇒ (i)
  26. No overflow on increment:
  27. (a) length < failbit_limit(size_t) ⇒ (o1) length ≤ \max(i) - 1
  28. (i, o1) ⇒ (nooverflow) i ≤ \max(i) - 1 */
  29. size_t i = 1;
  30. uint32_t ip = 0;
  31. /* Just 1 would do as well, as a single character can't both be
  32. [0-9] and ".", and sHT_index_nospec brings the index back to
  33. from[i]. Except, of course, on non-speculative architectures. */
  34. if (1 + 7 > length)
  35. /* (1) minlength(IPv4address) = len("0.0.0.0") = 7,
  36. (branch) nospec: length < 1 + 7
  37. (1, branch)
  38. ⇒ length < 1 + minlength(IPv4address)
  39. ⇒ select (noexist -> 0) { k | 1 ≤ k ≤ length }
  40. (match IPv4address _ 1) = 0
  41. ⇒ (2) nospec: end = 0 (definition of `end`)
  42. ret.end = fail(size_t, 1)
  43. ⇒ (3) ¬good(size_t, ret.end), (4) fail_value(ret.end) = 1
  44. 0 ≤ 1 ⇒ (5) 0 ≤ fail_value(ret.end)
  45. 0 < length (a) ⇒ (6) 1 ≤ length
  46. (6, 4) ⇒ (7) fail_value(ret.end) ≤ length
  47. (5, 7) ⇒ (v)
  48. (2, 3) ⇒ (w)
  49. (3) ⇒ (x), (y) (counterfactual)
  50. (z) match [0-9.]* from 1 fail_value(size_t, ret.end)
  51. ⇐ match [0-9.]* from 1 1 (substitution, 4)
  52. ⇐ match ε from 1 1 (regex)
  53. ⇐ ⊤ (regex) */
  54. /* .ip = ANYTHING is acceptable */
  55. return (struct s2_ipv4_maybe) { .ip = 0, .end = sHT_fail(1) };
  56. /* (branch, 0): length ≥ 8 */
  57. /* the number of octets to parse */
  58. int w = 4;
  59. /* (A): nonspec: 0 < w ≤ 4
  60. (B): 0 ≤ i ≤ length
  61. (C): nonspec: match (dec-octet '.'){4 - w} from 1 i
  62. (underflow: A)
  63. (D): nonspec: ip = (mappend . map (->octet . ->nat)) [\0 \to \(4 - w)])
  64. (E): nonspec: 0 ≤ i < length
  65. (A) ⇐ 0 < 4 ≤ 4 (definition of `w`) ≤ ⊤ (eval)
  66. (B) ⇐ 0 ≤ 1 ≤ length (definition of `i`)
  67. ⇐ 1 ≤ length (left: eval)
  68. ⇐ 0 < length
  69. ⇐ (a)
  70. (E) ⇐ 0 ≤ 1 < length (definition of `i`)
  71. ⇐ 1 < length (left: eval)
  72. ⇐ length > 1 (left: eval)
  73. ⇐ (0)
  74. nonspec: (C) ⇐ match (dec-octet '.'){4 - 4} from 1 1
  75. (definition of `w` and `i`)
  76. ⇐ match (dec-octet '.'){0} from 1 1 (eval)
  77. ⇐ match ε from 1 1 (eval)
  78. ⇐ ⊤ (1 = 1, regex)
  79. (D) simplify:
  80. let f = ->octet . ->nat in:
  81. ip = mappend . map ?A [\0 \to \(4 - w)]
  82. = mappend . map ?A [\0 \to \(4 - 4)]
  83. (definition of `w`)
  84. = mappend . map ?A [\0 \to \0] (eval)
  85. = mappend . map ?A []
  86. = mappend (map ?A []) (reduce _ . _)
  87. = mappend [] (eval) = 0 (eval)
  88. Satified by definition */
  89. do {
  90. /* let j := i (rewrite A, B, C, E)*/
  91. uint32_t octet, octettmp, c;
  92. /* sHT_index_nospec:
  93. (%a) ⇐ (E), preserves (i), ensures (i2)
  94. nonspec: octet := from[i] - '0' = from[j] - '0' (definition of `j`) */
  95. octet = from[i = sHT_index_nospec(i, length)] - '0';
  96. if (octet > 9)
  97. /* (branch, digit_minus_zero) ⇒ a ∉ [0-9]
  98. Before there has been a dot, of an IPv4Address,
  99. so a dec-octet must follow, so [0-9] must follow,
  100. which isn't the case, so a syntax error:
  101. (Bf) end = 0. (Af) ⇐ (B) */
  102. goto fail;
  103. /* nonspec: octet = from[i] - '0' (definition)
  104. = decimal->nat from[i] (branch) */
  105. /* invariant: rewrite (i2) to (i)
  106. let i := old i + 1 = j + 1 */
  107. i++;
  108. if (octet == 0)
  109. /* nonspec: 0 = octet (branch)
  110. = from[j] - '0' (definition of `octet`)
  111. ⇒ 0 + '0' = from[j] - '0' + '0'
  112. ⇒ '0' = from[j]
  113. (leading zeroes are not allowed (except the
  114. octet "0", so expect the dot: (Bj, Dj)))
  115. (Aj) ⇐ 0 ≤ j + 1 ≤ length (definition)
  116. ⇐ 0 ≤ j < length (definition)
  117. ⇐ 0 ≤ j ≤ length (weaken)
  118. ⇐ (B)
  119. (Cj) nonspec: \1 = "0", string->nat "0" = 0 = octet */
  120. goto join;
  121. if (sHT_eq(i, length))
  122. goto join;
  123. /* nonspec: (branch): i ≠ length ⇒ (9) j + 1 ≠ length (definition) */
  124. /* let i := index_nospec(old i, length)
  125. = index_nospec(j + 1, length)
  126. nonspec: (E) 0 ≤ j < length
  127. ⇒ 1 ≤ j + 1 < length + 1
  128. ⇒ 1 ≤ j + 1 ≤ length
  129. ⇒ 0 ≤ j + 1 ≤ length
  130. ⇒ 0 ≤ j + 1 ∧ j + 1 ≤ length
  131. ⇒ 0 ≤ j + 1 ∧ (j + 1 < length \/ j + 1 = length)
  132. ⇒ 0 ≤ j + 1 ∧ j + 1 < length (9)
  133. ⇒ (10) 0 ≤ j + 1 < length
  134. ⇒ (%a) 0 ≤ old i < length (definition)
  135. (%y, a) ⇒ (i2) 0 ≤ new i < length */
  136. i = sHT_index_nospec(i, length);
  137. /* (i2, b) ⇒ allow read event */
  138. c = from[i] - '0';
  139. if (c > 9)
  140. /* (11) ⇒ (Aj) (weaken)
  141. (first digit test) ⇒ (Bj)
  142. (last definition of octet, w counter) ⇒ (Cj)
  143. (branch) ⇒ (Dj) */
  144. goto join;
  145. /* nonspec: (append second digit) */
  146. octet = 10 * octet + c;
  147. /* invariant: rewrite (i2) to (i) */
  148. i++;
  149. if (sHT_eq(i, length))
  150. /* (i) ⇒ (Aj)
  151. (Bj): see digit tests
  152. (last definition of octet, w counter) ⇒ (Cj)
  153. (branch) ⇒ (Dj) (left) */
  154. goto join;
  155. /* (branch, i) ⇒ nonspec: (i2) */
  156. /* (nonspec: i2) ⇒ (%a)
  157. (%y, a) ⇒ (i2) 0 ≤ new i < length ⇒ (i) */
  158. c = from[i = sHT_index_nospec(i, length)] - '0';
  159. if (c > 9)
  160. /* (i) ⇒ (Aj)
  161. (Bj, Dj): see digit tests
  162. (last definition of octet, w counter) ⇒ (Cj) */
  163. goto join;
  164. if ((octettmp = 10 * octet + c) > 255)
  165. /* (i) ⇒ (Aj)
  166. (Bj, Dj) see digit tests,
  167. dec-octet = "0" | [1-9][0-9]{2} where < 256
  168. (last definition of octet, w counter) ⇒ (Cj) */
  169. goto join;
  170. octet = octettmp;
  171. /* invariant: rewrite (i2) to (i) */
  172. i++;
  173. /* (i) ⇒ (Aj)
  174. (Bj): see digit tests
  175. (last definition of octet, w counter) ⇒ (Cj)
  176. (maximum length achieved) ⇒ (Dj) (right) */
  177. /* Requires:
  178. (Aj): 0 ≤ i ≤ length
  179. (Bj): nonspec: match dec-octet from j i
  180. (Cj): nonspec: octet = \(4 - w)
  181. (Dj): nonspec: i = length ∨ ¬match dec-octet from j (i + 1) */
  182. join:
  183. /* one iteration of mappend (^ would do as well) */
  184. ip = (ip << 8) | octet;
  185. if (--w == 0)
  186. return (struct s2_ipv4_maybe) { .ip = ip, .end = sHT_success(i) };
  187. /* More octets coming, minimum length "."*/
  188. if (sHT_lt(length - i, 2))
  189. /* .0 */
  190. goto fail;
  191. /* (branch) ⇒ (i2) */
  192. /* (nonspec: i2) ⇒ (%a)
  193. (%y, a) ⇒ (i2) 0 ≤ new i < length ⇒ (i) */
  194. if (from[i = sHT_index_nospec(i, length)] != '.')
  195. goto fail;
  196. i++;
  197. } while (1);
  198. /* IPv4Address implies [0-9.]*, so a partial IPv4Address also implies [0-9.]* */
  199. /* Requires:
  200. (Af): 0 ≤ i ≤ length
  201. (Bf): nonspec: end = 0
  202. (Cf): nonspec: match [0-9.]* from 1 i
  203. (implied by partial IPv4Address match) */
  204. fail:
  205. /* .ip = ANYTHING is acceptable
  206. Begin sHT_fail:
  207. (Af, a) ⇒ 0 ≤ i < failbit_limit(size_t) ⇒ %a
  208. End.
  209. (v) ⇐ (Af, %z)
  210. (w) ⇐ (%y, Bf)
  211. (x), (y) ⇐ (%y) (counterfactual)
  212. (z) ⇐ (Cf) (remove premise) */
  213. return (struct s2_ipv4_maybe) { .ip = ip, .end = sHT_fail(i) };
  214. }