Skip to content

Commit 5b04429

Browse files
committed
Clarify and fix attribute data lifetime
Modify kdump_attr_ref_get() to return data that is guaranteed to stay valid. In particular: - string attributes are duplicated - blob and bitmap attributes increment their reference count Of course, the caller should free these resources again when the data is no longer needed. Add a kdump_attr_discard() function for that purpose. Rationale When used on a string, blob or bitmap attribute, kdump_get_attr(), kdump_get_typed_attr() and derivatives will return a pointer to the underlying data without any lifetime guarantees. Such pointers may become dangling whenever the caller touches the containing dump file object. In the extreme case, another thread may invalidate the pointer before it is even returned to the caller. However, most users are happy with these limitations, so keep the simple API unchanged, only updating documentation. There is also an attribute API based on references (kdump_attr_ref_t). This API was primarily intended to allow iterating over attributes in a thread-safe manner (although this is not yet fully implemented, cf. ptesarik#71), but it can be also used to get attribute values, see kdump_attr_ref_get(). Since this API is designed as thread-safe, it makes little sense to have a thread-unsafe function to get attribute value. It sounds fair that users of this API are now required to adapt their code to avoid memory leaks. Fixes: ptesarik#82 Signed-off-by: Petr Tesarik <[email protected]>
1 parent b30f767 commit 5b04429

File tree

10 files changed

+123
-19
lines changed

10 files changed

+123
-19
lines changed

NEWS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ next
22
----
33
* Incompatible API changes:
44
- kdump_get_typed_attr(): parameters and type mismatch behaviour
5+
- kdump_attr_ref_get(): result must be discarded
56
* Support flattened ELF dump files.
67
* Support partially rearranged makedumpfile split files.
78
* Parse QEMU CPU state ELF notes.

examples/dumpattr.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,7 @@ show_attr(kdump_ctx_t *ctx, kdump_attr_ref_t *ref, int indent, const char *key)
134134
printf("<unknown>\n");
135135
}
136136

137+
kdump_attr_discard(ctx, &attr);
137138
return 0;
138139
}
139140

include/libkdumpfile/kdumpfile.h.in

