2018-10-31
Institute of Informatics
Chair for Software and Computational Systems
Prof. Dr. Dirk Beyer

Application of Software Verification
to OpenBSD Network Modules

Moritz Buhl
m.buhl@campus.lmu.de
11549061

Period of Writing:2018-04-09 – 2018-09-30
External Advisor:Alexander Bluhm
Advisor:Thomas Lemberger
Supervisor:Prof. Dr. Dirk Beyer
Project Files:https://www.moritzbuhl.de/bachelor-thesis

0/18

Introduction

Bugs are present long after the initial implementation.
Is it possible to find errors in the network stack automatically?

PatchLength calculationMemory handlingNULL pointersLocation
003_mplsXXXmpls_input()
005_ahoptsXXah_massage_header()[sic]
006_ipseclenXIPsec callback()s
006_prevhdrXrip6_input(), frag6_input()
007_etheripXip_etherip_input()
010_ahauthXIPsec _input()s
010_pipexXpipex_pptp_input()
Recent bugs in the OpenBSD network functions for input processing
Missing NULL check (003_mpls)
  switch (rt_mpls->mpls_operation) {
  case MPLS_OP_POP:

  m = mpls_shim_pop(m);
+ if (m == NULL)
+   goto done;
Missing free() (003_mpls)
- if (m == NULL)
+ if (m == NULL) {
+    if_put(ifp);
     return;
+ }
Missing length check (003_mpls)
+ if (m->m_len < sizeof(u_char) &&
+   (m = m_pullup(m, sizeof(u_char))) == NULL)
+     return (NULL);

Background

SVG von CPAchecker funktionality Workflow of CPAchecker
CFA (control-flow automata) (L, l₀, G)
L := locations
l₀ := initial locatioin
G := control-flow edges
SVG eines CFA von ip_deliver()
int ip_deliver(struct mbuf **mp,
  int *offp, int nxt, int af) {




  const struct protosw *psw;
  int naf = af;






  while (nxt != IPPROTO_DONE) {






    psw = &inetsw[ip_protox[nxt]];

    nxt = (*psw->pr_input)(mp, offp, nxt, af);
    af = naf;
  }
  return nxt;
}

Configurable Program Analysis

A CPA 𝔻 = (D, ⇝, merge, stop)
An abstract Domain D = (C, 𝜀,⟦·⟧)
A semi-lattice 𝜀 = (E, ⊤, ⊥, ⊑, ⊔)
Concretization function ⟦·⟧: E → 2^C
The transfer relation ⇝ ⊆ E ⨯ G ⨯ E
The operator merge: E ⨯ E → E
The operator stop: E ⨯ 2^E → 𝔹

Algorith: CPA(𝔻, e0)

 1: Input: a CPA 𝔻 and an initial state e0 ∈ E
 2: Output: a set reached of reachable states

 3: waitlist := { e0 }
 4: reached := { e0 }
 5: while waitlist ≠ {}  do
 6:   pop e from waitlist
 7:   for each e' with e' ⇝ e do
 8:     for each e'' ∈ reached do
 9:       en := merge(e', e'')
10:       if en = e then
11:         waitlist := (waitlist ∪ {en}) ∪ {e''}
12:         reached := (reached ∪ {en}) ∪ {e''}
13:     if ¬stop(e', reached) then
14:       waitlist := waitlist ∪ {e'}
15:       reached := reached ∪ {e'}
16: return reached

Symbolic Memory Graphs

SVG von SMG OpenBSD lists Points-to: ● → (addr, target)
Has-value: □ → (offset, type)

Network

SVG von OpenBSD net flow

OpenBSD Memory Buffers

SVG von OpenBSD mbuf
 1 struct mbuf *
 2 m_get(int nowait, int type)
 3 {
 4     struct mbuf *m;
 5
 6     if (nowait == M_NOWAIT && __VERIFIER_nondet_bool()) {
 7         return (NULL);
 8     }
 9     m = malloc(sizeof(struct mbuf), 0, 0);
10
11     m->m_type = type;
12     m->m_next = NULL;
13     m->m_nextpkt = NULL;
14     m->m_data = m->m_dat;
15     m->m_flags = 0;
16
17     return (m);
18 }
Use of the __VERIFIER_nondet_ function in the Mbuf API
SVG von OpenBSD net input
SVG von OpenBSD net input

Adjustments

Preprocessing a Module:

due to problems with multiple files

$ cat *.c > module.c
#ifndef CPA_bpfilter_h
#define CPA_bpfilter_h 1
#include "bpfilter.h"
#endif
#ifndef CPA_pf_h
#define CPA_pf_h 1
#include "pf.h"
#endif
...
Removing assembler:

(not supported by CPAchecker)

#define _SYS_PERCPU_H_
#define COUNTERS_BOOT_MEMORY(_name, _n)
#define COUNTERS_BOOT_INITIALIZER(_) ((void *)0)
FP dereference:

due to a bug in the SMG CPA

psw = &inetsw[ip_protox[nxt]];
nxt = (*psw->pr_input)(mp, offp, nxt, af);

Patches

Optimize Constant Binary Expressions:
struct mutex m = { ((void *)0),
    ((((6)) > 0 && ((6)) < 12)? 12 : ((6))), 0}
SVG von conditional expression as cfa
Multiple Array Declarations:
extern u_char ip6_protox[];
u_char ip6_protox[256];
Others:

extern declarations in block Scope
memroy functions are not POSIX
Adjustments to the svcomp18--memory-safety configuration

Ethernet over IP Bug:

Called from ip_deliver()

   int ip6_etherip_input(...) {
     ...
 1   if (!etherip_allow &&
         (m->m_flags & (M_AUTH|M_CONF)) == 0) {
 2     m_freem(m);
 3     etheripstat.etherips_pdrops++;
 4 -   return IPPROTO_NONE;
 5 +   return IPPROTO_DONE;
 6   }
     ...
Initialization/Stubs:
 1 int main() {
 2   struct mbuf *m;
 3   int len, off;
 4
 5   ip_init();
 6   ip6_init();
 7   m = m_gethdr(0, MT_HEADER);
 8   m_tag_init(m);
 9
10   len = __VERIFIER_nondet_int();
11   __VERIFIER_assume(len > 0);
12   __VERIFIER_assume(len <= MHLEN);
13   off = __VERIFIER_nondet_int();
14   __VERIFIER_assume(off > 0);
15   __VERIFIER_assume(off <= len);
16
17   m->m_hdr.mh_len = len;
18   m->M_dat.MH.MH_pkthdr.len = len;
19   ip_deliver(&m, &off, IPPROTO_IP, AF_INET6);
20 }
PatchTimeMax MemoryResult
007_etherip314.712s2329MB✗
007_etherip (patched)313.433s2577MB✓

In total 11850 LOC

Discussion

  1. Reports: Witness program is the only readable output
    dot breaks at 65K characters on a label
  2. C quirks: Conversion from C to CFA has corner cases
    Using CPAchecker on more projects will resolve this problem
  3. Usage on multi processing problems?

References

Dirk Beyer et al. "Software model checking via large-block encoding".
  FMCAD 2009.
Dirk Beyer and M. Erkan Keremoglu. "CPAchecker: A Tool for Configurable Software Verification".
  CAV 2011.
Dirk Beyer, Thomas A. Henzinger, and Grégory Théoduloz. "Configurable Software Verification:
  Concretizing the Convergence of Model Checking and Program Analysis".
  CAV 2007.
Kamil Dudka, Petr Peringer, and Tomás Vojnar. "Byte-Precise Verification of Low-Level List
  Manipulation".
  Tech. rep. FIT-TR-2012-04.

Questions?