Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
e04ac14
add annotations to the package
jcp19 Sep 26, 2025
21535a3
more changes
jcp19 Sep 26, 2025
558ef09
Start adding annotations to the Path interface
jcp19 Sep 26, 2025
e89ce07
add annotations to path.go
jcp19 Sep 26, 2025
7cbff69
undo changes to mac.go
jcp19 Sep 26, 2025
1eb79da
update the CI config for Gobra to run Gobra on stdlib specs
jcp19 Sep 26, 2025
5bf4bcc
Fix module name in the config for the Gobra CI; add job to check the …
jcp19 Sep 26, 2025
0a3d770
Add include paths to the config
jcp19 Sep 26, 2025
02fb414
fix wrong key in the config
jcp19 Sep 26, 2025
7c232e0
fix project location in the config
jcp19 Sep 26, 2025
ab7d96c
fix include paths for third-party library specs
jcp19 Sep 26, 2025
bd30e9d
fix include paths for third-party library specs
jcp19 Sep 26, 2025
d2a8369
enable verification of packages pkg/addr and pkg/slayers/path
jcp19 Sep 26, 2025
c663a39
update version of the gobra action
jcp19 Sep 26, 2025
cccfeae
small syntactic improvements
jcp19 Sep 27, 2025
b63a921
add missing postcondition to Value interface
jcp19 Sep 27, 2025
c55f92c
add missing postcondition to TextUnmarshaller interface
jcp19 Sep 27, 2025
3973097
continue cleaning-up stdlib-specs
jcp19 Sep 27, 2025
72fd62a
Add README.md
jcp19 Sep 27, 2025
3bb72eb
tiny simplification in config comment
jcp19 Sep 27, 2025
78bdab8
Clean-up specs of stdlib
jcp19 Sep 27, 2025
514f0f4
Cleanup
jcp19 Sep 27, 2025
58fb065
Merge branch 'master' into jcp19-add-more-gobra-annotations
jcp19 Oct 2, 2025
77f8241
fix linting issues in the Gobra readme
jcp19 Oct 2, 2025
096aa12
add documentation to Gobra's workflow file
jcp19 Oct 2, 2025
c8218a5
make the Gobra checks optional
jcp19 Oct 2, 2025
530f628
Merge branch 'master' into jcp19-add-more-gobra-annotations
jcp19 Oct 24, 2025
fc8fd06
Update pkg/slayers/path/path.go
jcp19 Oct 25, 2025
0ab0635
go fmt
jcp19 Oct 25, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
76 changes: 56 additions & 20 deletions .github/workflows/gobra.yml
Original file line number Diff line number Diff line change
@@ -1,3 +1,18 @@
# This file sets up a workflow that runs Gobra (github.com/viperproject/gobra)
# on selected packages of the SCION implementation. Gobra is a program verifier,
# i.e., it checks that the implementation of each function behaves as expected.
# The expected behaviour of each function is provided as a formal specification
# written in comments above the function signature. When Gobra is not able to
# prove that a function behaves according to its specification, it generates an
# (hopefully) informative error message, which identifies the parts of the code
# where the issues might lie.
# For more information on Gobra and its error messages, check the following:
# - The Gobra Book (WIP): viperproject.github.io/gobra-book/
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A feedback about the book itself, not this particular PR: the search there doesn't work with special characters, like @. Fortunately for me (a complete beginner with Gobra), I noticed that one chapter has @ in its name. But for people like me, the search bar might be a first place to go...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you! I added a feature request in the book repository (viperproject/gobra-book#43).

# - The tutorial: github.com/viperproject/gobra/blob/master/docs/tutorial.md
# The former covers more features of Gobra and contains more up-to-date
# information, but it is still under construction.


name: Verify the specified codebase

on:
Expand All @@ -8,32 +23,53 @@ jobs:
setup-and-test:
runs-on: ubuntu-latest
env:
statsFile: '/gobra/stats.json'
imageVersion: 'v25.09'
module: 'github.com/scionproto/scion/'
includePaths: '. ./gobra/stdlib-specs'
continueOnError: true
steps:
- name: Checkout the SCION repository
uses: actions/checkout@v2
- name: Set-up caching for the verification results
uses: actions/cache@v3
env:
cache-name: gobra-cache
- name: Check that the package gobra/utils is well-formed
uses: viperproject/[email protected]
with:
path: ${{ runner.workspace }}/.gobra/cache.json
key: ${{ env.cache-name }}
- name: Verify the specified files
uses: viperproject/[email protected]
with:
# Prefix used to resolve SCION packages
module: 'github.com/scionproto/scion/'
packages: 'gobra/utils'
# Gobra only verifies files annotated with the header "// +gobra"
headerOnly: '1'
# Traverse the entire repository, including nested packages,
# in search for annotated files to verify
timeout: 2m
imageVersion: ${{ env.imageVersion }}
module: ${{ env.module }}
includePaths: ${{ env.includePaths }}
continue-on-error: ${{ env.continueOnError }}
- name: Check that specifications of third-party libraries are well-formed
uses: viperproject/[email protected]
with:
projectLocation: 'scion/gobra/stdlib-specs'
headerOnly: '1'
# Check all package specifications in directory stdlib-specs
recursive: '1'
timeout: 10m
caching: '1'
statsFile: ${{ env.statsFile }}
- name: Upload the verification report
uses: actions/upload-artifact@v4
imageVersion: ${{ env.imageVersion }}
module: ${{ env.module }}
includePaths: '. ../..' # relative to project location
continue-on-error: ${{ env.continueOnError }}
- name: Verify package `pkg/addr`
uses: viperproject/[email protected]
with:
packages: 'pkg/addr'
headerOnly: '1'
timeout: 2m
imageVersion: ${{ env.imageVersion }}
module: ${{ env.module }}
includePaths: ${{ env.includePaths }}
continue-on-error: ${{ env.continueOnError }}
- name: Verify package `pkg/slayers/path`
uses: viperproject/[email protected]
with:
name: verification_stats.json
path: ${{ env.statsFile }}
packages: 'pkg/slayers/path'
headerOnly: '1'
timeout: 2m
imageVersion: ${{ env.imageVersion }}
module: ${{ env.module }}
includePaths: ${{ env.includePaths }}
continue-on-error: ${{ env.continueOnError }}
22 changes: 22 additions & 0 deletions gobra/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# Gobra specs