Lines changed: 47 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -732,10 +732,18 @@ kdump_clear_attr(kdump_ctx_t *ctx, const char *key)
732732
* @param valp Value (filled on successful return).
733733
* @returns Error status.
734734
*
735-
* Note that the caller does not hold a reference to the attribute, so
736-
* it is not generally safe to use this function in a multi-threaded
737-
* program, or across another library call which modifies the attribute
738-
* (explicitly or implicitly).
735+
* This function is useful only when the caller can fully control the
736+
* lifetime of the returned attribute value. In that case, the value
737+
* is valid only as long as no changes are made to the containing dump
738+
* file object.
739+
*
740+
* Note that attribute data (or the attribute hierarchy itself) may
741+
* change as a side effect of another operation. Merely reading dump
742+
* data or attributes will never invalidate the attribute value, i.e.
743+
* it is safe to dereference pointers. However, the returned value may
744+
* become stale, for example cache statistics.
745+
*
746+
* A more robust API is provided by @ref kdump_attr_ref_get.
739747
*/
740748
kdump_status kdump_get_attr(kdump_ctx_t *ctx, const char *key,
741749
kdump_attr_t *valp);
@@ -750,8 +758,8 @@ kdump_status kdump_get_attr(kdump_ctx_t *ctx, const char *key,
750758
* If the attribute is of a different type, this function returns
751759
* KDUMP_ERR_INVALID and does not change @p valp.
752760
*
753-
* Note that the caller does not hold a reference to the attribute. See
754-
* the description of @ref kdump_get_attr for limitations.
761+
* The caller must ensure the lifetime of the returned data is valid. See
762+
* the description of @ref kdump_get_attr for more details.
755763
*/
756764
kdump_status kdump_get_typed_attr(kdump_ctx_t *ctx, const char *key,
757765
kdump_attr_type_t type,
@@ -792,8 +800,8 @@ kdump_get_address_attr(kdump_ctx_t *ctx, const char *key, kdump_addr_t *addr)
792800
* @param str[out] Filled with the attribute value on successful return.
793801
* @returns Error status.
794802
*
795-
* Note that the caller does not hold a reference to the string. See
796-
* the description of @ref kdump_get_attr for limitations.
803+
* The caller must ensure the lifetime of the returned data is valid. See
804+
* the description of @ref kdump_get_attr for more details.
797805
*/
798806
static inline kdump_status
799807
kdump_get_string_attr(kdump_ctx_t *ctx, const char *key, const char **str)
@@ -846,14 +854,42 @@ int kdump_attr_ref_isset(kdump_attr_ref_t *ref);
846854
/** Get attribute data by reference.
847855
* @param ctx Dump file object.
848856
* @param[in] ref Attribute reference.
849-
* @param[out] valp Attribute value (filled on successful return).
857+
* @param[out] valp Attribute value (filled on return).
858+
* @returns Error status.
850859
*
851-
* This works just like @ref kdump_get_attr, except that the attribute
852-
* is denoted by a reference rather than by its key path.
860+
* This works similarly to @ref kdump_get_attr, except:
861+
* - the attribute is denoted by a reference rather than by path,
862+
* - the returned data stays valid even if changes are made to the dump
863+
* object.
864+
*
865+
* The returned value may be a dynamically allocated copy of the attribute
866+
* value and/or it may hold an extra reference to the underlying objects.
867+
* You should free up these resources with @ref kdump_attr_discard when
868+
* the data is no longer needed.
869+
*
870+
* If the function returns an error, it is not necessary to discard the
871+
* result. However, the type of @p valp is set to @c KDUMP_NIL, so it is
872+
* always safe to call @ref kdump_attr_discard on the result. The goal of
873+
* this feature is to simplify code flow in callers.
853874
*/
854875
kdump_status kdump_attr_ref_get(kdump_ctx_t *ctx, const kdump_attr_ref_t *ref,
855876
kdump_attr_t *valp);
856877

878+
/** Discard attribute data value.
879+
* @param ctx Dump file object.
880+
* @param attr Attribute data.
881+
*
882+
* Perform any actions necessary to release resources by the attribute data.
883+
* This function may decrement object references, free dynamically allocated
884+
* memory, or even do nothing, depending on the data type.
885+
*
886+
* Call this function when the data returned by @ref kdump_attr_ref_get is no
887+
* longer needed. Do *NOT* call this function on attribute data returned by
888+
* @ref kdump_get_attr.
889+
*/
890+
void
891+
kdump_attr_discard(kdump_ctx_t *ctx, kdump_attr_t *attr);
892+
857893
/** Set attribute data by reference.
858894
* @param ctx Dump file object.
859895
* @param[in] ref Attribute reference.

python/kdumpfile.c

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -199,26 +199,37 @@ static PyObject *kdumpfile_read (PyObject *_self, PyObject *args, PyObject *kw)
199199
static PyObject *
200200
attr_new(kdumpfile_object *kdumpfile, kdump_attr_ref_t *ref, kdump_attr_t *attr)
201201
{
202+
PyObject *obj = NULL;
203+
202204
if (attr->type != KDUMP_DIRECTORY)
203205
kdump_attr_unref(kdumpfile->ctx, ref);
204206

205207
switch (attr->type) {
206208
case KDUMP_NUMBER:
207-
return PyLong_FromUnsignedLong(attr->val.number);
209+
obj = PyLong_FromUnsignedLong(attr->val.number);
210+
break;
208211
case KDUMP_ADDRESS:
209-
return PyLong_FromUnsignedLong(attr->val.address);
212+
obj = PyLong_FromUnsignedLong(attr->val.address);
213+
break;
210214
case KDUMP_STRING:
211-
return PyString_FromString(attr->val.string);
215+
obj = PyString_FromString(attr->val.string);
216+
break;
212217
case KDUMP_DIRECTORY:
213-
return attr_dir_new(kdumpfile, ref);
218+
obj= attr_dir_new(kdumpfile, ref);
219+
break;
214220
case KDUMP_BITMAP:
215-
return bmp_new(attr->val.bitmap);
221+
obj = bmp_new(attr->val.bitmap);
222+
break;
216223
case KDUMP_BLOB:
217-
return blob_new(attr->val.blob);
224+
obj = blob_new(attr->val.blob);
225+
break;
218226
default:
219227
PyErr_SetString(PyExc_RuntimeError, "Unhandled attr type");
220-
return NULL;
221228
}
229+
230+
kdump_attr_discard(kdumpfile->ctx, attr);
231+
232+
return obj;
222233
}
223234

224235
PyDoc_STRVAR(get_addrxlat_ctx__doc__,
@@ -1604,14 +1615,15 @@ attr_iteritem_next(PyObject *_self)
16041615

16051616
result = PyTuple_New(2);
16061617
if (result == NULL)
1607-
return NULL;
1618+
goto err_attr;
16081619
key = PyString_FromString(self->iter.key);
16091620
if (!key)
16101621
goto err_result;
16111622
value = attr_new(self->kdumpfile, &self->iter.pos, &attr);
16121623
if (!value)
16131624
goto err_key;
16141625

1626+
kdump_attr_discard(self->kdumpfile->ctx, &attr);
16151627
PyTuple_SET_ITEM(result, 0, key);
16161628
PyTuple_SET_ITEM(result, 1, value);
16171629
return attr_iter_advance(self, result);
@@ -1620,6 +1632,8 @@ attr_iteritem_next(PyObject *_self)
16201632
Py_DECREF(key);
16211633
err_result:
16221634
Py_DECREF(result);
1635+
err_attr:
1636+
kdump_attr_discard(self->kdumpfile->ctx, &attr);
16231637
return NULL;
16241638
}
16251639

src/kdumpfile/attr.c

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1321,6 +1321,36 @@ kdump_attr_ref_isset(kdump_attr_ref_t *ref)
13211321
return attr_isset(ref_attr(ref));
13221322
}
13231323

1324+
static kdump_status
1325+
hold_attr_data(kdump_ctx_t *ctx, kdump_attr_t *valp)
1326+
{
1327+
switch (valp->type) {
1328+
case KDUMP_NIL:
1329+
case KDUMP_DIRECTORY:
1330+
case KDUMP_NUMBER:
1331+
case KDUMP_ADDRESS:
1332+
/* Value is embedded: Nothing to be done. */
1333+
break;
1334+
1335+
case KDUMP_STRING:
1336+
valp->val.string = strdup(valp->val.string);
1337+
if (!valp->val.string)
1338+
return set_error(ctx, KDUMP_ERR_SYSTEM,
1339+
"Cannot allocate string");
1340+
break;
1341+
1342+
case KDUMP_BITMAP:
1343+
internal_bmp_incref(valp->val.bitmap);
1344+
break;
1345+
1346+
case KDUMP_BLOB:
1347+
internal_blob_incref(valp->val.blob);
1348+
break;
1349+
}
1350+
1351+
return KDUMP_OK;
1352+
}
1353+
13241354
kdump_status
13251355
kdump_attr_ref_get(kdump_ctx_t *ctx, const kdump_attr_ref_t *ref,
13261356
kdump_attr_t *valp)
@@ -1332,10 +1362,21 @@ kdump_attr_ref_get(kdump_ctx_t *ctx, const kdump_attr_ref_t *ref,
13321362
rwlock_rdlock(&ctx->shared->lock);
13331363
valp->type = d->template->type;
13341364
ret = get_attr_data(ctx, d, &valp->val);
1365+
if (ret == KDUMP_OK)
1366+
ret = hold_attr_data(ctx, valp);
1367+
if (ret != KDUMP_OK)
1368+
valp->type = KDUMP_NIL;
13351369
rwlock_unlock(&ctx->shared->lock);
13361370
return ret;
13371371
}
13381372

