Skip to content

Commit df382ca

Browse files
kanard38knard38
authored andcommitted
DAOS-19018 kv: fix reference leak in daos_kv2objhandle()
kv_hdl2ptr() increments the dc_kv reference count via daos_hhash_link_lookup(). daos_kv2objhandle() was returning without calling kv_decref(), leaking one reference per call. Add the missing kv_decref() after copying the object handle. Also add a regression test in the DAOS_KV_API suite that calls daos_kv2objhandle() with both invalid and valid handles, exercises the function repeatedly to surface any leak under ASAN/LeakSanitizer, and verifies the KV handle can be cleanly closed afterwards. Signed-off-by: Cedric Koch-Hofer <cedric.koch-hofer@hpe.com>
1 parent 8399dbf commit df382ca

2 files changed

Lines changed: 65 additions & 12 deletions

File tree

src/client/kv/dc_kv.c

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
/**
22
* (C) Copyright 2017-2024 Intel Corporation.
3+
* (C) Copyright 2026 Hewlett Packard Enterprise Development LP
34
*
45
* SPDX-License-Identifier: BSD-2-Clause-Patent
56
*/
@@ -94,12 +95,19 @@ kv_hdl2ptr(daos_handle_t oh)
9495

9596
daos_handle_t daos_kv2objhandle(daos_handle_t kv_oh)
9697
{
97-
struct dc_kv *dk = kv_hdl2ptr(kv_oh);
98+
struct dc_kv *dk;
99+
daos_handle_t oh;
98100

99-
if (dk)
100-
return dk->daos_oh;
101+
dk = kv_hdl2ptr(kv_oh);
102+
if (dk == NULL) {
103+
oh = DAOS_HDL_INVAL;
104+
goto out;
105+
}
106+
oh = dk->daos_oh;
107+
kv_decref(dk);
101108

102-
return DAOS_HDL_INVAL;
109+
out:
110+
return oh;
103111
}
104112

105113
static void

src/tests/suite/daos_kv.c

Lines changed: 53 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
/**
22
* (C) Copyright 2016-2022 Intel Corporation.
3+
* (C) Copyright 2026 Hewlett Packard Enterprise Development LP
34
*
45
* SPDX-License-Identifier: BSD-2-Clause-Patent
56
*/
@@ -10,6 +11,7 @@
1011
*/
1112
#include <daos.h>
1213
#include <daos_kv.h>
14+
#include <daos/kv.h>
1315
#include "daos_test.h"
1416

1517
#if D_HAS_WARNING(4, "-Wframe-larger-than")
@@ -352,15 +354,58 @@ kv_cond_ops(void **state)
352354
print_message("all good\n");
353355
} /* End simple_put_get */
354356

357+
static void
358+
kv_obj_handle(void **state)
359+
{
360+
test_arg_t *arg = *state;
361+
daos_obj_id_t oid;
362+
daos_handle_t kv_oh;
363+
daos_handle_t oh;
364+
int rc;
365+
int i;
366+
367+
/** invalid handle must return DAOS_HDL_INVAL */
368+
print_message("Getting obj handle from invalid KV handle\n");
369+
oh = daos_kv2objhandle(DAOS_HDL_INVAL);
370+
assert_true(daos_handle_is_inval(oh));
371+
372+
oid = daos_test_oid_gen(arg->coh, OC_SX, type, 0, arg->myrank);
373+
rc = daos_kv_open(arg->coh, oid, DAOS_OO_RW, &kv_oh, NULL);
374+
assert_rc_equal(rc, 0);
375+
376+
/** valid handle must return a valid object handle */
377+
print_message("Getting obj handle from valid KV handle\n");
378+
oh = daos_kv2objhandle(kv_oh);
379+
assert_true(daos_handle_is_valid(oh));
380+
381+
/**
382+
* Call multiple times: each call must release the lookup reference so
383+
* no reference leak accumulates (regression for DAOS-19018).
384+
* When built with ASAN/LeakSanitizer, any leaked dc_kv reference will
385+
* be reported here.
386+
*/
387+
print_message("Calling daos_kv2objhandle() repeatedly\n");
388+
for (i = 0; i < 10; i++) {
389+
oh = daos_kv2objhandle(kv_oh);
390+
assert_true(daos_handle_is_valid(oh));
391+
}
392+
393+
/** KV must be closeable after repeated calls */
394+
print_message("Closing KV handle\n");
395+
rc = daos_kv_close(kv_oh, NULL);
396+
assert_rc_equal(rc, 0);
397+
398+
print_message("all good\n");
399+
}
400+
355401
static const struct CMUnitTest kv_tests[] = {
356-
{"KV: Object Put/GET (blocking)",
357-
simple_put_get, async_disable, NULL},
358-
{"KV: Object Put/GET with daos_ofeat_t flag(blocking)",
359-
simple_put_get_old, async_disable, NULL},
360-
{"KV: Object Put/GET (non-blocking)",
361-
simple_put_get, async_enable, NULL},
362-
{"KV: Object Conditional Ops (blocking)",
363-
kv_cond_ops, async_disable, NULL},
402+
{"KV: Object Put/GET (blocking)", simple_put_get, async_disable, NULL},
403+
{"KV: Object Put/GET with daos_ofeat_t flag(blocking)", simple_put_get_old, async_disable,
404+
NULL},
405+
{"KV: Object Put/GET (non-blocking)", simple_put_get, async_enable, NULL},
406+
{"KV: Object Conditional Ops (blocking)", kv_cond_ops, async_disable, NULL},
407+
{"KV: daos_kv2objhandle() correctness and reference leak (DAOS-19018)", kv_obj_handle,
408+
async_disable, NULL},
364409
};
365410

366411
int

0 commit comments

Comments
 (0)