Skip to content

Commit ffaeb3d

Browse files
authored
gh-127081: add critical sections to dbm objects (gh-132749)
1 parent 17d0fec commit ffaeb3d

File tree

5 files changed

+229
-41
lines changed

5 files changed

+229
-41
lines changed
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Fix libc thread safety issues with :mod:`dbm` by performing stateful
2+
operations in critical sections.

Modules/_dbmmodule.c

Lines changed: 66 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ typedef struct {
6969
#include "clinic/_dbmmodule.c.h"
7070

7171
#define check_dbmobject_open(v, err) \
72+
_Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED((v)) \
7273
if ((v)->di_dbm == NULL) { \
7374
PyErr_SetString(err, "DBM object has already been closed"); \
7475
return NULL; \
@@ -116,7 +117,7 @@ dbm_dealloc(PyObject *self)
116117
}
117118

118119
static Py_ssize_t
119-
dbm_length(PyObject *self)
120+
dbm_length_lock_held(PyObject *self)
120121
{
121122
dbmobject *dp = dbmobject_CAST(self);
122123
_dbm_state *state = PyType_GetModuleState(Py_TYPE(dp));
@@ -138,8 +139,18 @@ dbm_length(PyObject *self)
138139
return dp->di_size;
139140
}
140141

142+
static Py_ssize_t
143+
dbm_length(PyObject *self)
144+
{
145+
Py_ssize_t result;
146+
Py_BEGIN_CRITICAL_SECTION(self);
147+
result = dbm_length_lock_held(self);
148+
Py_END_CRITICAL_SECTION();
149+
return result;
150+
}
151+
141152
static int
142-
dbm_bool(PyObject *self)
153+
dbm_bool_lock_held(PyObject *self)
143154
{
144155
dbmobject *dp = dbmobject_CAST(self);
145156
_dbm_state *state = PyType_GetModuleState(Py_TYPE(dp));
@@ -170,8 +181,18 @@ dbm_bool(PyObject *self)
170181
return 1;
171182
}
172183

184+
static int
185+
dbm_bool(PyObject *self)
186+
{
187+
int result;
188+
Py_BEGIN_CRITICAL_SECTION(self);
189+
result = dbm_bool_lock_held(self);
190+
Py_END_CRITICAL_SECTION();
191+
return result;
192+
}
193+
173194
static PyObject *
174-
dbm_subscript(PyObject *self, PyObject *key)
195+
dbm_subscript_lock_held(PyObject *self, PyObject *key)
175196
{
176197
datum drec, krec;
177198
Py_ssize_t tmp_size;
@@ -197,8 +218,18 @@ dbm_subscript(PyObject *self, PyObject *key)
197218
return PyBytes_FromStringAndSize(drec.dptr, drec.dsize);
198219
}
199220

221+
static PyObject *
222+
dbm_subscript(PyObject *self, PyObject *key)
223+
{
224+
PyObject *result;
225+
Py_BEGIN_CRITICAL_SECTION(self);
226+
result = dbm_subscript_lock_held(self, key);
227+
Py_END_CRITICAL_SECTION();
228+
return result;
229+
}
230+
200231
static int
201-
dbm_ass_sub(PyObject *self, PyObject *v, PyObject *w)
232+
dbm_ass_sub_lock_held(PyObject *self, PyObject *v, PyObject *w)
202233
{
203234
datum krec, drec;
204235
Py_ssize_t tmp_size;
@@ -252,15 +283,26 @@ dbm_ass_sub(PyObject *self, PyObject *v, PyObject *w)
252283
return 0;
253284
}
254285

286+
static int
287+
dbm_ass_sub(PyObject *self, PyObject *v, PyObject *w)
288+
{
289+
int result;
290+
Py_BEGIN_CRITICAL_SECTION(self);
291+
result = dbm_ass_sub_lock_held(self, v, w);
292+
Py_END_CRITICAL_SECTION();
293+
return result;
294+
}
295+
255296
/*[clinic input]
297+
@critical_section
256298
_dbm.dbm.close
257299
258300
Close the database.
259301
[clinic start generated code]*/
260302

261303
static PyObject *
262304
_dbm_dbm_close_impl(dbmobject *self)
263-
/*[clinic end generated code: output=c8dc5b6709600b86 input=046db72377d51be8]*/
305+
/*[clinic end generated code: output=c8dc5b6709600b86 input=4a94f79facbc28ca]*/
264306
{
265307
if (self->di_dbm) {
266308
dbm_close(self->di_dbm);
@@ -270,6 +312,7 @@ _dbm_dbm_close_impl(dbmobject *self)
270312
}
271313

272314
/*[clinic input]
315+
@critical_section
273316
_dbm.dbm.keys
274317
275318
cls: defining_class
@@ -279,7 +322,7 @@ Return a list of all keys in the database.
279322

280323
static PyObject *
281324
_dbm_dbm_keys_impl(dbmobject *self, PyTypeObject *cls)
282-
/*[clinic end generated code: output=f2a593b3038e5996 input=d3706a28fc051097]*/
325+
/*[clinic end generated code: output=f2a593b3038e5996 input=6ddefeadf2a80156]*/
283326
{
284327
PyObject *v, *item;
285328
datum key;
@@ -310,7 +353,7 @@ _dbm_dbm_keys_impl(dbmobject *self, PyTypeObject *cls)
310353
}
311354

312355
static int
313-
dbm_contains(PyObject *self, PyObject *arg)
356+
dbm_contains_lock_held(PyObject *self, PyObject *arg)
314357
{
315358
dbmobject *dp = dbmobject_CAST(self);
316359
datum key, val;
@@ -343,7 +386,18 @@ dbm_contains(PyObject *self, PyObject *arg)
343386
return val.dptr != NULL;
344387
}
345388

389+
static int
390+
dbm_contains(PyObject *self, PyObject *arg)
391+
{
392+
int result;
393+
Py_BEGIN_CRITICAL_SECTION(self);
394+
result = dbm_contains_lock_held(self, arg);
395+
Py_END_CRITICAL_SECTION();
396+
return result;
397+
}
398+
346399
/*[clinic input]
400+
@critical_section
347401
_dbm.dbm.get
348402
cls: defining_class
349403
key: str(accept={str, robuffer}, zeroes=True)
@@ -356,7 +410,7 @@ Return the value for key if present, otherwise default.
356410
static PyObject *
357411
_dbm_dbm_get_impl(dbmobject *self, PyTypeObject *cls, const char *key,
358412
Py_ssize_t key_length, PyObject *default_value)
359-
/*[clinic end generated code: output=b4e55f8b6d482bc4 input=66b993b8349fa8c1]*/
413+
/*[clinic end generated code: output=b4e55f8b6d482bc4 input=1d88a22bb5e55202]*/
360414
{
361415
datum dbm_key, val;
362416
_dbm_state *state = PyType_GetModuleState(cls);
@@ -373,6 +427,7 @@ _dbm_dbm_get_impl(dbmobject *self, PyTypeObject *cls, const char *key,
373427
}
374428

375429
/*[clinic input]
430+
@critical_section
376431
_dbm.dbm.setdefault
377432
cls: defining_class
378433
key: str(accept={str, robuffer}, zeroes=True)
@@ -387,7 +442,7 @@ If key is not in the database, it is inserted with default as the value.
387442
static PyObject *
388443
_dbm_dbm_setdefault_impl(dbmobject *self, PyTypeObject *cls, const char *key,
389444
Py_ssize_t key_length, PyObject *default_value)
390-
/*[clinic end generated code: output=9c2f6ea6d0fb576c input=126a3ff15c5f8232]*/
445+
/*[clinic end generated code: output=9c2f6ea6d0fb576c input=c01510ef7571e13b]*/
391446
{
392447
datum dbm_key, val;
393448
Py_ssize_t tmp_size;
@@ -427,6 +482,7 @@ _dbm_dbm_setdefault_impl(dbmobject *self, PyTypeObject *cls, const char *key,
427482
}
428483

429484
/*[clinic input]
485+
@critical_section
430486
_dbm.dbm.clear
431487
cls: defining_class
432488
/
@@ -436,7 +492,7 @@ Remove all items from the database.
436492

437493
static PyObject *
438494
_dbm_dbm_clear_impl(dbmobject *self, PyTypeObject *cls)
439-
/*[clinic end generated code: output=8d126b9e1d01a434 input=43aa6ca1acb7f5f5]*/
495+
/*[clinic end generated code: output=8d126b9e1d01a434 input=a1aa5d99adfb9656]*/
440496
{
441497
_dbm_state *state = PyType_GetModuleState(cls);
442498
assert(state != NULL);

0 commit comments

Comments
 (0)