1373+
void
1374+
kdump_attr_discard(kdump_ctx_t *ctx, kdump_attr_t *attr)
1375+
{
1376+
clear_error(ctx);
1377+
discard_value(&attr->val, attr->type, ATTR_DYNSTR);
1378+
}
1379+
13391380
kdump_status
13401381
kdump_attr_ref_set(kdump_ctx_t *ctx, kdump_attr_ref_t *ref,
13411382
const kdump_attr_t *valp)

src/kdumpfile/kdumpfile-priv.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -482,6 +482,9 @@ struct attr_flags {
482482
#define ATTR_PERSIST_INDIRECT \
483483
((struct attr_flags){ .persist = true, .indirect = true })
484484

485+
/** Dynamically allocated attribute flags. */
486+
#define ATTR_DYNSTR \
487+
((struct attr_flags){ .dynstr = true })
485488

486489
/** Attribute template flags.
487490
*/

src/kdumpfile/libkdumpfile.map

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ LIBKDUMPFILE_0 {
4242
kdump_attr_ref_type;
4343
kdump_attr_ref_isset;
4444
kdump_attr_ref_get;
45+
kdump_attr_discard;
4546
kdump_attr_ref_set;
4647
kdump_set_sub_attr;
4748
kdump_attr_iter_start;

tests/attriter.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,7 @@ main(int argc, char **argv)
137137
rc = TEST_FAIL;
138138
} else
139139
printf("%s = %s\n", it.key, attr.val.string);
140+
kdump_attr_discard(ctx, &attr);
140141
}
141142

