123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239 |
- // SPDX-License-Identifier: GPL-2.0 or GPL-3.0
- // Copyright © 2019 Ariadne Devos
- #include <stdint.h>
- #include <stddef.h>
- #include <stdlib.h>
- #include <sHT/lex/ipv4-addr.h>
- #include <sHT/lex/nat.h>
- #include <sHT/logic/failbit.h>
- #include <sHT/nospec.h>
- #include <sHT/test.h>
- /* TODO: possible optimisations:
- - nonspeculative architectures: do a length check at the beginning,
- if the length exceeds the maximum length of an IPv4Address,
- don't do any further checks
- - speculative architectures: combine index masking with bounds checking
- (requires asm goto output operands or compiler support)
- TODO: readability
- - try to shorten proofs */
- /* TODO: leaks IP addresses */
- struct s2_ipv4_maybe
- s2_parse_ipv4(size_t length, const uint8_t from[length])
- {
- /* invariant: (i) i ≤ length
- sometimes: (i2) i < length
- (a, definition `i`) ⇒ (i)
- No overflow on increment:
- (a) length < failbit_limit(size_t) ⇒ (o1) length ≤ \max(i) - 1
- (i, o1) ⇒ (nooverflow) i ≤ \max(i) - 1 */
- size_t i = 1;
- uint32_t ip = 0;
- /* Just 1 would do as well, as a single character can't both be
- [0-9] and ".", and sHT_index_nospec brings the index back to
- from[i]. Except, of course, on non-speculative architectures. */
- if (1 + 7 > length)
- /* (1) minlength(IPv4address) = len("0.0.0.0") = 7,
- (branch) nospec: length < 1 + 7
- (1, branch)
- ⇒ length < 1 + minlength(IPv4address)
- ⇒ select (noexist -> 0) { k | 1 ≤ k ≤ length }
- (match IPv4address _ 1) = 0
- ⇒ (2) nospec: end = 0 (definition of `end`)
- ret.end = fail(size_t, 1)
- ⇒ (3) ¬good(size_t, ret.end), (4) fail_value(ret.end) = 1
- 0 ≤ 1 ⇒ (5) 0 ≤ fail_value(ret.end)
- 0 < length (a) ⇒ (6) 1 ≤ length
- (6, 4) ⇒ (7) fail_value(ret.end) ≤ length
- (5, 7) ⇒ (v)
- (2, 3) ⇒ (w)
- (3) ⇒ (x), (y) (counterfactual)
- (z) match [0-9.]* from 1 fail_value(size_t, ret.end)
- ⇐ match [0-9.]* from 1 1 (substitution, 4)
- ⇐ match ε from 1 1 (regex)
- ⇐ ⊤ (regex) */
- /* .ip = ANYTHING is acceptable */
- return (struct s2_ipv4_maybe) { .ip = 0, .end = sHT_fail(1) };
- /* (branch, 0): length ≥ 8 */
- /* the number of octets to parse */
- int w = 4;
- /* (A): nonspec: 0 < w ≤ 4
- (B): 0 ≤ i ≤ length
- (C): nonspec: match (dec-octet '.'){4 - w} from 1 i
- (underflow: A)
- (D): nonspec: ip = (mappend . map (->octet . ->nat)) [\0 \to \(4 - w)])
- (E): nonspec: 0 ≤ i < length
- (A) ⇐ 0 < 4 ≤ 4 (definition of `w`) ≤ ⊤ (eval)
- (B) ⇐ 0 ≤ 1 ≤ length (definition of `i`)
- ⇐ 1 ≤ length (left: eval)
- ⇐ 0 < length
- ⇐ (a)
- (E) ⇐ 0 ≤ 1 < length (definition of `i`)
- ⇐ 1 < length (left: eval)
- ⇐ length > 1 (left: eval)
- ⇐ (0)
- nonspec: (C) ⇐ match (dec-octet '.'){4 - 4} from 1 1
- (definition of `w` and `i`)
- ⇐ match (dec-octet '.'){0} from 1 1 (eval)
- ⇐ match ε from 1 1 (eval)
- ⇐ ⊤ (1 = 1, regex)
- (D) simplify:
- let f = ->octet . ->nat in:
- ip = mappend . map ?A [\0 \to \(4 - w)]
- = mappend . map ?A [\0 \to \(4 - 4)]
- (definition of `w`)
- = mappend . map ?A [\0 \to \0] (eval)
- = mappend . map ?A []
- = mappend (map ?A []) (reduce _ . _)
- = mappend [] (eval) = 0 (eval)
- Satified by definition */
- do {
- /* let j := i (rewrite A, B, C, E)*/
- uint32_t octet, octettmp, c;
- /* sHT_index_nospec:
- (%a) ⇐ (E), preserves (i), ensures (i2)
- nonspec: octet := from[i] - '0' = from[j] - '0' (definition of `j`) */
- octet = from[i = sHT_index_nospec(i, length)] - '0';
- if (octet > 9)
- /* (branch, digit_minus_zero) ⇒ a ∉ [0-9]
- Before there has been a dot, of an IPv4Address,
- so a dec-octet must follow, so [0-9] must follow,
- which isn't the case, so a syntax error:
- (Bf) end = 0. (Af) ⇐ (B) */
- goto fail;
- /* nonspec: octet = from[i] - '0' (definition)
- = decimal->nat from[i] (branch) */
- /* invariant: rewrite (i2) to (i)
- let i := old i + 1 = j + 1 */
- i++;
- if (octet == 0)
- /* nonspec: 0 = octet (branch)
- = from[j] - '0' (definition of `octet`)
- ⇒ 0 + '0' = from[j] - '0' + '0'
- ⇒ '0' = from[j]
- (leading zeroes are not allowed (except the
- octet "0", so expect the dot: (Bj, Dj)))
- (Aj) ⇐ 0 ≤ j + 1 ≤ length (definition)
- ⇐ 0 ≤ j < length (definition)
- ⇐ 0 ≤ j ≤ length (weaken)
- ⇐ (B)
- (Cj) nonspec: \1 = "0", string->nat "0" = 0 = octet */
- goto join;
- if (sHT_eq(i, length))
- goto join;
- /* nonspec: (branch): i ≠ length ⇒ (9) j + 1 ≠ length (definition) */
- /* let i := index_nospec(old i, length)
- = index_nospec(j + 1, length)
- nonspec: (E) 0 ≤ j < length
- ⇒ 1 ≤ j + 1 < length + 1
- ⇒ 1 ≤ j + 1 ≤ length
- ⇒ 0 ≤ j + 1 ≤ length
- ⇒ 0 ≤ j + 1 ∧ j + 1 ≤ length
- ⇒ 0 ≤ j + 1 ∧ (j + 1 < length \/ j + 1 = length)
- ⇒ 0 ≤ j + 1 ∧ j + 1 < length (9)
- ⇒ (10) 0 ≤ j + 1 < length
- ⇒ (%a) 0 ≤ old i < length (definition)
- (%y, a) ⇒ (i2) 0 ≤ new i < length */
- i = sHT_index_nospec(i, length);
- /* (i2, b) ⇒ allow read event */
- c = from[i] - '0';
- if (c > 9)
- /* (11) ⇒ (Aj) (weaken)
- (first digit test) ⇒ (Bj)
- (last definition of octet, w counter) ⇒ (Cj)
- (branch) ⇒ (Dj) */
- goto join;
- /* nonspec: (append second digit) */
- octet = 10 * octet + c;
- /* invariant: rewrite (i2) to (i) */
- i++;
- if (sHT_eq(i, length))
- /* (i) ⇒ (Aj)
- (Bj): see digit tests
- (last definition of octet, w counter) ⇒ (Cj)
- (branch) ⇒ (Dj) (left) */
- goto join;
- /* (branch, i) ⇒ nonspec: (i2) */
- /* (nonspec: i2) ⇒ (%a)
- (%y, a) ⇒ (i2) 0 ≤ new i < length ⇒ (i) */
- c = from[i = sHT_index_nospec(i, length)] - '0';
- if (c > 9)
- /* (i) ⇒ (Aj)
- (Bj, Dj): see digit tests
- (last definition of octet, w counter) ⇒ (Cj) */
- goto join;
- if ((octettmp = 10 * octet + c) > 255)
- /* (i) ⇒ (Aj)
- (Bj, Dj) see digit tests,
- dec-octet = "0" | [1-9][0-9]{2} where < 256
- (last definition of octet, w counter) ⇒ (Cj) */
- goto join;
- octet = octettmp;
- /* invariant: rewrite (i2) to (i) */
- i++;
- /* (i) ⇒ (Aj)
- (Bj): see digit tests
- (last definition of octet, w counter) ⇒ (Cj)
- (maximum length achieved) ⇒ (Dj) (right) */
- /* Requires:
- (Aj): 0 ≤ i ≤ length
- (Bj): nonspec: match dec-octet from j i
- (Cj): nonspec: octet = \(4 - w)
- (Dj): nonspec: i = length ∨ ¬match dec-octet from j (i + 1) */
- join:
- /* one iteration of mappend (^ would do as well) */
- ip = (ip << 8) | octet;
- if (--w == 0)
- return (struct s2_ipv4_maybe) { .ip = ip, .end = sHT_success(i) };
- /* More octets coming, minimum length "."*/
- if (sHT_lt(length - i, 2))
- /* .0 */
- goto fail;
- /* (branch) ⇒ (i2) */
- /* (nonspec: i2) ⇒ (%a)
- (%y, a) ⇒ (i2) 0 ≤ new i < length ⇒ (i) */
- if (from[i = sHT_index_nospec(i, length)] != '.')
- goto fail;
- i++;
- } while (1);
- /* IPv4Address implies [0-9.]*, so a partial IPv4Address also implies [0-9.]* */
- /* Requires:
- (Af): 0 ≤ i ≤ length
- (Bf): nonspec: end = 0
- (Cf): nonspec: match [0-9.]* from 1 i
- (implied by partial IPv4Address match) */
- fail:
- /* .ip = ANYTHING is acceptable
- Begin sHT_fail:
- (Af, a) ⇒ 0 ≤ i < failbit_limit(size_t) ⇒ %a
- End.
- (v) ⇐ (Af, %z)
- (w) ⇐ (%y, Bf)
- (x), (y) ⇐ (%y) (counterfactual)
- (z) ⇐ (Cf) (remove premise) */
- return (struct s2_ipv4_maybe) { .ip = ip, .end = sHT_fail(i) };
- }
|