chiark / gitweb /
rand.c, pgen.c: Invalidate random generators from pgen events.
authorMark Wooding <mdw@distorted.org.uk>
Fri, 9 Nov 2018 17:23:00 +0000 (17:23 +0000)
committerMark Wooding <mdw@distorted.org.uk>
Sat, 10 Nov 2018 01:30:58 +0000 (01:30 +0000)
I've not actually seen a crash from a Python program which keeps hold of
the random generator from a prime-generation event and tries to use it
after the operation has finished, but it was certainly possible.

Arrange for the event object to retain the random generator object (so
it always hands out the same one when requested), and invalidates it
when the event is itself invalidated.

This also involves messing with the `grand' code to cope with the idea
of invalidated random generators.

pgen.c
rand.c

diff --git a/pgen.c b/pgen.c
index 8b5f6b23e10d2212ab6273a28bbf4c43acb925a7..c1795f792436d836fdce36ad4b27c58f4b50920c 100644 (file)
--- a/pgen.c
+++ b/pgen.c
@@ -379,6 +379,7 @@ static PyTypeObject rabin_pytype_skel = {
 
 typedef struct pgevent_pyobj {
   PyObject_HEAD
+  PyObject *r;
   pgen_event *ev;
 } pgevent_pyobj;
 
@@ -388,18 +389,30 @@ static PyTypeObject *pgevent_pytype;
 static PyObject *pgevent_pywrap(pgen_event *ev)
 {
   pgevent_pyobj *o = PyObject_New(pgevent_pyobj, pgevent_pytype);
-  o->ev = ev;
+  o->ev = ev; o->r = 0;
   return ((PyObject *)o);
 }
 
 static CONVFUNC(pgevent, pgen_event *, PGEVENT_EV)
 
-static void pgevent_kill(PyObject *me) { PGEVENT_EV(me) = 0; }
-static void pgevent_pydealloc(PyObject *me) { FREEOBJ(me); }
+static void pgevent_kill(PyObject *me)
+{
+  pgevent_pyobj *ev = (pgevent_pyobj *)me;
+
+  ev->ev = 0;
+  if (ev->r) GRAND_R(ev->r) = 0;
+}
+
+static void pgevent_pydealloc(PyObject *me)
+{
+  pgevent_pyobj *ev = (pgevent_pyobj *)me;
+  if (ev->r) Py_DECREF(ev->r);
+  FREEOBJ(me);
+}
 
 #define PGEVENT_CHECK(me) do {                                         \
   if (!PGEVENT_EV(me)) {                                               \
-    PyErr_SetString(PyExc_ValueError, "event object is dead");         \
+    PyErr_SetString(PyExc_ValueError, "event object is no longer valid"); \
     return (0);                                                                \
   }                                                                    \
 } while (0)
@@ -417,7 +430,13 @@ static PyObject *peget_tests(PyObject *me, void *hunoz)
   { PGEVENT_CHECK(me); return (PyInt_FromLong(PGEVENT_EV(me)->tests)); }
 
 static PyObject *peget_rng(PyObject *me, void *hunoz)
-  { PGEVENT_CHECK(me); return (grand_pywrap(PGEVENT_EV(me)->r, 0)); }
+{
+  pgevent_pyobj *ev = (pgevent_pyobj *)me;
+
+  PGEVENT_CHECK(me);
+  if (!ev->r) ev->r = grand_pywrap(ev->ev->r, 0);
+  Py_INCREF(ev->r); return ((PyObject *)ev->r);
+}
 
 static int peset_x(PyObject *me, PyObject *xobj, void *hunoz)
 {
diff --git a/rand.c b/rand.c
index 9da7f663cccc4b0a0d81faebbf82a92e8ac38923..e2082375a2a39f2bf41ff9700ddba1ec6591c48c 100644 (file)
--- a/rand.c
+++ b/rand.c
@@ -69,15 +69,25 @@ PyObject *grand_pywrap(grand *r, unsigned f)
 
 CONVFUNC(grand, grand *, GRAND_R)
 
+static int grand_check(PyObject *me)
+{
+  if (!GRAND_R(me)) VALERR("random generator object is no longer valid");
+  return (0);
+end:
+  return (-1);
+}
+
 static PyObject *grmeth_byte(PyObject *me, PyObject *arg)
 {
   if (!PyArg_ParseTuple(arg, ":byte")) return (0);
+  if (grand_check(me)) return (0);
   return (PyInt_FromLong(grand_byte(GRAND_R(me))));
 }
 
 static PyObject *grmeth_word(PyObject *me, PyObject *arg)
 {
   if (!PyArg_ParseTuple(arg, ":word")) return (0);
+  if (grand_check(me)) return (0);
   return (getulong(grand_word(GRAND_R(me))));
 }
 
@@ -88,6 +98,7 @@ static PyObject *grmeth_range(PyObject *me, PyObject *arg)
   mp *y = 0;
 
   if (!PyArg_ParseTuple(arg, "O:range", &m)) return (0);
+  if (grand_check(me)) return (0);
   if (PyInt_Check(m)) {
     long mm = PyInt_AS_LONG(m);
     if (mm <= 0)
@@ -118,6 +129,7 @@ static PyObject *grmeth_mp(PyObject *me, PyObject *arg, PyObject *kw)
   if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&|O&:mp", kwlist,
                                   convszt, &l, convmpw, &o))
     goto end;
+  if (grand_check(me)) return (0);
   if (l < MPW_BITS && (o >> l)) VALERR("or mask too large");
   return (mp_pywrap(mprand(MP_NEW, l, GRAND_R(me), o)));
 end:
@@ -130,6 +142,7 @@ static PyObject *grmeth_block(PyObject *me, PyObject *arg)
   PyObject *rc = 0;
 
   if (!PyArg_ParseTuple(arg, "O&:block", convulong, &n)) goto end;
+  if (grand_check(me)) return (0);
   rc = bytestring_pywrap(0, n);
   grand_fill(GRAND_R(me), PyString_AS_STRING(rc), n);
 end:
@@ -138,8 +151,7 @@ end:
 
 static int checkop(grand *r, unsigned op, const char *what)
 {
-  if (r->ops->misc(r, GRAND_CHECK, op))
-    return (0);
+  if (r->ops->misc(r, GRAND_CHECK, op)) return (0);
   PyErr_Format(PyExc_TypeError, "operation %s not supported", what);
   return (-1);
 }
@@ -149,7 +161,7 @@ static PyObject *grmeth_seedint(PyObject *me, PyObject *arg)
   int i;
   grand *r = GRAND_R(me);
   if (!PyArg_ParseTuple(arg, "i:seedint", &i) ||
-      checkop(r, GRAND_SEEDINT, "seedint"))
+      grand_check(me) || checkop(r, GRAND_SEEDINT, "seedint"))
     goto end;
   r->ops->misc(r, GRAND_SEEDINT, i);
   RETURN_ME;
@@ -162,7 +174,7 @@ static PyObject *grmeth_seedword(PyObject *me, PyObject *arg)
   uint32 u;
   grand *r = GRAND_R(me);
   if (!PyArg_ParseTuple(arg, "O&:seedword", convu32, &u) ||
-      checkop(r, GRAND_SEEDUINT32, "seedword"))
+      grand_check(me) || checkop(r, GRAND_SEEDUINT32, "seedword"))
     goto end;
   r->ops->misc(r, GRAND_SEEDUINT32, u);
   RETURN_ME;
@@ -176,7 +188,7 @@ static PyObject *grmeth_seedblock(PyObject *me, PyObject *arg)
   Py_ssize_t n;
   grand *r = GRAND_R(me);
   if (!PyArg_ParseTuple(arg, "s#:seedblock", &p, &n) ||
-      checkop(r, GRAND_SEEDBLOCK, "seedblock"))
+      grand_check(me) || checkop(r, GRAND_SEEDBLOCK, "seedblock"))
     goto end;
   r->ops->misc(r, GRAND_SEEDBLOCK, p, (size_t)n);
   RETURN_ME;
@@ -190,7 +202,7 @@ static PyObject *grmeth_seedmp(PyObject *me, PyObject *arg)
   mp *xx;
   grand *r = GRAND_R(me);
   if (!PyArg_ParseTuple(arg, "O:seedmp", &x) ||
-      checkop(r, GRAND_SEEDMP, "seedmp") ||
+      grand_check(me) || checkop(r, GRAND_SEEDMP, "seedmp") ||
       (xx = getmp(x)) == 0)
     goto end;
   r->ops->misc(r, GRAND_SEEDMP, xx);
@@ -207,7 +219,7 @@ static PyObject *grmeth_seedrand(PyObject *me, PyObject *arg, PyObject *kw)
   grand *rr = &rand_global;
   if (!PyArg_ParseTupleAndKeywords(arg, kw, "|O&:seedrand", kwlist,
                                   convgrand, &rr) ||
-      checkop(r, GRAND_SEEDRAND, "seedrand"))
+      grand_check(me) || checkop(r, GRAND_SEEDRAND, "seedrand"))
     goto end;
   r->ops->misc(r, GRAND_SEEDRAND, rr);
   RETURN_ME;
@@ -223,6 +235,7 @@ static PyObject *grmeth_mask(PyObject *me, PyObject *arg)
   PyObject *rc;
 
   if (!PyArg_ParseTuple(arg, "s#:mask", &p, &sz)) return (0);
+  if (grand_check(me)) return (0);
   rc = bytestring_pywrap(0, sz);
   q = PyString_AS_STRING(rc);
   GR_FILL(r, q, sz);
@@ -233,16 +246,15 @@ static PyObject *grmeth_mask(PyObject *me, PyObject *arg)
 static void grand_pydealloc(PyObject *me)
 {
   grand_pyobj *g = (grand_pyobj *)me;
-  if (g->f & f_freeme)
-    GR_DESTROY(g->r);
+  if ((g->f & f_freeme) && g->r) GR_DESTROY(g->r);
   FREEOBJ(me);
 }
 
 static PyObject *grget_name(PyObject *me, void *hunoz)
-  { return (PyString_FromString(GRAND_R(me)->ops->name)); }
+  { return (grand_check(me) ? 0 : PyString_FromString(GRAND_R(me)->ops->name)); }
 
 static PyObject *grget_cryptop(PyObject *me, void *hunoz)
-  { return (getbool(GRAND_R(me)->ops->f & GRAND_CRYPTO)); }
+  { return (grand_check(me) ? 0 : getbool(GRAND_R(me)->ops->f & GRAND_CRYPTO)); }
 
 static PyGetSetDef grand_pygetset[] = {
 #define GETSETNAME(op, name) gr##op##_##name