This directory contains the formal specification of a small part of the
Go standard library (in [`./stdlib-specs`](./stdlib-specs)) required to
verify the annotated files in the SCION codebase. In addition, we provide
a verified Gobra package in [`./utils`](./utils) that contains useful
definitions that are used in the verified packages of SCION.

## Contributing

It is expected that we will need a formal specification for more parts
of the Go standard library as we progress in verifying more code in the
SCION repository. To do so, one may add the type declarations and
signatures of unspecified functions and methods (_without the bodies_),
together with their contracts (i.e., pre- and postconditions), to the
corresponding package specification in directory
[`./stdlib-specs`](./stdlib-specs).

Given that the specification of the standard library is not verified by
Gobra itself, it is possible to introduce errors when writing the
specification for these methods. Thus, any extensions to the specification
require extreme care from the author of the extensions and from code reviewers.
26 changes: 26 additions & 0 deletions gobra/stdlib-specs/encoding/encoding_spec.gobra
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// Copyright 2025 ETH Zurich
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// +gobra

package encoding

type TextUnmarshaler interface {
pred Mem()

preserves Mem() && acc(text)
ensures err != nil ==> err.ErrorMem()
decreases
UnmarshalText(text []byte) (err error)
}
28 changes: 28 additions & 0 deletions gobra/stdlib-specs/flag/flag_spec.gobra
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// +gobra

package flag

type Value interface {
pred Mem()

preserves Mem()
decreases
String() string

preserves Mem()
ensures err != nil ==> err.ErrorMem()
decreases
Set(string) (err error)
}
32 changes: 32 additions & 0 deletions gobra/stdlib-specs/fmt/fmt_spec.gobra
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// Copyright 2025 ETH Zurich
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// +gobra

package fmt

import "github.com/scionproto/scion/gobra/utils/"

preserves forall i int :: { &v[i] } 0 <= i && i < len(v) ==>
acc(&v[i], utils.ReadPerm)
decreases
func Sprintf(format string, v ...interface{}) string

type Stringer interface {
pred Mem()

preserves Mem()
decreases
String() string
}
55 changes: 55 additions & 0 deletions gobra/stdlib-specs/hash/hash_spec.gobra
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
// Copyright 2025 ETH Zurich
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// +gobra

package hash

import "github.com/scionproto/scion/gobra/utils/"

type Hash interface {
pred Mem()

// Implementations of Hash must also implement io.Writer.
preserves Mem()
preserves acc(p, utils.ReadPerm)
ensures 0 <= n && n <= len(p)
// The documentation of this interface states that the Write
// method in implementations of Hash may never fail.
// (https://pkg.go.dev/hash#Hash)
ensures err == nil && n == len(p)
ensures Size() == old(Size()) + len(p)
decreases
Write(p []byte) (n int, err error)

preserves acc(Mem(), utils.ReadPerm)
requires acc(b)
ensures acc(res) && len(res) == len(b) + Size()
decreases
Sum(b []byte) (res []byte)

preserves Mem()
decreases
Reset()

requires Mem()
ensures 0 <= res
decreases
pure Size() (res int)

requires Mem()
ensures 0 <= res
decreases
pure BlockSize() (res int)
}
30 changes: 30 additions & 0 deletions gobra/stdlib-specs/net/ip.gobra
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// Copyright 2025 ETH Zurich
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// +gobra

package net

// IP address lengths (bytes).
const (
IPv4len = 4
IPv6len = 16
)

type IP []byte

pred (ip IP) Mem() {
(len(ip) == IPv4len || len(ip) == IPv6len) &&
forall i int :: { &ip[i] } 0 <= i && i < len(ip) ==> acc(&ip[i])
}
21 changes: 21 additions & 0 deletions gobra/stdlib-specs/net/iprawsock.gobra
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// Copyright 2025 ETH Zurich
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// +gobra

package net

ensures err != nil ==> err.ErrorMem()
decreases
func SplitHostPort(hostport string) (host string, port string, err error)
62 changes: 62 additions & 0 deletions gobra/stdlib-specs/net/netip/netip_spec.gobra
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
// Copyright 2025 ETH Zurich
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// +gobra

package netip

import "github.com/scionproto/scion/gobra/utils/"

type Addr struct {
privateField utils.PrivateField
}

type AddrPort struct {
privateField utils.PrivateField
}

decreases
func (ip Addr) String() string

ensures err != nil ==> err.ErrorMem()
decreases
func ParseAddr(s string) (addr Addr, err error)

decreases
func AddrFrom4(addr [4]byte) Addr

decreases
func AddrFrom16(addr [16]byte) Addr

decreases
func (ip Addr) Unmap() Addr

decreases
func (ip Addr) IsValid() (res bool)

decreases
func (ip Addr) Is4() bool

decreases
func (ip Addr) Is6() bool

ensures acc(b)
decreases
func (ip Addr) AsSlice() (b []byte)

decreases
func (p AddrPort) IsValid() (res bool)

decreases
func (p AddrPort) Addr() Addr
Loading
Loading