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>
Fri, 22 Nov 2019 22:18:11 +0000 (22:18 +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.

(cherry picked from commit d65d80d7c096e6afc500270ee29909f64869e5dc)

pgen.c
rand.c

diff --git a/pgen.c b/pgen.c
index 5d42829..9460310 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 ddec6ff..5a323b0 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