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>
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 5d4282909d1abea54aa4210e0393c1ce9de93ff4..94603100b23c03e7f78a8e06cceab018676922ef 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 ddec6ffb7f8ce62ed69df2364bc2fd6a4458648b..5a323b0dbc31e20fd560bd6e60bc4da64550eddc 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