Skip to content

Commit ad650ca

Browse files
committed
Merge master into the visibility-rules branch
master added lemmas and assertions that use the ghost members this branch renamed (absPkt, validPktMetaHdr, io.upd_uinfo, path.ifsToIO_ifs, ...). The conflicting hunks in router/dataplane.go and router/io-spec-lemmas.gobra are resolved in favour of master's content, with this branch's renames re-applied to it; the same renames are applied to the newly added pkg/slayers/path/scion/lemmas.gobra. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Txb3nNXx2mRUF6uae7PsRL
2 parents 09de830 + ecf9370 commit ad650ca

8 files changed

Lines changed: 320 additions & 8 deletions

File tree

gobra-mod.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
{
22
"default_job_cfg": {
33
"module": "github.com/scionproto/scion",
4+
"assert_timeout": 45000,
45
"includes": ["", "verification/dependencies"],
56
"only_files_with_header": true,
67
"assume_injectivity_inhale": true,
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
// This Source Code Form is subject to the terms of the Mozilla Public
2+
// License, v. 2.0. If a copy of the MPL was not distributed with this
3+
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
4+
//
5+
// Copyright (c) 2011-2020 ETH Zurich.
6+
7+
// +gobra
8+
9+
package scion
10+
11+
import (
12+
. "verification/utils/definitions"
13+
sl "verification/utils/slices"
14+
"verification/io"
15+
)
16+
17+
// IsXoverLemma shows that the abstract xover guard of the packet encoded in
18+
// `ubuf` agrees with the concrete check performed by (*Raw).IsXover, i.e., that
19+
// (io.Pkt).IsXover is a faithful abstraction of (Base).IsXoverSpec.
20+
ghost
21+
preserves acc(s.Mem(ubuf), R55)
22+
preserves acc(sl.Bytes(ubuf, 0, len(ubuf)), R56)
23+
preserves ValidPktMetaHdr(ubuf)
24+
preserves s.GetBase(ubuf).EqAbsHeader(ubuf)
25+
ensures s.AbsPkt(ubuf).IsXover() == s.GetBase(ubuf).IsXoverSpec()
26+
decreases
27+
func (s *Raw) IsXoverLemma(ubuf []byte) {
28+
if s.GetBase(ubuf).IsXoverSpec() {
29+
s.XoverLemma(ubuf)
30+
} else {
31+
// The packet is not at a segment switch. Either its current hop field is
32+
// not the last one of the segment being traversed, in which case the
33+
// future of the current segment contains more than one hop field, or
34+
// there is no segment left to switch to, in which case LeftSeg is none.
35+
reveal ValidPktMetaHdr(ubuf)
36+
metaHdr := RawBytesToMetaHdr(ubuf)
37+
currInfIdx := int(metaHdr.CurrINF)
38+
currHfIdx := int(metaHdr.CurrHF)
39+
seg1Len := int(metaHdr.SegLen[0])
40+
seg2Len := int(metaHdr.SegLen[1])
41+
seg3Len := int(metaHdr.SegLen[2])
42+
segs := io.CombineSegLens(seg1Len, seg2Len, seg3Len)
43+
segLen := segs.LengthOfCurrSeg(currHfIdx)
44+
prevSegLen := segs.LengthOfPrevSeg(currHfIdx)
45+
numINF := segs.NumInfoFields()
46+
offset := HopFieldOffset(numINF, prevSegLen, MetaLen)
47+
pkt := reveal s.AbsPkt(ubuf)
48+
assert pkt.CurrSeg == reveal CurrSeg(ubuf, offset, currInfIdx, currHfIdx - prevSegLen, segLen, MetaLen)
49+
assert pkt.LeftSeg == reveal LeftSeg(ubuf, currInfIdx + 1, segs, MetaLen)
50+
assert len(pkt.CurrSeg.Future) == segLen - (currHfIdx - prevSegLen)
51+
assert s.GetBase(ubuf) == RawBytesToBase(ubuf)
52+
assert !pkt.IsXover()
53+
}
54+
}
55+
56+
// LastHopNotXoverLemma shows that a packet whose current hop field is the last
57+
// one of its path is never at a segment switch. This is what allows the router
58+
// to deliver packets destined to the local AS without switching segments.
59+
ghost
60+
preserves acc(s.Mem(ubuf), R55)
61+
preserves s.IsLastHopSpec(ubuf)
62+
preserves acc(sl.Bytes(ubuf, 0, len(ubuf)), R56)
63+
preserves ValidPktMetaHdr(ubuf)
64+
preserves s.GetBase(ubuf).EqAbsHeader(ubuf)
65+
ensures !s.AbsPkt(ubuf).IsXover()
66+
decreases
67+
func (s *Raw) LastHopNotXoverLemma(ubuf []byte) {
68+
reveal ValidPktMetaHdr(ubuf)
69+
metaHdr := RawBytesToMetaHdr(ubuf)
70+
currInfIdx := int(metaHdr.CurrINF)
71+
currHfIdx := int(metaHdr.CurrHF)
72+
seg1Len := int(metaHdr.SegLen[0])
73+
seg2Len := int(metaHdr.SegLen[1])
74+
seg3Len := int(metaHdr.SegLen[2])
75+
segs := io.CombineSegLens(seg1Len, seg2Len, seg3Len)
76+
segLen := segs.LengthOfCurrSeg(currHfIdx)
77+
prevSegLen := segs.LengthOfPrevSeg(currHfIdx)
78+
numINF := segs.NumInfoFields()
79+
offset := HopFieldOffset(numINF, prevSegLen, MetaLen)
80+
pkt := reveal s.AbsPkt(ubuf)
81+
assert pkt.CurrSeg == reveal CurrSeg(ubuf, offset, currInfIdx, currHfIdx - prevSegLen, segLen, MetaLen)
82+
// A segment that could be switched to only exists when the current segment
83+
// is not the last one, which contradicts `currHfIdx == segs.TotalHops() - 1`.
84+
assert pkt.LeftSeg == reveal LeftSeg(ubuf, currInfIdx + 1, segs, MetaLen)
85+
assert s.GetBase(ubuf) == RawBytesToBase(ubuf)
86+
assert currHfIdx == segs.TotalHops() - 1
87+
assert pkt.LeftSeg == none[io.Seg]
88+
}

pkg/slayers/path/scion/raw_spec.gobra

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -451,6 +451,11 @@ func ValidPktMetaHdrSublice(raw []byte, idx int) {
451451
unfold acc(sl.Bytes(raw[:idx], 0, idx), R56)
452452
assert forall i int :: { &raw[:MetaLen][i] } 0 <= i && i < MetaLen ==>
453453
&raw[:MetaLen][i] == &raw[:idx][:MetaLen][i]
454+
// Sharing the locations is not enough: the two calls of RawBytesToMetaHdr
455+
// read the meta header through different sl.Bytes predicates, so their
456+
// snapshots must be related on those bytes as well.
457+
assert forall i int :: { &raw[:MetaLen][i] } 0 <= i && i < MetaLen ==>
458+
raw[:MetaLen][i] == raw[:idx][:MetaLen][i]
454459
fold acc(sl.Bytes(raw, 0, len(raw)), R56)
455460
fold acc(sl.Bytes(raw[:idx], 0, idx), R56)
456461
}

pkg/slayers/scion_spec.gobra

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -441,10 +441,10 @@ ensures acc(sl.Bytes(ub, 0, length), R55)
441441
ensures s.ValidHeaderOffset(ub, len(ub))
442442
decreases
443443
func (s *SCION) ValidHeaderOffsetFromSubSliceLemma(ub []byte, length int) {
444-
reveal s.ValidHeaderOffset(ub, len(ub))
444+
reveal s.ValidHeaderOffset(ub, length)
445445
unfold acc(sl.Bytes(ub, 0, len(ub)), R56)
446446
unfold acc(sl.Bytes(ub, 0, length), R56)
447-
assert reveal s.ValidHeaderOffset(ub, length)
447+
assert reveal s.ValidHeaderOffset(ub, len(ub))
448448
fold acc(sl.Bytes(ub, 0, len(ub)), R56)
449449
fold acc(sl.Bytes(ub, 0, length), R56)
450450
}
@@ -551,7 +551,11 @@ pure func IsSupportedPkt(raw []byte) bool {
551551
return CmnHdrLen <= len(raw) &&
552552
let pathType := path.Type(GetPathType(raw)) in
553553
let nextHdr := L4ProtocolType(GetNextHdr(raw)) in
554-
pathType == scion.PathType &&
554+
// EPIC packets are forwarded by the same code that forwards SCION packets,
555+
// operating on the SCION path that the EPIC metadata prefixes. Their
556+
// abstraction is the abstraction of that underlying SCION path, so they
557+
// are subject to the same IO-spec obligations.
558+
(pathType == scion.PathType || pathType == epic.PathType) &&
555559
nextHdr != L4SCMP
556560
}
557561

@@ -562,7 +566,7 @@ pure func IsSupportedRawPkt(raw seq[byte]) bool {
562566
return CmnHdrLen <= len(raw) &&
563567
let pathType := path.Type(raw[8]) in
564568
let nextHdr := L4ProtocolType(raw[4]) in
565-
pathType == scion.PathType &&
569+
(pathType == scion.PathType || pathType == epic.PathType) &&
566570
nextHdr != L4SCMP
567571
}
568572

@@ -575,6 +579,12 @@ decreases
575579
func IsSupportedPktSubslice(raw []byte, idx int) {
576580
unfold acc(sl.Bytes(raw, 0, len(raw)), R56)
577581
unfold acc(sl.Bytes(raw[:idx], 0, idx), R56)
582+
// The two calls of IsSupportedPkt read the next header and the path type
583+
// through different sl.Bytes predicates. Reading both locations through
584+
// both slices while the predicates are unfolded is what makes their
585+
// snapshots agree on them.
586+
assert raw[:idx][4] == raw[4]
587+
assert raw[:idx][8] == raw[8]
578588
reveal IsSupportedPkt(raw)
579589
reveal IsSupportedPkt(raw[:idx])
580590
fold acc(sl.Bytes(raw, 0, len(raw)), R56)
@@ -590,6 +600,9 @@ decreases
590600
func GetPathTypeSubslice(raw []byte, idx int) {
591601
unfold acc(sl.Bytes(raw, 0, len(raw)), R56)
592602
unfold acc(sl.Bytes(raw[:idx], 0, idx), R56)
603+
// Relates the two sl.Bytes snapshots on the byte holding the path type,
604+
// which is the only one the two calls of GetPathType read.
605+
assert raw[:idx][8] == raw[8]
593606
fold acc(sl.Bytes(raw, 0, len(raw)), R56)
594607
fold acc(sl.Bytes(raw[:idx], 0, idx), R56)
595608
}

0 commit comments

Comments
 (0)