Skip to content

Commit 0dea0ed

Browse files
committed
PYCBC-1823: Fix pending-exception leak in add_* dict helpers
Changes -------- * Check the return value of PyDict_SetItem/PyDict_SetItemString in add_field (all 3 overloads), add_string_field_if_not_empty, and add_duration_field, reporting a failure via PyErr_WriteUnraisable instead of leaving the exception pending on the thread * Apply the same fix to add_bool_field and add_cpp_core_span_field, changing their return type from void to int to match the rest of the family * Add a shared banner comment above the helper block documenting the return 0/-1 and self-reporting convention for all six functions Change-Id: I786dd7421d857541a0c9adfee7c7c83797370e05 Reviewed-on: https://review.couchbase.org/c/couchbase-python-client/+/250034 Tested-by: Build Bot <build@couchbase.com> Reviewed-by: Dimitris Christodoulou <dimitris.christodoulou@couchbase.com>
1 parent f373e8f commit 0dea0ed

1 file changed

Lines changed: 35 additions & 11 deletions

File tree

src/utils.hxx

Lines changed: 35 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -228,11 +228,10 @@ extract_legacy_span_field(PyObject* dict,
228228
}
229229
}
230230

231-
// Add a field to result dict (auto-converts C++ value to Python).
232-
// Returns 0 on success, -1 on failure. Most callers (generated code) don't
233-
// check the return value, so on failure we report the pending exception via
234-
// PyErr_WriteUnraisable (matches the pattern used in logger.hxx) rather than
235-
// leaving it set on the thread or silently dropping it.
231+
// Shared convention for every add_* helper below (add_field, add_bool_field,
232+
// add_string_field_if_not_empty, add_duration_field, add_cpp_core_span_field): return 0 on
233+
// success, -1 on failure, self-reported via PyErr_WriteUnraisable (matches logger.hxx) since
234+
// most callers (generated code) never check the return value.
236235
template<typename T>
237236
inline int
238237
add_field(PyObject* dict, const char* key, const T& value)
@@ -244,6 +243,9 @@ add_field(PyObject* dict, const char* key, const T& value)
244243
}
245244
int rv = PyDict_SetItemString(dict, key, pyObj);
246245
Py_DECREF(pyObj);
246+
if (rv < 0) {
247+
PyErr_WriteUnraisable(dict);
248+
}
247249
return rv;
248250
}
249251

@@ -258,6 +260,9 @@ add_field(PyObject* dict, PyObject* interned_key, const T& value)
258260
}
259261
int rv = PyDict_SetItem(dict, interned_key, pyObj);
260262
Py_DECREF(pyObj);
263+
if (rv < 0) {
264+
PyErr_WriteUnraisable(dict);
265+
}
261266
return rv;
262267
}
263268

@@ -271,13 +276,20 @@ add_field(PyObject* dict, const char* key, PyObject* value)
271276
}
272277
int rv = PyDict_SetItemString(dict, key, value);
273278
Py_DECREF(value);
279+
if (rv < 0) {
280+
PyErr_WriteUnraisable(dict);
281+
}
274282
return rv;
275283
}
276284

277-
inline void
285+
inline int
278286
add_bool_field(PyObject* dict, const char* key, bool value)
279287
{
280-
PyDict_SetItemString(dict, key, value ? Py_True : Py_False);
288+
int rv = PyDict_SetItemString(dict, key, value ? Py_True : Py_False);
289+
if (rv < 0) {
290+
PyErr_WriteUnraisable(dict);
291+
}
292+
return rv;
281293
}
282294

283295
inline int
@@ -293,6 +305,9 @@ add_string_field_if_not_empty(PyObject* dict, const char* key, const std::string
293305
}
294306
int rv = PyDict_SetItemString(dict, key, pyObj);
295307
Py_DECREF(pyObj);
308+
if (rv < 0) {
309+
PyErr_WriteUnraisable(dict);
310+
}
296311
return rv;
297312
}
298313

@@ -307,20 +322,29 @@ add_duration_field(PyObject* dict, const char* key, const std::chrono::milliseco
307322
}
308323
int rv = PyDict_SetItemString(dict, key, pyObj);
309324
Py_DECREF(pyObj);
325+
if (rv < 0) {
326+
PyErr_WriteUnraisable(dict);
327+
}
310328
return rv;
311329
}
312330

313-
inline void
331+
inline int
314332
add_cpp_core_span_field(
315333
PyObject* dict,
316334
const char* key,
317335
const std::shared_ptr<couchbase::core::tracing::wrapper_sdk_span>& wrapperSpan)
318336
{
319337
PyObject* pyObj = cbpp_wrapper_span_to_py(wrapperSpan);
320-
if (pyObj != nullptr) {
321-
PyDict_SetItemString(dict, key, pyObj);
322-
Py_DECREF(pyObj);
338+
if (pyObj == nullptr) {
339+
PyErr_WriteUnraisable(dict);
340+
return -1;
341+
}
342+
int rv = PyDict_SetItemString(dict, key, pyObj);
343+
Py_DECREF(pyObj);
344+
if (rv < 0) {
345+
PyErr_WriteUnraisable(dict);
323346
}
347+
return rv;
324348
}
325349

326350
// ======================================================================

0 commit comments

Comments
 (0)