Skip to content

Commit

Permalink
Allow for origin-aware sanity checks, so garbage from the network can…
Browse files Browse the repository at this point in the history
… later

be made to result in bad_decode instead of informative_failure.

The sanity macros now look at a value 'made_from_t made_from', which can be
made_from_local or made_from_network. There is a global constant by this
name set to made_from_local, giving the expected behavior. If you hide this
with a made_from which happens to be set to made_from_network, all three
sanity macros will throw bad_decode instead of their ordinary behavior.
ATOMIC and ENCODING vocab types inherit origin_aware to get a public
made_from, and have a new constructor that takes (string, made_from_t).

This uses a variable you can redefine instead of functions you can
redefine/inherit because foo.E() syntax can't work due to not wanting to
evaluate the format argument if the condition is OK.
  • Loading branch information
Timothy Brownawell committed Nov 10, 2008
1 parent 1f3d1a5 commit 77aa1c2
Show file tree
Hide file tree
Showing 13 changed files with 179 additions and 40 deletions.
2 changes: 1 addition & 1 deletion basic_io.hh
Expand Up @@ -61,7 +61,7 @@ namespace basic_io
} token_type;

struct
input_source
input_source : public origin_aware
{
size_t line, col;
std::string const & in;
Expand Down
5 changes: 5 additions & 0 deletions cert.cc
Expand Up @@ -231,6 +231,11 @@ cert::cert(std::string const & s)
{
read_cert(s, *this);
}
cert::cert(std::string const & s, made_from_t m)
: origin_aware(m)
{
read_cert(s, *this);
}

cert::cert(revision_id const & ident,
cert_name const & name,
Expand Down
3 changes: 2 additions & 1 deletion cert.hh
Expand Up @@ -28,12 +28,13 @@ class database;
class project_t;
struct options;

struct cert
struct cert : public origin_aware
{
cert();

// This is to make revision<cert> and manifest<cert> work.
explicit cert(std::string const & s);
cert(std::string const & s, made_from_t m);

cert(revision_id const & ident,
cert_name const & name,
Expand Down
1 change: 1 addition & 0 deletions database.cc
Expand Up @@ -2514,6 +2514,7 @@ database::put_roster_for_revision(revision_id const & new_id,
MM(roster_manifest_id);
make_roster_for_revision(*this, rev, new_id, *ros_writeable, *mm_writeable);
calculate_ident(*ros_writeable, roster_manifest_id);
made_from_t made_from(rev.made_from);
I(rev.new_manifest == roster_manifest_id);
// const'ify the objects, suitable for caching etc.
roster_t_cp ros = ros_writeable;
Expand Down
5 changes: 0 additions & 5 deletions netio.hh
Expand Up @@ -19,11 +19,6 @@
#include "sanity.hh"
#include "string_queue.hh"

struct bad_decode {
bad_decode(i18n_format const & fmt) : what(fmt.str()) {}
std::string what;
};

inline void
require_bytes(std::string const & str,
size_t pos,
Expand Down
5 changes: 4 additions & 1 deletion netsync.cc
Expand Up @@ -2272,7 +2272,10 @@ session::process_data_cmd(netcmd_item_type type,
case revision_item:
{
L(FL("received revision '%s'") % hitem());
if (project.db.put_revision(revision_id(item), revision_data(dat)))
revision_t rev;
rev.made_from = made_from_network;
read_revision(data(dat), rev);
if (project.db.put_revision(revision_id(item), rev))
written_revisions.push_back(revision_id(item));
}
break;
Expand Down
58 changes: 54 additions & 4 deletions revision.cc
Expand Up @@ -119,6 +119,7 @@ revision_t::is_nontrivial() const
}

revision_t::revision_t(revision_t const & other)
: origin_aware(other)
{
/* behave like normal constructor if other is empty */
made_for = made_for_nobody;
Expand Down Expand Up @@ -1822,7 +1823,7 @@ print_revision(basic_io::printer & printer,

void
parse_edge(basic_io::parser & parser,
edge_map & es)
revision_t & rev)
{
shared_ptr<cset> cs(new cset());
MM(*cs);
Expand All @@ -1836,7 +1837,7 @@ parse_edge(basic_io::parser & parser,

parse_cset(parser, *cs);

es.insert(make_pair(old_rev, cs));
rev.edges.insert(make_pair(old_rev, cs));
}


Expand All @@ -1857,9 +1858,9 @@ parse_revision(basic_io::parser & parser,
% tmp);
parser.esym(syms::new_manifest);
parser.hex(tmp);
rev.new_manifest = manifest_id(decode_hexenc(tmp));
rev.new_manifest = manifest_id(decode_hexenc(tmp), made_from_network);
while (parser.symp(syms::old_revision))
parse_edge(parser, rev.edges);
parse_edge(parser, rev);
rev.check_sane();
}

Expand All @@ -1869,6 +1870,10 @@ read_revision(data const & dat,
{
MM(rev);
basic_io::input_source src(dat(), "revision");
// FIXME: maybe this is backwards, and made_from should
// come from @param{dat}?
src.made_from = rev.made_from;
made_from_t made_from(rev.made_from);
basic_io::tokenizer tok(src);
basic_io::parser pars(tok);
parse_revision(pars, rev);
Expand Down Expand Up @@ -1955,6 +1960,51 @@ UNIT_TEST(revision, find_old_new_path_for)
I(foo_bar == find_new_path_for(renames, foo_baz));
}

UNIT_TEST(revision, from_network)
{
char const * bad_revisions[] = {
"",

"format_version \"1\"\n",

"format_version \"1\"\n"
"\n"
"new_manifest [0000000000000000000000000000000000000000]\n",

"format_version \"1\"\n"
"\n"
"new_manifest [000000000000000]\n",

"format_version \"1\"\n"
"\n"
"new_manifest [0000000000000000000000000000000000000000]\n"
"\n"
"old_revision [66ff7f4640593afacdb056fefc069349e7d9ed9e]\n"
"\n"
"rename \"some_file\"\n"
" foo \"x\"\n",

"format_version \"1\"\n"
"\n"
"new_manifest [0000000000000000000000000000000000000000]\n"
"\n"
"old_revision [66ff7f4640593afacdb056fefc069349e7d9ed9e]\n"
"\n"
"rename \"some_file\"\n"
" foo \"some_file\"\n"
};
revision_t rev;
rev.made_from = made_from_network;
for (unsigned i = 0; i < sizeof(bad_revisions)/sizeof(char const*); ++i)
{
UNIT_TEST_CHECKPOINT((string("iteration ")
+ boost::lexical_cast<string>(i)).c_str());
UNIT_TEST_CHECK_THROW(read_revision(data(bad_revisions[i]),
rev),
bad_decode);
}
}

#endif // BUILD_UNIT_TESTS

// Local Variables:
Expand Down
2 changes: 1 addition & 1 deletion revision.hh
Expand Up @@ -47,7 +47,7 @@ class path_restriction;
enum made_for { made_for_nobody, made_for_workspace, made_for_database };

struct
revision_t
revision_t : public origin_aware
{
void check_sane() const;
bool is_merge_node() const;
Expand Down
74 changes: 56 additions & 18 deletions sanity.hh
Expand Up @@ -321,32 +321,70 @@ do { \
#define UNLIKELY(zz) (zz)
#endif

struct bad_decode {
bad_decode(i18n_format const & fmt) : what(fmt.str()) {}
std::string what;
};

enum made_from_t { made_from_local, made_from_network };
made_from_t const made_from = made_from_local;

// Something that knows where it came from, so that its sanity checks
// can throw bad_decode instead of informative_error if it came from
// the network.
class origin_aware
{
public:
made_from_t made_from;
origin_aware() : made_from(made_from_local) {}
origin_aware(made_from_t m) : made_from(m) {}
};

// I is for invariants that "should" always be true
// (if they are wrong, there is a *bug*)
#define I(e) \
do { \
if(UNLIKELY(!(e))) { \
global_sanity.invariant_failure("I("#e")", __FILE__, __LINE__); \
} \
} while(0)
#define I(e) \
do { \
if (UNLIKELY(!(e))) \
{ \
if (made_from == made_from_network) \
throw bad_decode(F("%s:%s : %s") \
% __FILE__ % __LINE__ % "I("#e")"); \
else \
global_sanity.invariant_failure("I("#e")", __FILE__, __LINE__); \
} \
} while (0)

// N is for naughtyness on behalf of the user
// (if they are wrong, the user just did something wrong)
#define N(e, explain)\
do { \
if(UNLIKELY(!(e))) { \
global_sanity.naughty_failure("N("#e")", (explain), __FILE__, __LINE__); \
} \
} while(0)
#define N(e, explain) \
do { \
if (UNLIKELY(!(e))) \
{ \
if (made_from == made_from_network) \
throw bad_decode(F("%s:%s : %s") \
% __FILE__ % __LINE__ % (explain)); \
else \
global_sanity.naughty_failure("N("#e")", \
(explain), \
__FILE__, __LINE__); \
} \
} while (0)

// E is for errors; they are normal (i.e., not a bug), but not necessarily
// attributable to user naughtiness
#define E(e, explain)\
do { \
if(UNLIKELY(!(e))) { \
global_sanity.error_failure("E("#e")", (explain), __FILE__, __LINE__); \
} \
} while(0)
#define E(e, explain) \
do { \
if (UNLIKELY(!(e))) \
{ \
if (made_from == made_from_network) \
throw bad_decode(F("%s:%s : %s") \
% __FILE__ % __LINE__ % (explain)); \
else \
global_sanity.error_failure("E("#e")", \
(explain), \
__FILE__, __LINE__); \
} \
} while (0)

// Last gasp dumps

Expand Down
6 changes: 3 additions & 3 deletions transforms.hh
Expand Up @@ -52,7 +52,7 @@ base64<T> encode_base64(T const & in)

template <typename T>
T decode_base64(base64<T> const & in)
{ return T(xform<Botan::Base64_Decoder>(in())); }
{ return T(xform<Botan::Base64_Decoder>(in()), in.made_from); }

template <typename T>
T decode_base64_as(std::string const & in)
Expand All @@ -67,7 +67,7 @@ void encode_hexenc(T const & in, hexenc<T> & out)

template <typename T>
void decode_hexenc(hexenc<T> const & in, T & out)
{ out = T(xform<Botan::Hex_Decoder>(in())); }
{ out = T(xform<Botan::Hex_Decoder>(in()), in.made_from); }

inline std::string encode_hexenc(std::string const & in)
{ return xform<Botan::Hex_Encoder>(in); }
Expand All @@ -83,7 +83,7 @@ void encode_gzip(T const & in, gzip<T> & out)

template <typename T>
void decode_gzip(gzip<T> const & in, T & out)
{ out = T(xform<Botan::Gzip_Decompression>(in())); }
{ out = T(xform<Botan::Gzip_Decompression>(in()), in.made_from); }

// string variant for netsync
template <typename T>
Expand Down
8 changes: 8 additions & 0 deletions vocab.cc
Expand Up @@ -29,6 +29,7 @@ template <typename INNER>
inline void
verify(hexenc<INNER> const & val)
{
made_from_t made_from(val.made_from);
for (string::const_iterator i = val().begin(); i != val().end(); ++i)
{
N(is_xdigit(*i),
Expand All @@ -43,6 +44,7 @@ verify(hexenc<id> const & val)
if (val().empty())
return;

made_from_t made_from(val.made_from);
N(val().size() == constants::idlen,
F("hex encoded ID '%s' size != %d") % val % constants::idlen);
for (string::const_iterator i = val().begin(); i != val().end(); ++i)
Expand All @@ -59,13 +61,15 @@ verify(id & val)
if (val().empty())
return;

made_from_t made_from(val.made_from);
N(val().size() == constants::idlen_bytes,
F("invalid ID '%s'") % val);
}

inline void
verify(symbol const & val)
{
made_from_t made_from(val.made_from);
for (string::const_iterator i = val().begin(); i != val().end(); ++i)
{
N(is_alnum(*i) || *i == '_',
Expand All @@ -76,6 +80,7 @@ verify(symbol const & val)
inline void
verify(cert_name const & val)
{
made_from_t made_from(val.made_from);
string::size_type pos = val().find_first_not_of(constants::legal_cert_name_bytes);
N(pos == string::npos,
F("bad character '%c' in cert name '%s'") % val().at(pos) % val);
Expand All @@ -84,6 +89,7 @@ verify(cert_name const & val)
inline void
verify(rsa_keypair_id const & val)
{
made_from_t made_from(val.made_from);
string::size_type pos = val().find_first_not_of(constants::legal_key_name_bytes);
N(pos == string::npos,
F("bad character '%c' in key name '%s'") % val().at(pos) % val);
Expand All @@ -102,6 +108,7 @@ verify(netsync_session_key & val)
return;
}

made_from_t made_from(val.made_from);
N(val().size() == constants::netsync_session_key_length_in_bytes,
F("Invalid key length of %d bytes") % val().length());
}
Expand All @@ -115,6 +122,7 @@ verify(netsync_hmac_value & val)
return;
}

made_from_t made_from(val.made_from);
N(val().size() == constants::netsync_hmac_value_length_in_bytes,
F("Invalid hmac length of %d bytes") % val().length());
}
Expand Down
2 changes: 2 additions & 0 deletions vocab.hh
Expand Up @@ -12,6 +12,8 @@

#include <boost/shared_ptr.hpp>

#include "sanity.hh"

// the purpose of this file is to wrap things which are otherwise strings
// in a bit of typesafety, set up enumerations and tuple-types, and
// generally describe the "vocabulary" (nouns anyways) that modules in this
Expand Down

0 comments on commit 77aa1c2

Please sign in to comment.