diff -r 4c2f3240ad65 Lib/test/test_dbm_gnu.py --- a/Lib/test/test_dbm_gnu.py Thu Jul 17 00:00:26 2014 +0300 +++ b/Lib/test/test_dbm_gnu.py Mon Jul 21 07:55:10 2014 +0300 @@ -93,5 +93,61 @@ self.assertEqual(str(cm.exception), "GDBM object has already been closed") + +def with_broken_gdbm(func): + def wrapper(self): + g = gdbm.open(filename, 'c') + try: + g[b'a'] = b'b' + open(filename, 'wb').close() + with self.assertRaisesRegex(gdbm.error, '^gdbm fatal:'): + func(self, g) + finally: + g.close() + unlink(filename) + return wrapper + +class TestFatalErrors(unittest.TestCase): + @with_broken_gdbm + def test_len(self, g): + len(g) + + @with_broken_gdbm + def test_getitem(self, g): + g[b'a'] + + @with_broken_gdbm + def test_setitem(self, g): + g[b'a'] = b'c' + + @with_broken_gdbm + def test_delitem(self, g): + del g[b'a'] + + @with_broken_gdbm + def test_keys(self, g): + g.keys() + + @with_broken_gdbm + def test_firstkey(self, g): + g.firstkey() + + @with_broken_gdbm + def test_nextkey(self, g): + g.nextkey(b'a') + + @with_broken_gdbm + def test_reorganize(self, g): + g.reorganize() + + @with_broken_gdbm + def test_get(self, g): + g.get(b'a', b'c') + + @with_broken_gdbm + def test_setdefault(self, g): + g.setdefault(b'a', b'c') + + if __name__ == '__main__': unittest.main() diff -r 4c2f3240ad65 Modules/_gdbmmodule.c --- a/Modules/_gdbmmodule.c Thu Jul 17 00:00:26 2014 +0300 +++ b/Modules/_gdbmmodule.c Mon Jul 21 07:55:10 2014 +0300 @@ -9,6 +9,7 @@ #include #include #include +#include #include "gdbm.h" #if defined(WIN32) && !defined(__CYGWIN__) @@ -45,6 +46,30 @@ static PyObject *DbmError; +static jmp_buf dbm_jmp_env; +static int dbm_longjmp_level = 0; + +static void +dbm_fatal(const char *msg, ...) +{ + PyErr_Format(DbmError, "gdbm fatal: %s", msg); + if (dbm_longjmp_level == 1) + longjmp(dbm_jmp_env, 1); + else + Py_FatalError("gdbm fatal"); +} + +#define CATCH_FATAL(dp, ret, stmt) do { \ + dbm_longjmp_level++; \ + if (setjmp(dbm_jmp_env)) { \ + dbm_longjmp_level--; \ + dp->di_dbm = NULL; \ + return ret; \ + } \ + stmt; \ + dbm_longjmp_level--; \ + } while(0) + PyDoc_STRVAR(gdbm_object__doc__, "This object represents a GDBM database.\n\ GDBM objects behave like mappings (dictionaries), except that keys and\n\ @@ -65,7 +90,9 @@ return NULL; dp->di_size = -1; errno = 0; - if ((dp->di_dbm = gdbm_open(file, 0, flags, mode, NULL)) == 0) { + CATCH_FATAL(dp, NULL, + dp->di_dbm = gdbm_open(file, 0, flags, mode, (void *)dbm_fatal)); + if (dp->di_dbm == 0) { if (errno != 0) PyErr_SetFromErrno(DbmError); else @@ -78,14 +105,6 @@ /* Methods */ -static void -dbm_dealloc(dbmobject *dp) -{ - if (dp->di_dbm) - gdbm_close(dp->di_dbm); - PyObject_Del(dp); -} - static Py_ssize_t dbm_length(dbmobject *dp) { @@ -100,11 +119,14 @@ okey.dptr=NULL; size = 0; - for (key=gdbm_firstkey(dp->di_dbm); key.dptr; - key = gdbm_nextkey(dp->di_dbm,okey)) { + CATCH_FATAL(dp, -1, + key = gdbm_firstkey(dp->di_dbm)); + while (key.dptr) { size++; if(okey.dsize) free(okey.dptr); okey=key; + CATCH_FATAL(dp, -1, + key = gdbm_nextkey(dp->di_dbm,okey)); } dp->di_size = size; } @@ -125,7 +147,8 @@ "GDBM object has already been closed"); return NULL; } - drec = gdbm_fetch(dp->di_dbm, krec); + CATCH_FATAL(dp, NULL, + drec = gdbm_fetch(dp->di_dbm, krec)); if (drec.dptr == 0) { PyErr_SetObject(PyExc_KeyError, key); return NULL; @@ -161,6 +184,7 @@ dbm_ass_sub(dbmobject *dp, PyObject *v, PyObject *w) { datum krec, drec; + int rc; if (!PyArg_Parse(v, "s#", &krec.dptr, &krec.dsize) ) { PyErr_SetString(PyExc_TypeError, @@ -174,7 +198,9 @@ } dp->di_size = -1; if (w == NULL) { - if (gdbm_delete(dp->di_dbm, krec) < 0) { + CATCH_FATAL(dp, -1, + rc = gdbm_delete(dp->di_dbm, krec)); + if (rc < 0) { PyErr_SetObject(PyExc_KeyError, v); return -1; } @@ -186,7 +212,9 @@ return -1; } errno = 0; - if (gdbm_store(dp->di_dbm, krec, drec, GDBM_REPLACE) < 0) { + CATCH_FATAL(dp, -1, + rc = gdbm_store(dp->di_dbm, krec, drec, GDBM_REPLACE)); + if (rc < 0) { if (errno != 0) PyErr_SetFromErrno(DbmError); else @@ -234,13 +262,23 @@ static PyObject * dbm_close(dbmobject *dp, PyObject *unused) { - if (dp->di_dbm) - gdbm_close(dp->di_dbm); + GDBM_FILE di_dbm = dp->di_dbm; dp->di_dbm = NULL; + if (di_dbm) + CATCH_FATAL(dp, NULL, + gdbm_close(di_dbm)); Py_INCREF(Py_None); return Py_None; } +static void +dbm_dealloc(dbmobject *dp) +{ + PyObject *res = dbm_close(dp, NULL); + Py_XDECREF(res); + PyObject_Del(dp); +} + /* XXX Should return a set or a set view */ PyDoc_STRVAR(dbm_keys__doc__, "keys() -> list_of_keys\n\ @@ -263,7 +301,8 @@ if (v == NULL) return NULL; - key = gdbm_firstkey(dp->di_dbm); + CATCH_FATAL(dp, NULL, + key = gdbm_firstkey(dp->di_dbm)); while (key.dptr) { item = PyBytes_FromStringAndSize(key.dptr, key.dsize); if (item == NULL) { @@ -278,7 +317,8 @@ Py_DECREF(v); return NULL; } - nextkey = gdbm_nextkey(dp->di_dbm, key); + CATCH_FATAL(dp, NULL, + nextkey = gdbm_nextkey(dp->di_dbm, key)); free(key.dptr); key = nextkey; } @@ -291,6 +331,7 @@ dbmobject *dp = (dbmobject *)self; datum key; Py_ssize_t size; + int rc; if ((dp)->di_dbm == NULL) { PyErr_SetString(DbmError, @@ -313,7 +354,9 @@ key.dptr = PyBytes_AS_STRING(arg); key.dsize = PyBytes_GET_SIZE(arg); } - return gdbm_exists(dp->di_dbm, key); + CATCH_FATAL(dp, -1, + rc = gdbm_exists(dp->di_dbm, key)); + return rc; } static PySequenceMethods dbm_as_sequence = { @@ -343,7 +386,8 @@ datum key; check_dbmobject_open(dp); - key = gdbm_firstkey(dp->di_dbm); + CATCH_FATAL(dp, NULL, + key = gdbm_firstkey(dp->di_dbm)); if (key.dptr) { v = PyBytes_FromStringAndSize(key.dptr, key.dsize); free(key.dptr); @@ -375,7 +419,8 @@ if (!PyArg_ParseTuple(args, "s#:nextkey", &key.dptr, &key.dsize)) return NULL; check_dbmobject_open(dp); - nextkey = gdbm_nextkey(dp->di_dbm, key); + CATCH_FATAL(dp, NULL, + nextkey = gdbm_nextkey(dp->di_dbm, key)); if (nextkey.dptr) { v = PyBytes_FromStringAndSize(nextkey.dptr, nextkey.dsize); free(nextkey.dptr); @@ -398,9 +443,13 @@ static PyObject * dbm_reorganize(dbmobject *dp, PyObject *unused) { + int rc; + check_dbmobject_open(dp); errno = 0; - if (gdbm_reorganize(dp->di_dbm) < 0) { + CATCH_FATAL(dp, NULL, + rc = gdbm_reorganize(dp->di_dbm)); + if (rc < 0) { if (errno != 0) PyErr_SetFromErrno(DbmError); else @@ -420,7 +469,8 @@ dbm_sync(dbmobject *dp, PyObject *unused) { check_dbmobject_open(dp); - gdbm_sync(dp->di_dbm); + CATCH_FATAL(dp, NULL, + gdbm_sync(dp->di_dbm)); Py_INCREF(Py_None); return Py_None; }