142143
res = kdump_attr_iter_next(ctx, &it);

tests/fdset.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ check_unset_file(kdump_ctx_t *ctx, kdump_attr_ref_t *fileset, const char *key,
9797
key, kdump_get_err(ctx));
9898
ret = TEST_FAIL;
9999
}
100+
kdump_attr_discard(ctx, &attr);
100101
kdump_attr_unref(ctx, &tmpref);
101102
}
102103

@@ -156,6 +157,7 @@ check_fileset_zero(kdump_ctx_t *ctx, kdump_attr_ref_t *fileset, int fd)
156157
"file.set.0.fd", attr.val.number, fd);
157158
ret = TEST_FAIL;
158159
}
160+
kdump_attr_discard(ctx, &attr);
159161
kdump_attr_unref(ctx, &tmpref);
160162

161163
return ret;
@@ -192,6 +194,7 @@ check_filename(kdump_ctx_t *ctx, kdump_attr_ref_t *fileset,
192194
KDUMP_ATTR_FILE_SET, subkey, attr.val.string, name);
193195
ret = TEST_FAIL;
194196
}
197+
kdump_attr_discard(ctx, &attr);
195198
kdump_attr_unref(ctx, &tmpref);
196199

197200
return ret;
@@ -253,6 +256,7 @@ main(int argc, char **argv)
253256
attr.val.number);
254257
ret = TEST_FAIL;
255258
}
259+
kdump_attr_discard(ctx, &attr);
256260

257261
/* Check that file.set.0.fd does not exist. */
258262
rc = check_not_exist(ctx, &fileset, "0.fd");

tests/subattr.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ main(int argc, char **argv)
8383
rc = TEST_FAIL;
8484
} else
8585
printf("%s is a directory\n", ATTRDIR);
86+
kdump_attr_discard(ctx, &attr);
8687
kdump_attr_unref(ctx, &subref);
8788
} else {
8889
fprintf(stderr, "kdump_sub_attr_ref failed for %s: %s\n",
@@ -107,6 +108,7 @@ main(int argc, char **argv)
107108
rc = TEST_FAIL;
108109
} else
109110
printf("%s = %s\n", ATTRPATH, attr.val.string);
111+
kdump_attr_discard(ctx, &attr);
110112
kdump_attr_unref(ctx, &subref);
111113
} else {
112114
fprintf(stderr, "kdump_sub_attr_ref failed for %s: %s\n",

0 commit comments

Comments
 (0)