pyldb: add PyErr_LDB_MESSAGE_OR_RAISE() macro
authorDouglas Bagnall <douglas.bagnall@catalyst.net.nz>
Thu, 14 Mar 2024 03:41:43 +0000 (16:41 +1300)
committerAndrew Bartlett <abartlet@samba.org>
Wed, 10 Apr 2024 05:13:32 +0000 (05:13 +0000)
The Python level message has a reference to an LDB, which should be NULL,
or the same as the dn's LDB, lest one of them is freed early.

The message LDB will be NULL until a DN is set, and if the DN is replaced,
the LDB is also be replaced (see py_ldb_msg_set_dn), so it is *unlikely*
for these to get out of sync. In addition, fetching msg.dn via python
compares the LDBs at that point (py_ldb_msg_get_dn).

Signed-off-by: Douglas Bagnall <douglas.bagnall@catalyst.net.nz>
Reviewed-by: Andrew Bartlett <abartlet@samba.org>
lib/ldb/pyldb.c

index b618beea2f24cf98fd3024ab7f64735fdf682a48..d191bc18935b0d1bcdcfeaf6962805c51bfea71d 100644 (file)
@@ -3661,6 +3661,41 @@ static PyObject *py_ldb_msg_from_dict(PyTypeObject *type, PyObject *args)
        return py_ret;
 }
 
+
+#define pyldb_Message_as_message(pyobj) ((PyLdbMessageObject *)pyobj)->msg
+
+#define pyldb_Message_get_pyldb(pyobj) ((PyLdbMessageObject *)pyobj)->pyldb
+
+/*
+ * PyErr_LDB_MESSAGE_OR_RAISE does 3 things:
+ * 1. checks that a PyObject is really a PyLdbMessageObject.
+ * 2. checks that the ldb that the PyLdbMessageObject knows is the ldb that
+ *    its dn knows -- but only if the underlying message has a DN.
+ * 3. sets message to the relevant struct ldb_message *.
+ *
+ * We need to do all this to ensure the message belongs to the right
+ * ldb, lest it be freed before we are ready.
+ */
+#define PyErr_LDB_MESSAGE_OR_RAISE(_py_obj, message) do {              \
+       PyLdbMessageObject *_py_message = NULL;                 \
+       struct ldb_dn *_dn = NULL;                                      \
+       if (_py_obj == NULL || !PyLdbMessage_Check(_py_obj)) {          \
+               PyErr_SetString(PyExc_TypeError,                        \
+                               "ldb Message object required"); \
+               return NULL;                                            \
+       }                                                               \
+       _py_message = (PyLdbMessageObject *)_py_obj;                    \
+       message = pyldb_Message_as_message(_py_message);                \
+       _dn = message->dn;                                              \
+       if (_dn != NULL &&                                              \
+           (_py_message->pyldb->ldb_ctx != ldb_dn_get_ldb_context(_dn))) { \
+               PyErr_SetString(PyExc_RuntimeError,                     \
+                               "Message has a stale LDB connection");  \
+               return NULL;                                            \
+       }                                                               \
+} while(0)
+
+
 static PyObject *py_ldb_msg_remove_attr(PyLdbMessageObject *self, PyObject *args)
 {
        char *name;