summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDave Brolley <brolley@redhat.com>2009-10-09 11:09:12 -0400
committerDave Brolley <brolley@redhat.com>2009-10-09 11:09:12 -0400
commit2865d17a48d055b3aef6e45506292908800cdb21 (patch)
tree216ef4616108250518e0bd52b5c37a489f2906e0
parent47f025139d1c2e75781cdab40dc9195396133754 (diff)
downloadsystemtap-steved-2865d17a48d055b3aef6e45506292908800cdb21.tar.gz
systemtap-steved-2865d17a48d055b3aef6e45506292908800cdb21.tar.xz
systemtap-steved-2865d17a48d055b3aef6e45506292908800cdb21.zip
Generate safety net assertions in probe function not authorized for unprivileged users.
2009-10-08 Dave Brolley <brolley@redhat.com> * elaborate.h (emit_unprivileged_assertion): New virtual method of deriv ed_probe. (emit_process_owner_assertion): New static method of derived_probe. (check_unprivileged): New virtual method of derived_probe_builder. (match_node::unprivileged_ok): Removed. (match_node::allow_unprivileged): Removed. (match_node::unprivileged_allowed): Removed. * elaborate.cxx (translate.h): #include it. (emit_unprivileged_assertion): New virtual method of derived_probe. (emit_process_owner_assertion): New static method of derived_probe. (check_unprivileged): New virtual method of derived_probe_builder. (match_node::unprivileged_ok): Removed. (match_node::allow_unprivileged): Removed. (match_node::unprivileged_allowed): Removed. (find_and_build): Don't check for unprivileged restrictions here. Call t he builder's check_unprivileged method. (alias_expansion_builder::check_unprivileged): New virtual method. * tapset-been.cxx (be_derived_probe::emit_unprivileged_assertion): New v irtual method. (be_builder::check_unprivileged): Likewise. (never_derived_probe::emit_unprivileged_assertion): Likewise. (never_builder::check_unprivileged): Likewise. (register_tapset_been): Don't call allow_unprivileged. * tapset-itrace.cxx (itrace_derived_probe::emit_unprivileged_assertion): New virtual method. (itrace_builder::check_unprivileged): Likewise. (register_tapset_itrace): Don't call allow_unprivileged. * tapset-utrace.cxx (utrace_derived_probe::emit_unprivileged_assertion): New virtual method. (utrace_builder::check_unprivileged): Likewise. (register_tapset_utrace): Don't call allow_unprivileged. * tapset-timer.cxx (timer_derived_probe::emit_unprivileged_assertion): N ew virtual method. (timer_builder::check_unprivileged): Likewise. (register_tapset_timers): Don't call allow_unprivileged. * tapsets.cxx (uprobe_derived_probe::emit_unprivileged_assertion): New v irtual method. (uprobe_builder::check_unprivileged): Likewise. (register_standard_tapsets): Don't call allow_unprivileged. (register_statement_variants): Remove unprivileged_ok_p parameter. Don't call allow_unprivileged. (register_function_variants): Likewise. (register_function_and_statement_variants): Likewise. (register_patterns): Don't call allow_unprivileged. * translate.cxx (emit_probe): Call v->emit_unprivileged_assertion.
-rw-r--r--elaborate.cxx69
-rw-r--r--elaborate.h14
-rw-r--r--tapset-been.cxx21
-rw-r--r--tapset-itrace.cxx19
-rw-r--r--tapset-timers.cxx40
-rw-r--r--tapset-utrace.cxx27
-rw-r--r--tapsets.cxx62
-rw-r--r--translate.cxx3
8 files changed, 164 insertions, 91 deletions
diff --git a/elaborate.cxx b/elaborate.cxx
index 01c6e3cd..e8795a6c 100644
--- a/elaborate.cxx
+++ b/elaborate.cxx
@@ -9,6 +9,7 @@
#include "config.h"
#include "elaborate.h"
+#include "translate.h"
#include "parse.h"
#include "tapsets.h"
#include "session.h"
@@ -132,10 +133,50 @@ derived_probe::sole_location () const
}
+void
+derived_probe::emit_unprivileged_assertion (translator_output* o)
+{
+ // Emit code which will cause compilation to fail if it is compiled in
+ // unprivileged mode.
+ o->newline() << "#ifndef STP_PRIVILEGED";
+ o->newline() << "#error Internal Error: Probe ";
+ probe::printsig (o->line());
+ o->line() << " generated in --unprivileged mode";
+ o->newline() << "#endif";
+}
+
+
+void
+derived_probe::emit_process_owner_assertion (translator_output* o)
+{
+ // Emit code which will abort should the current target not belong to the
+ // user in unprivileged mode.
+ o->newline() << "#ifndef STP_PRIVILEGED";
+ o->newline(1) << "if (! is_myproc ()) {";
+ o->newline(1) << "snprintf(c->error_buffer, sizeof(c->error_buffer),";
+ o->newline() << " \"Internal Error: Process %d does not belong to user %d in probe %s in --unprivileged mode\",";
+ o->newline() << " current->tgid, _stp_uid, c->probe_point);";
+ o->newline() << "c->last_error = c->error_buffer;";
+ o->newline() << "goto out;";
+ o->newline(-1) << "}";
+ o->newline(-1) << "#endif";
+}
+
// ------------------------------------------------------------------------
// Members of derived_probe_builder
+void
+derived_probe_builder::check_unprivileged (const systemtap_session & sess,
+ const literal_map_t & parameters)
+{
+ // By default, probes are not allowed for unprivileged users.
+ if (sess.unprivileged)
+ {
+ throw semantic_error (string("probe point is not allowed for unprivileged users"));
+ }
+}
+
bool
derived_probe_builder::get_param (std::map<std::string, literal*> const & params,
const std::string& key,
@@ -261,7 +302,6 @@ match_key::globmatch(match_key const & other) const
// ------------------------------------------------------------------------
match_node::match_node()
- : unprivileged_ok (false)
{
}
@@ -303,19 +343,6 @@ match_node::bind_num(string const & k)
return bind(match_key(k).with_number());
}
-match_node*
-match_node::allow_unprivileged (bool b)
-{
- unprivileged_ok = b;
- return this;
-}
-
-bool
-match_node::unprivileged_allowed () const
-{
- return unprivileged_ok;
-}
-
void
match_node::find_and_build (systemtap_session& s,
probe* p, probe_point *loc, unsigned pos,
@@ -340,18 +367,11 @@ match_node::find_and_build (systemtap_session& s,
param_map[loc->components[i]->functor] = loc->components[i]->arg;
// maybe 0
- // Are we compiling for unprivileged users? */
- if (s.unprivileged)
- {
- // Is this probe point ok for unprivileged users?
- if (! unprivileged_allowed ())
- throw semantic_error (string("probe point is not allowed for unprivileged users"));
- }
-
// Iterate over all bound builders
for (unsigned k=0; k<ends.size(); k++)
{
derived_probe_builder *b = ends[k];
+ b->check_unprivileged (s, param_map);
b->build (s, p, loc, param_map, results);
}
}
@@ -573,6 +593,11 @@ alias_expansion_builder
}
return false;
}
+
+ // No action required. The actual probes will be checked when they are
+ // built.
+ virtual void check_unprivileged (const systemtap_session & sess,
+ const literal_map_t & parameters) {}
};
diff --git a/elaborate.h b/elaborate.h
index cd60b8bb..d41608cb 100644
--- a/elaborate.h
+++ b/elaborate.h
@@ -146,11 +146,19 @@ struct derived_probe: public probe
// From within unparser::emit_probe, emit any extra processing block
// for this probe.
+ virtual void emit_unprivileged_assertion (translator_output*);
+ // From within unparser::emit_probe, emit any unprivileged mode
+ // checking for this probe.
+
public:
static void emit_common_header (translator_output* o);
// from c_unparser::emit_common_header
// XXX: probably can move this stuff to a probe_group::emit_module_decls
+ static void emit_process_owner_assertion (translator_output*);
+ // From within unparser::emit_probe, emit a check that the current
+ // process belongs to the user.
+
virtual bool needs_global_locks () { return true; }
// by default, probes need locks around global variables
};
@@ -199,6 +207,8 @@ struct derived_probe_builder
probe_point* location,
literal_map_t const & parameters,
std::vector<derived_probe*> & finished_results) = 0;
+ virtual void check_unprivileged (const systemtap_session & sess,
+ const literal_map_t & parameters);
virtual ~derived_probe_builder() {}
virtual void build_no_more (systemtap_session &) {}
@@ -236,7 +246,6 @@ match_node
typedef std::map<match_key, match_node*>::iterator sub_map_iterator_t;
sub_map_t sub;
std::vector<derived_probe_builder*> ends;
- bool unprivileged_ok;
public:
match_node();
@@ -251,9 +260,6 @@ match_node
match_node* bind_str(std::string const & k);
match_node* bind_num(std::string const & k);
void bind(derived_probe_builder* e);
-
- match_node* allow_unprivileged (bool b = true);
- bool unprivileged_allowed () const;
};
// ------------------------------------------------------------------------
diff --git a/tapset-been.cxx b/tapset-been.cxx
index 99b59574..002bf66a 100644
--- a/tapset-been.cxx
+++ b/tapset-been.cxx
@@ -52,6 +52,10 @@ struct be_derived_probe: public derived_probe
return a->priority < b->priority;
}
+ // No assertion need be emitted, since these probes are allowed for
+ // unprivileged users.
+ void emit_unprivileged_assertion (translator_output*) {}
+
bool needs_global_locks () { return false; }
// begin/end probes don't need locks around global variables, since
// they aren't run concurrently with any other probes
@@ -86,6 +90,10 @@ struct be_builder: public derived_probe_builder
finished_results.push_back
(new be_derived_probe(base, location, type, priority));
}
+
+ // No action required. These probes are allowed for unprivileged users.
+ virtual void check_unprivileged (const systemtap_session & sess,
+ const literal_map_t & parameters) {}
};
@@ -188,6 +196,8 @@ struct never_derived_probe: public derived_probe
never_derived_probe (probe* p): derived_probe (p) {}
never_derived_probe (probe* p, probe_point* l): derived_probe (p, l) {}
void join_group (systemtap_session&) { /* thus no probe_group */ }
+ void emit_unprivileged_assertion (translator_output*) {}
+
};
@@ -202,6 +212,10 @@ struct never_builder: public derived_probe_builder
{
finished_results.push_back(new never_derived_probe(base, location));
}
+
+ // No action required. This probe is allowed for unprivileged users.
+ virtual void check_unprivileged (const systemtap_session & sess,
+ const literal_map_t & parameters) {}
};
@@ -216,28 +230,21 @@ register_tapset_been(systemtap_session& s)
match_node* root = s.pattern_root;
root->bind(TOK_BEGIN)
- ->allow_unprivileged()
->bind(new be_builder(BEGIN));
root->bind_num(TOK_BEGIN)
- ->allow_unprivileged()
->bind(new be_builder(BEGIN));
root->bind(TOK_END)
- ->allow_unprivileged()
->bind(new be_builder(END));
root->bind_num(TOK_END)
- ->allow_unprivileged()
->bind(new be_builder(END));
root->bind(TOK_ERROR)
- ->allow_unprivileged()
->bind(new be_builder(ERROR));
root->bind_num(TOK_ERROR)
- ->allow_unprivileged()
->bind(new be_builder(ERROR));
root->bind(TOK_NEVER)
- ->allow_unprivileged()
->bind(new never_builder());
}
diff --git a/tapset-itrace.cxx b/tapset-itrace.cxx
index 9fc59d42..512a70b0 100644
--- a/tapset-itrace.cxx
+++ b/tapset-itrace.cxx
@@ -44,6 +44,8 @@ struct itrace_derived_probe: public derived_probe
bool hp, string &pn, int64_t pd, int ss
);
void join_group (systemtap_session& s);
+
+ void emit_unprivileged_assertion (translator_output*);
};
@@ -79,6 +81,15 @@ itrace_derived_probe::itrace_derived_probe (systemtap_session &s,
void
+itrace_derived_probe::emit_unprivileged_assertion (translator_output* o)
+{
+ // These probes are allowed for unprivileged users, but only in the
+ // context of processes which they own.
+ emit_process_owner_assertion (o);
+}
+
+
+void
itrace_derived_probe::join_group (systemtap_session& s)
{
if (! s.itrace_derived_probes)
@@ -121,6 +132,10 @@ struct itrace_builder: public derived_probe_builder
single_step
));
}
+
+ // No action required. These probes are allowed for unprivileged users.
+ virtual void check_unprivileged (const systemtap_session & sess,
+ const literal_map_t & parameters) {}
};
@@ -300,16 +315,12 @@ register_tapset_itrace(systemtap_session& s)
derived_probe_builder *builder = new itrace_builder();
root->bind_str(TOK_PROCESS)->bind(TOK_INSN)
- ->allow_unprivileged()
->bind(builder);
root->bind_num(TOK_PROCESS)->bind(TOK_INSN)
- ->allow_unprivileged()
->bind(builder);
root->bind_str(TOK_PROCESS)->bind(TOK_INSN)->bind(TOK_BLOCK)
- ->allow_unprivileged()
->bind(builder);
root->bind_num(TOK_PROCESS)->bind(TOK_INSN)->bind(TOK_BLOCK)
- ->allow_unprivileged()
->bind(builder);
}
diff --git a/tapset-timers.cxx b/tapset-timers.cxx
index 16dcefcb..de57d81a 100644
--- a/tapset-timers.cxx
+++ b/tapset-timers.cxx
@@ -37,6 +37,10 @@ struct timer_derived_probe: public derived_probe
timer_derived_probe (probe* p, probe_point* l,
int64_t i, int64_t r, bool ms=false);
virtual void join_group (systemtap_session& s);
+
+ // No assertion need be emitted, since this probe is allowed for unprivileged
+ // users.
+ void emit_unprivileged_assertion (translator_output*) {}
};
@@ -204,6 +208,10 @@ struct hrtimer_derived_probe: public derived_probe
}
void join_group (systemtap_session& s);
+
+ // No assertion need be emitted, since these probes are allowed for
+ // unprivileged users.
+ void emit_unprivileged_assertion (translator_output*) {}
};
@@ -505,6 +513,9 @@ struct timer_builder: public derived_probe_builder
vector<derived_probe *> & finished_results);
static void register_patterns(systemtap_session& s);
+
+ virtual void check_unprivileged (const systemtap_session & sess,
+ const literal_map_t & parameters);
};
void
@@ -586,6 +597,16 @@ timer_builder::build(systemtap_session & sess,
}
void
+timer_builder::check_unprivileged (const systemtap_session & sess,
+ const literal_map_t & parameters)
+{
+ // All timer probes are allowed except for timer.profile
+ if (has_null_param(parameters, "profile"))
+ derived_probe_builder::check_unprivileged (sess, parameters);
+}
+
+
+void
register_tapset_timers(systemtap_session& s)
{
match_node* root = s.pattern_root;
@@ -594,66 +615,47 @@ register_tapset_timers(systemtap_session& s)
root = root->bind(TOK_TIMER);
root->bind_num("s")
- ->allow_unprivileged()
->bind(builder);
root->bind_num("s")->bind_num("randomize")
- ->allow_unprivileged()
->bind(builder);
root->bind_num("sec")
- ->allow_unprivileged()
->bind(builder);
root->bind_num("sec")->bind_num("randomize")
- ->allow_unprivileged()
->bind(builder);
root->bind_num("ms")
- ->allow_unprivileged()
->bind(builder);
root->bind_num("ms")->bind_num("randomize")
- ->allow_unprivileged()
->bind(builder);
root->bind_num("msec")
- ->allow_unprivileged()
->bind(builder);
root->bind_num("msec")->bind_num("randomize")
- ->allow_unprivileged()
->bind(builder);
root->bind_num("us")
- ->allow_unprivileged()
->bind(builder);
root->bind_num("us")->bind_num("randomize")
- ->allow_unprivileged()
->bind(builder);
root->bind_num("usec")
- ->allow_unprivileged()
->bind(builder);
root->bind_num("usec")->bind_num("randomize")
- ->allow_unprivileged()
->bind(builder);
root->bind_num("ns")
- ->allow_unprivileged()
->bind(builder);
root->bind_num("ns")->bind_num("randomize")
- ->allow_unprivileged()
->bind(builder);
root->bind_num("nsec")
- ->allow_unprivileged()
->bind(builder);
root->bind_num("nsec")->bind_num("randomize")
- ->allow_unprivileged()
->bind(builder);
root->bind_num("jiffies")
- ->allow_unprivileged()
->bind(builder);
root->bind_num("jiffies")->bind_num("randomize")
- ->allow_unprivileged()
->bind(builder);
root->bind_num("hz")
- ->allow_unprivileged()
->bind(builder);
root->bind("profile")
diff --git a/tapset-utrace.cxx b/tapset-utrace.cxx
index b13dc290..819a2d87 100644
--- a/tapset-utrace.cxx
+++ b/tapset-utrace.cxx
@@ -60,6 +60,8 @@ struct utrace_derived_probe: public derived_probe
bool hp, string &pn, int64_t pd,
enum utrace_derived_probe_flags f);
void join_group (systemtap_session& s);
+
+ void emit_unprivileged_assertion (translator_output*);
};
@@ -195,6 +197,21 @@ utrace_derived_probe::join_group (systemtap_session& s)
void
+utrace_derived_probe::emit_unprivileged_assertion (translator_output* o)
+{
+ // Process end probes are allowed for unprivileged users, even if the process
+ // does not belong to them. They are required to check is_myproc() from within
+ // their probe script before doing anything "dangerous".
+ if (flags == UDPF_END)
+ return;
+
+ // Other process probes are allowed for unprivileged users, but only in the
+ // context of processes which they own.
+ emit_process_owner_assertion (o);
+}
+
+
+void
utrace_var_expanding_visitor::visit_target_symbol_cached (target_symbol* e)
{
// Get the full name of the target symbol.
@@ -624,6 +641,10 @@ struct utrace_builder: public derived_probe_builder
has_path, path, pid,
flags));
}
+
+ // No action required. These probes are allowed for unprivileged users.
+ virtual void check_unprivileged (const systemtap_session & sess,
+ const literal_map_t & parameters) {}
};
@@ -1054,22 +1075,16 @@ register_tapset_utrace(systemtap_session& s)
for (unsigned i = 0; i < roots.size(); ++i)
{
roots[i]->bind(TOK_BEGIN)
- ->allow_unprivileged()
->bind(builder);
roots[i]->bind(TOK_END)
- ->allow_unprivileged()
->bind(builder);
roots[i]->bind(TOK_THREAD)->bind(TOK_BEGIN)
- ->allow_unprivileged()
->bind(builder);
roots[i]->bind(TOK_THREAD)->bind(TOK_END)
- ->allow_unprivileged()
->bind(builder);
roots[i]->bind(TOK_SYSCALL)
- ->allow_unprivileged()
->bind(builder);
roots[i]->bind(TOK_SYSCALL)->bind(TOK_RETURN)
- ->allow_unprivileged()
->bind(builder);
}
}
diff --git a/tapsets.cxx b/tapsets.cxx
index 6267f314..88c10f85 100644
--- a/tapsets.cxx
+++ b/tapsets.cxx
@@ -359,14 +359,11 @@ struct dwarf_derived_probe: public derived_probe
// Pattern registration helpers.
static void register_statement_variants(match_node * root,
- dwarf_builder * dw,
- bool unprivileged_ok_p = false);
+ dwarf_builder * dw);
static void register_function_variants(match_node * root,
- dwarf_builder * dw,
- bool unprivileged_ok_p = false);
+ dwarf_builder * dw);
static void register_function_and_statement_variants(match_node * root,
- dwarf_builder * dw,
- bool unprivileged_ok_p = false);
+ dwarf_builder * dw);
static void register_patterns(systemtap_session& s);
protected:
@@ -411,6 +408,8 @@ struct uprobe_derived_probe: public dwarf_derived_probe
{}
void join_group (systemtap_session& s);
+
+ void emit_unprivileged_assertion (translator_output*);
};
struct dwarf_derived_probe_group: public derived_probe_group
@@ -2945,28 +2944,25 @@ dwarf_derived_probe::printargs(std::ostream &o) const
void
dwarf_derived_probe::register_statement_variants(match_node * root,
- dwarf_builder * dw,
- bool unprivileged_ok_p)
+ dwarf_builder * dw)
{
- root->allow_unprivileged(unprivileged_ok_p)->bind(dw);
+ root->bind(dw);
}
void
dwarf_derived_probe::register_function_variants(match_node * root,
- dwarf_builder * dw,
- bool unprivileged_ok_p)
-{
- root->allow_unprivileged(unprivileged_ok_p)->bind(dw);
- root->bind(TOK_INLINE)->allow_unprivileged(unprivileged_ok_p)->bind(dw);
- root->bind(TOK_CALL)->allow_unprivileged(unprivileged_ok_p)->bind(dw);
- root->bind(TOK_RETURN)->allow_unprivileged(unprivileged_ok_p)->bind(dw);
- root->bind(TOK_RETURN)->bind_num(TOK_MAXACTIVE)->allow_unprivileged(unprivileged_ok_p)->bind(dw);
+ dwarf_builder * dw)
+{
+ root->bind(dw);
+ root->bind(TOK_INLINE)->bind(dw);
+ root->bind(TOK_CALL)->bind(dw);
+ root->bind(TOK_RETURN)->bind(dw);
+ root->bind(TOK_RETURN)->bind_num(TOK_MAXACTIVE)->bind(dw);
}
void
dwarf_derived_probe::register_function_and_statement_variants(match_node * root,
- dwarf_builder * dw,
- bool unprivileged_ok_p)
+ dwarf_builder * dw)
{
// Here we match 4 forms:
//
@@ -2975,10 +2971,10 @@ dwarf_derived_probe::register_function_and_statement_variants(match_node * root,
// .statement("foo")
// .statement(0xdeadbeef)
- register_function_variants(root->bind_str(TOK_FUNCTION), dw, unprivileged_ok_p);
- register_function_variants(root->bind_num(TOK_FUNCTION), dw, unprivileged_ok_p);
- register_statement_variants(root->bind_str(TOK_STATEMENT), dw, unprivileged_ok_p);
- register_statement_variants(root->bind_num(TOK_STATEMENT), dw, unprivileged_ok_p);
+ register_function_variants(root->bind_str(TOK_FUNCTION), dw);
+ register_function_variants(root->bind_num(TOK_FUNCTION), dw);
+ register_statement_variants(root->bind_str(TOK_STATEMENT), dw);
+ register_statement_variants(root->bind_num(TOK_STATEMENT), dw);
}
void
@@ -2997,15 +2993,12 @@ dwarf_derived_probe::register_patterns(systemtap_session& s)
root->bind(TOK_KERNEL)->bind_str(TOK_FUNCTION)->bind_str(TOK_LABEL)
->bind(dw);
- register_function_and_statement_variants(root->bind_str(TOK_PROCESS), dw, true/*unprivileged_ok_p*/);
+ register_function_and_statement_variants(root->bind_str(TOK_PROCESS), dw);
root->bind_str(TOK_PROCESS)->bind_str(TOK_FUNCTION)->bind_str(TOK_LABEL)
- ->allow_unprivileged()
->bind(dw);
root->bind_str(TOK_PROCESS)->bind_str(TOK_MARK)
- ->allow_unprivileged()
->bind(dw);
root->bind_str(TOK_PROCESS)->bind_num(TOK_MARK)
- ->allow_unprivileged()
->bind(dw);
}
@@ -4307,6 +4300,15 @@ uprobe_derived_probe::join_group (systemtap_session& s)
}
+void
+uprobe_derived_probe::emit_unprivileged_assertion (translator_output* o)
+{
+ // These probes are allowed for unprivileged users, but only in the
+ // context of processes which they own.
+ emit_process_owner_assertion (o);
+}
+
+
struct uprobe_builder: public derived_probe_builder
{
uprobe_builder() {}
@@ -4327,6 +4329,10 @@ struct uprobe_builder: public derived_probe_builder
finished_results.push_back(new uprobe_derived_probe(base, location, process, address, rr));
}
+
+ // No action required. These probes are allowed for unprivileged users.
+ virtual void check_unprivileged (const systemtap_session & sess,
+ const literal_map_t & parameters) {}
};
@@ -6220,11 +6226,9 @@ register_standard_tapsets(systemtap_session & s)
// XXX: user-space starter set
s.pattern_root->bind_num(TOK_PROCESS)
->bind_num(TOK_STATEMENT)->bind(TOK_ABSOLUTE)
- ->allow_unprivileged()
->bind(new uprobe_builder ());
s.pattern_root->bind_num(TOK_PROCESS)
->bind_num(TOK_STATEMENT)->bind(TOK_ABSOLUTE)->bind(TOK_RETURN)
- ->allow_unprivileged()
->bind(new uprobe_builder ());
// kernel tracepoint probes
diff --git a/translate.cxx b/translate.cxx
index a3246d9c..1278a8d5 100644
--- a/translate.cxx
+++ b/translate.cxx
@@ -1622,6 +1622,9 @@ c_unparser::emit_probe (derived_probe* v)
o->newline(1) << "& c->probe_locals." << v->name << ";";
o->newline(-1) << "(void) l;"; // make sure "l" is marked used
+ // Emit runtime safety net for unprivileged mode.
+ v->emit_unprivileged_assertion (o);
+
o->newline() << "#ifdef STP_TIMING";
o->newline() << "c->statp = & time_" << v->basest()->name << ";";
o->newline() << "#endif";