593 lines
12 KiB
C
593 lines
12 KiB
C
/*
|
|
* Scyther : An automatic verifier for security protocols.
|
|
* Copyright (C) 2007 Cas Cremers
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation; either version 2
|
|
* of the License, or (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
*/
|
|
|
|
/**
|
|
* @file depend.c
|
|
* \brief interface for graph code from the viewpoint of events.
|
|
*
|
|
*/
|
|
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include "depend.h"
|
|
#include "term.h"
|
|
#include "system.h"
|
|
#include "binding.h"
|
|
#include "warshall.h"
|
|
#include "debug.h"
|
|
#include "error.h"
|
|
|
|
/*
|
|
* Generic structures
|
|
* ---------------------------------------------------------------
|
|
*/
|
|
//! Event dependency structure
|
|
struct depeventgraph
|
|
{
|
|
//! Flag denoting what it was made for (newrun|newbinding)
|
|
int fornewrun;
|
|
//! Number of runs;
|
|
int runs;
|
|
//! System where it derives from
|
|
System sys;
|
|
//! Number of nodes
|
|
int n;
|
|
//! Rowsize
|
|
int rowsize;
|
|
//! Graph structure
|
|
unsigned int *G;
|
|
//! Zombie dummy push
|
|
int zombie;
|
|
//! Previous graph
|
|
struct depeventgraph *prev;
|
|
};
|
|
|
|
//! Pointer shorthard
|
|
typedef struct depeventgraph *Depeventgraph;
|
|
|
|
/*
|
|
* External
|
|
* ---------------------------------------------------------------
|
|
*/
|
|
|
|
extern Protocol INTRUDER; //!< The intruder protocol
|
|
extern Role I_M; //!< special role; precedes all other events always
|
|
|
|
/*
|
|
* Globals
|
|
* ---------------------------------------------------------------
|
|
*/
|
|
|
|
Depeventgraph currentdepgraph = NULL;
|
|
|
|
/*
|
|
* Default code
|
|
* ---------------------------------------------------------------
|
|
*/
|
|
|
|
//! Default init
|
|
void
|
|
dependInit (const System sys)
|
|
{
|
|
currentdepgraph = NULL;
|
|
}
|
|
|
|
//! Pring
|
|
void
|
|
dependPrint ()
|
|
{
|
|
Depeventgraph dg;
|
|
|
|
eprintf ("Printing DependEvent stack, top first.\n\n");
|
|
for (dg = currentdepgraph; dg != NULL; dg = dg->prev)
|
|
{
|
|
eprintf ("%i nodes, %i rowsize, %i zombies, %i runs: created for new ",
|
|
dg->n, dg->rowsize, dg->zombie, dg->runs);
|
|
if (dg->fornewrun)
|
|
{
|
|
eprintf ("run");
|
|
}
|
|
else
|
|
{
|
|
eprintf ("binding");
|
|
}
|
|
eprintf ("\n");
|
|
}
|
|
eprintf ("\n");
|
|
#ifdef DEBUG
|
|
{
|
|
int n1;
|
|
int r1;
|
|
int o1;
|
|
|
|
r1 = 0;
|
|
o1 = 0;
|
|
eprintf ("Printing dependency graph.\n");
|
|
eprintf ("Y axis nodes comes before X axis node.\n");
|
|
for (n1 = 0; n1 < nodeCount (); n1++)
|
|
{
|
|
int n2;
|
|
int r2;
|
|
int o2;
|
|
|
|
if ((n1 - o1) >= currentdepgraph->sys->runs[r1].rolelength)
|
|
{
|
|
o1 += currentdepgraph->sys->runs[r1].rolelength;
|
|
r1++;
|
|
eprintf ("\n");
|
|
}
|
|
r2 = 0;
|
|
o2 = 0;
|
|
eprintf ("%5i : ", n1);
|
|
for (n2 = 0; n2 < nodeCount (); n2++)
|
|
{
|
|
if ((n2 - o2) >= currentdepgraph->sys->runs[r2].rolelength)
|
|
{
|
|
o2 += currentdepgraph->sys->runs[r2].rolelength;
|
|
r2++;
|
|
eprintf (" ");
|
|
}
|
|
eprintf ("%i", getNode (n1, n2));
|
|
}
|
|
eprintf ("\n");
|
|
|
|
}
|
|
eprintf ("\n");
|
|
}
|
|
#endif
|
|
}
|
|
|
|
//! Default cleanup
|
|
void
|
|
dependDone (const System sys)
|
|
{
|
|
if (currentdepgraph != NULL)
|
|
{
|
|
globalError++;
|
|
eprintf ("\n\n");
|
|
dependPrint ();
|
|
globalError--;
|
|
error
|
|
("depgraph stack (depend.c) not empty at dependDone, bad iteration?");
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Libs
|
|
* ---------------------------------------------------------------
|
|
*/
|
|
|
|
//! Convert from event to node in a graph (given that sys is set)
|
|
int
|
|
eventtonode (const Depeventgraph dgx, const int r, const int e)
|
|
{
|
|
int i;
|
|
int n;
|
|
|
|
n = 0;
|
|
for (i = 0; i < dgx->sys->maxruns; i++)
|
|
{
|
|
if (i == r)
|
|
{
|
|
// this run
|
|
#ifdef DEBUG
|
|
if (dgx->sys->runs[i].rolelength <= e)
|
|
{
|
|
error ("Bad offset for eventtonode");
|
|
}
|
|
#endif
|
|
return (n + e);
|
|
}
|
|
else
|
|
{
|
|
// not this run, add offset
|
|
n += dgx->sys->runs[i].rolelength;
|
|
}
|
|
}
|
|
error ("Bad offset (run number too high?) for eventtonode");
|
|
return 0;
|
|
}
|
|
|
|
//! Return the number of nodes in a graph
|
|
int
|
|
countnodes (const Depeventgraph dgx)
|
|
{
|
|
int i;
|
|
int nodes;
|
|
|
|
nodes = 0;
|
|
for (i = 0; i < dgx->sys->maxruns; i++)
|
|
{
|
|
nodes += dgx->sys->runs[i].rolelength;
|
|
}
|
|
return nodes;
|
|
}
|
|
|
|
//! Graph size given the number of nodes
|
|
unsigned int
|
|
getGraphSize (const Depeventgraph dgx)
|
|
{
|
|
return (dgx->n * dgx->rowsize);
|
|
}
|
|
|
|
//! Create graph from sys
|
|
Depeventgraph
|
|
dependCreate (const System sys)
|
|
{
|
|
Depeventgraph dgnew;
|
|
|
|
dgnew = (Depeventgraph) MALLOC (sizeof (struct depeventgraph));
|
|
dgnew->sys = sys;
|
|
dgnew->fornewrun = true;
|
|
dgnew->runs = sys->maxruns;
|
|
dgnew->zombie = 0;
|
|
dgnew->prev = NULL;
|
|
dgnew->n = countnodes (dgnew); // count nodes works on ->sys
|
|
dgnew->rowsize = WORDSIZE (dgnew->n);
|
|
dgnew->G = (unsigned int *) CALLOC (1, getGraphSize (dgnew) * sizeof (unsigned int)); // works on ->n and ->rowsize
|
|
|
|
return dgnew;
|
|
}
|
|
|
|
//! Copy graph from current one
|
|
Depeventgraph
|
|
dependCopy (const Depeventgraph dgold)
|
|
{
|
|
Depeventgraph dgnew;
|
|
|
|
// Copy old to new
|
|
dgnew = (Depeventgraph) MALLOC (sizeof (struct depeventgraph));
|
|
memcpy ((void *) dgnew, (void *) dgold,
|
|
(size_t) sizeof (struct depeventgraph));
|
|
|
|
// New copy
|
|
dgnew->fornewrun = false;
|
|
dgnew->zombie = 0;
|
|
|
|
// copy inner graph
|
|
dgnew->G =
|
|
(unsigned int *) MALLOC (getGraphSize (dgold) * sizeof (unsigned int));
|
|
memcpy ((void *) dgnew->G, (void *) dgold->G,
|
|
getGraphSize (dgold) * sizeof (unsigned int));
|
|
|
|
return dgnew;
|
|
}
|
|
|
|
//! Destroy graph
|
|
void
|
|
dependDestroy (const Depeventgraph dgold)
|
|
{
|
|
FREE (dgold->G);
|
|
FREE (dgold);
|
|
}
|
|
|
|
//! push graph to stack (generic)
|
|
void
|
|
dependPushGeneric (Depeventgraph dgnew)
|
|
{
|
|
dgnew->prev = currentdepgraph;
|
|
currentdepgraph = dgnew;
|
|
}
|
|
|
|
//! restore graph from stack (generic)
|
|
void
|
|
dependPopGeneric (void)
|
|
{
|
|
Depeventgraph dgprev;
|
|
|
|
dgprev = currentdepgraph->prev;
|
|
dependDestroy (currentdepgraph);
|
|
currentdepgraph = dgprev;
|
|
}
|
|
|
|
// Dependencies from role order
|
|
void
|
|
dependDefaultRoleOrder (void)
|
|
{
|
|
int r;
|
|
|
|
for (r = 0; r < currentdepgraph->sys->maxruns; r++)
|
|
{
|
|
int e;
|
|
|
|
for (e = 1; e < currentdepgraph->sys->runs[r].rolelength; e++)
|
|
{
|
|
setDependEvent (r, e - 1, r, e);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Dependencies fro bindings order
|
|
void
|
|
dependDefaultBindingOrder (void)
|
|
{
|
|
List bl;
|
|
|
|
for (bl = currentdepgraph->sys->bindings; bl != NULL; bl = bl->next)
|
|
{
|
|
Binding b;
|
|
|
|
b = (Binding) bl->data;
|
|
if (valid_binding (b))
|
|
{
|
|
int r1, e1, r2, e2;
|
|
|
|
r1 = b->run_from;
|
|
e1 = b->ev_from;
|
|
r2 = b->run_to;
|
|
e2 = b->ev_to;
|
|
if (!((r1 == r2) && (e1 == e2)))
|
|
{
|
|
// Not a self-binding
|
|
setDependEvent (r1, e1, r2, e2);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//! Construct graph dependencies from sys
|
|
/**
|
|
* uses currentdepgraph->sys
|
|
*/
|
|
void
|
|
dependFromSys (void)
|
|
{
|
|
dependDefaultRoleOrder ();
|
|
dependDefaultBindingOrder ();
|
|
}
|
|
|
|
//! Detect whether the graph has a cycle. If so, a node can get to itself (through the cycle)
|
|
int
|
|
hasCycle ()
|
|
{
|
|
int n;
|
|
|
|
for (n = 0; n < currentdepgraph->n; n++)
|
|
{
|
|
if (getNode (n, n))
|
|
{
|
|
return true;
|
|
}
|
|
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
* Public Code
|
|
* ---------------------------------------------------------------
|
|
*/
|
|
|
|
//! get node
|
|
int
|
|
getNode (const int n1, const int n2)
|
|
{
|
|
return BIT (currentdepgraph->G + currentdepgraph->rowsize * n1, n2);
|
|
}
|
|
|
|
//! set node
|
|
void
|
|
setNode (const int n1, const int n2)
|
|
{
|
|
SETBIT (currentdepgraph->G + currentdepgraph->rowsize * n1, n2);
|
|
}
|
|
|
|
//! Count nodes
|
|
int
|
|
nodeCount (void)
|
|
{
|
|
return countnodes (currentdepgraph);
|
|
}
|
|
|
|
/*
|
|
* Simple setting
|
|
*/
|
|
void
|
|
setDependEvent (const int r1, const int e1, const int r2, const int e2)
|
|
{
|
|
int n1, n2;
|
|
|
|
n1 = eventtonode (currentdepgraph, r1, e1);
|
|
n2 = eventtonode (currentdepgraph, r2, e2);
|
|
setNode (n1, n2);
|
|
}
|
|
|
|
/*
|
|
* Simple testing
|
|
*/
|
|
int
|
|
isDependEvent (const int r1, const int e1, const int r2, const int e2)
|
|
{
|
|
int n1, n2;
|
|
|
|
n1 = eventtonode (currentdepgraph, r1, e1);
|
|
n2 = eventtonode (currentdepgraph, r2, e2);
|
|
return getNode (n1, n2);
|
|
}
|
|
|
|
//! create new graph after adding runs or events (new number of nodes)
|
|
void
|
|
dependPushRun (const System sys)
|
|
{
|
|
#ifdef DEBUG
|
|
debug (5, "Push dependGraph for new run\n");
|
|
#endif
|
|
dependPushGeneric (dependCreate (sys));
|
|
dependFromSys ();
|
|
}
|
|
|
|
//! restore graph to state after previous run add
|
|
void
|
|
dependPopRun (void)
|
|
{
|
|
if (!currentdepgraph->fornewrun)
|
|
{
|
|
globalError++;
|
|
dependPrint ();
|
|
globalError--;
|
|
error ("Trying to pop graph created for new binding.");
|
|
}
|
|
#ifdef DEBUG
|
|
debug (5, "Pop dependGraph for new run\n");
|
|
#endif
|
|
dependPopGeneric ();
|
|
}
|
|
|
|
//! create new graph by adding event bindings
|
|
/*
|
|
* The push code returns true or false: if false, the operation fails because
|
|
* it there is now a cycle in the graph, and there is no need to pop the
|
|
* result.
|
|
*/
|
|
int
|
|
dependPushEvent (const int r1, const int e1, const int r2, const int e2)
|
|
{
|
|
if (isDependEvent (r2, e2, r1, e1))
|
|
{
|
|
// Adding would imply a cycle, so we won't do that.
|
|
#ifdef DEBUG
|
|
if (DEBUGL (3))
|
|
{
|
|
eprintf ("Cycle detected for binding %i,%i -> %i,%i.\n", r1, e1, r2,
|
|
e2);
|
|
}
|
|
if (DEBUGL (5))
|
|
{
|
|
dependPrint ();
|
|
}
|
|
#endif
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
// No immediate cycle: new graph, return true TODO disabled
|
|
if ((1 == 1) && (((r1 == r2) && (e1 == e2))
|
|
|| isDependEvent (r1, e1, r2, e2)))
|
|
{
|
|
// if n->n or the binding already existed, no changes
|
|
// no change: add zombie
|
|
currentdepgraph->zombie += 1;
|
|
#ifdef DEBUG
|
|
debug (5, "Push dependGraph for new event (zombie push)\n");
|
|
if (DEBUGL (5))
|
|
{
|
|
globalError++;
|
|
eprintf ("r%ii%i --> r%ii%i\n", r1, e1, r2, e2);
|
|
globalError--;
|
|
}
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
// change: make new graph copy of the old one
|
|
dependPushGeneric (dependCopy (currentdepgraph));
|
|
// really new?
|
|
if (!isDependEvent (r1, e1, r2, e2))
|
|
{
|
|
// add new binding
|
|
setDependEvent (r1, e1, r2, e2);
|
|
// recompute closure
|
|
transitive_closure (currentdepgraph->G, currentdepgraph->n);
|
|
// check for cycles
|
|
if (hasCycle ())
|
|
{
|
|
//warning ("Cycle slipped undetected by the reverse check.");
|
|
// Closure introduced cycle, undo it
|
|
dependPopEvent ();
|
|
return false;
|
|
}
|
|
#ifdef DEBUG
|
|
debug (5, "Push dependGraph for new event (real push)\n");
|
|
if (DEBUGL (5))
|
|
{
|
|
globalError++;
|
|
eprintf ("r%ii%i --> r%ii%i\n", r1, e1, r2, e2);
|
|
globalError--;
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
}
|
|
|
|
//! restore graph to state before previous binding add
|
|
void
|
|
dependPopEvent (void)
|
|
{
|
|
if (currentdepgraph->zombie > 0)
|
|
{
|
|
// zombie pushed
|
|
#ifdef DEBUG
|
|
debug (5, "Pop dependGraph for new event (zombie pop)\n");
|
|
#endif
|
|
currentdepgraph->zombie -= 1;
|
|
}
|
|
else
|
|
{
|
|
if (currentdepgraph->fornewrun)
|
|
{
|
|
globalError++;
|
|
dependPrint ();
|
|
globalError--;
|
|
error ("Trying to pop graph created for new run.");
|
|
}
|
|
else
|
|
{
|
|
// real graph
|
|
#ifdef DEBUG
|
|
debug (5, "Pop dependGraph for new event (real pop)\n");
|
|
#endif
|
|
dependPopGeneric ();
|
|
}
|
|
}
|
|
}
|
|
|
|
//! Current event to node
|
|
int
|
|
eventNode (const int r, const int e)
|
|
{
|
|
return eventtonode (currentdepgraph, r, e);
|
|
}
|
|
|
|
//! Iterate over any preceding events
|
|
int
|
|
iteratePrecedingEvents (const System sys, int (*func) (int run, int ev),
|
|
const int run, const int ev)
|
|
{
|
|
int run2;
|
|
|
|
for (run2 = 0; run2 < sys->maxruns; run2++)
|
|
{
|
|
int ev2;
|
|
|
|
for (ev2 = 0; ev2 < sys->runs[run2].step; ev2++)
|
|
{
|
|
if (isDependEvent (run2, ev2, run, ev))
|
|
{
|
|
if (!func (run2, ev2))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
}
|