Skip to content

Commit 0148935

Browse files
committed
aaa_diameter: Fix race condition with async dm_send_request()
- Avoid reading the @dmsg after it has been put on the queue, as it might get freed meanwhile. * aaa_diameter: Fix race condition on pending async replies It was possible for the dm_send_request_async_tout() async timeout function to ran concurrently with a late Diameter server reply, leading to a use-after-free bug on the @cond struct. * Add refcounting to the "cond" object The SHM-stored @cond object is effectively referenced by two separate processes/threads, which run concurrently: - dm_send_request_async_tout(), the reactor async timeout callback - dm_receive_msg(), the libfdcore receiver thread(s)
1 parent 6c7d6bf commit 0148935

3 files changed

Lines changed: 96 additions & 14 deletions

File tree

modules/aaa_diameter/aaa_diameter.c

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -472,21 +472,22 @@ struct dm_async_msg {
472472
struct dm_cond *cond;
473473
};
474474

475-
static struct dm_async_msg *dm_get_async_msg(pv_spec_t *rpl_avps_pv, aaa_message *dmsg)
475+
static struct dm_async_msg *dm_get_async_msg(pv_spec_t *rpl_avps_pv, struct dm_cond *cond)
476476
{
477477
struct dm_async_msg *msg = pkg_malloc(sizeof *msg);
478478
if (!msg)
479479
return NULL;
480480
memset(msg, 0, sizeof *msg);
481481
msg->ret = rpl_avps_pv;
482-
msg->cond = ((struct dm_message *)(dmsg->avpair))->reply_cond;
482+
msg->cond = cond;
483+
dm_cond_ref(cond);
483484
return msg;
484485
}
485486

486487
static void dm_free_sync_msg(struct dm_async_msg *amsg)
487488
{
488489
if (amsg->cond)
489-
shm_free(amsg->cond);
490+
dm_cond_unref(amsg->cond);
490491
pkg_free(amsg);
491492
}
492493

@@ -529,12 +530,19 @@ static int dm_send_request_async_reply(int fd,
529530
static int dm_send_request_async_tout(int fd,
530531
struct sip_msg *msg, void *param)
531532
{
533+
int removed;
532534
struct dm_async_msg *amsg = (struct dm_async_msg *)param;
533535
pv_value_t val = {STR_NULL, 0, PV_VAL_NULL};
534536

537+
async_status = ASYNC_DONE_CLOSE_FD;
538+
535539
if (pv_set_value(msg, amsg->ret, 0, &val) != 0)
536540
LM_ERR("failed to set output rpl_avps pv to NULL\n");
537541

542+
removed = dm_drop_pending_reply_cond(amsg->cond);
543+
if (removed > 0)
544+
dm_cond_unref(amsg->cond);
545+
538546
dm_free_sync_msg(amsg);
539547
return -2;
540548
}
@@ -546,6 +554,7 @@ static int dm_send_request_async(struct sip_msg *msg, async_ctx *ctx,
546554
struct dict_object *req;
547555
cJSON *avps;
548556
struct dm_async_msg *amsg;
557+
struct dm_cond *cond;
549558

550559
if (fd_dict_search(fd_g_config->cnf_dict, DICT_COMMAND, CMD_BY_CODE_R,
551560
cmd_code, &req, ENOENT) == ENOENT) {
@@ -585,12 +594,12 @@ static int dm_send_request_async(struct sip_msg *msg, async_ctx *ctx,
585594
_dm_destroy_message(dmsg);
586595
goto error;
587596
}
588-
if (_dm_send_message_async(NULL, dmsg, &async_status) < 0) {
597+
if (_dm_send_message_async(NULL, dmsg, &async_status, &cond) < 0) {
589598
LM_ERR("cannot send async message!\n");
590599
goto error;
591600
}
592601

593-
amsg = dm_get_async_msg(rpl_avps_pv, dmsg);
602+
amsg = dm_get_async_msg(rpl_avps_pv, cond);
594603
if (!amsg)
595604
goto error;
596605
cJSON_Delete(avps);

modules/aaa_diameter/dm_impl.c

Lines changed: 61 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,7 @@ static struct dm_cond *dm_get_cond(int type, diameter_reply_cb *cb, void *param)
113113
return NULL;
114114
}
115115
memset(cond, 0, sizeof *cond);
116+
cond->ref = 1;
116117
cond->type = type;
117118
switch (type) {
118119
case DM_TYPE_EVENT:
@@ -274,16 +275,19 @@ static void dm_cond_event_resume(int sender, void *param)
274275
} while (ret < 0 && (errno == EINTR || errno == EAGAIN));
275276
if (ret < 0)
276277
LM_ERR("could not notify resume: %s\n", strerror(errno));
278+
279+
dm_cond_unref(cond);
277280
}
278281

279282
static void dm_cond_signal(struct dm_cond *cond)
280283
{
281284
LM_INFO("singalling %p/%d\n", cond, cond->type);
282285
switch (cond->type) {
283286
case DM_TYPE_EVENT:
287+
dm_cond_ref(cond);
284288
if (ipc_send_rpc(cond->sync.event.pid, dm_cond_event_resume, cond) < 0) {
285289
LM_ERR("could not resume async MI command!\n");
286-
shm_free(cond);
290+
dm_cond_unref(cond);
287291
}
288292
break;
289293
case DM_TYPE_COND:
@@ -295,7 +299,6 @@ static void dm_cond_signal(struct dm_cond *cond)
295299
case DM_TYPE_CB:
296300
if (cond->sync.cb.f)
297301
cond->sync.cb.f(NULL, &cond->rpl, cond->sync.cb.p);
298-
shm_free(cond);
299302
break;
300303
}
301304
}
@@ -356,6 +359,7 @@ static int dm_auth_reply(struct msg **_msg, struct avp * avp, struct session * s
356359
rpl_cond->rpl.is_error = 0;
357360
}
358361
dm_cond_signal(rpl_cond);
362+
dm_cond_unref(rpl_cond);
359363

360364
out:
361365
FD_CHECK(fd_msg_free(msg));
@@ -715,6 +719,7 @@ static int dm_receive_msg(struct msg **_msg, struct avp * avp, struct session *
715719
rpl_cond->rpl.is_error = 0;
716720
}
717721
dm_cond_signal(rpl_cond);
722+
dm_cond_unref(rpl_cond);
718723

719724
out:
720725
cJSON_InitHooks(NULL);
@@ -830,11 +835,57 @@ int dm_add_pending_reply(const str *callid, struct dm_cond *reply_cond)
830835
}
831836

832837
*cond_holder = reply_cond;
838+
dm_cond_ref(reply_cond);
833839
hash_unlock(pending_replies, hentry);
834840

835841
return 0;
836842
}
837843

844+
struct dm_find_cond_ctx {
845+
struct dm_cond *cond;
846+
str key;
847+
int found;
848+
};
849+
850+
static int dm_match_pending_cond(void *param, str key, void *value)
851+
{
852+
struct dm_find_cond_ctx *ctx = (struct dm_find_cond_ctx *)param;
853+
854+
if (value != ctx->cond)
855+
return 0;
856+
857+
ctx->key = key;
858+
ctx->found = 1;
859+
return 1;
860+
}
861+
862+
int dm_drop_pending_reply_cond(struct dm_cond *reply_cond)
863+
{
864+
struct dm_find_cond_ctx ctx;
865+
unsigned int hentry;
866+
867+
if (!reply_cond)
868+
return 0;
869+
870+
for (hentry = 0; hentry < hash_size(pending_replies); hentry++) {
871+
memset(&ctx, 0, sizeof ctx);
872+
ctx.cond = reply_cond;
873+
874+
hash_lock(pending_replies, hentry);
875+
map_for_each(pending_replies->entries[hentry], dm_match_pending_cond, &ctx);
876+
if (!ctx.found) {
877+
hash_unlock(pending_replies, hentry);
878+
continue;
879+
}
880+
881+
hash_remove(pending_replies, hentry, ctx.key);
882+
hash_unlock(pending_replies, hentry);
883+
return 1;
884+
}
885+
886+
return 0;
887+
}
888+
838889

839890

840891
/* all of these AVPs are part of "RADIUS Extension for Digest Auth" RFC 5090 */
@@ -1938,21 +1989,22 @@ static void dm_push_queue(aaa_message *msg, struct dm_cond *cond)
19381989
pthread_mutex_unlock(msg_send_lk);
19391990
}
19401991

1941-
int _dm_send_message_async(aaa_conn *_, aaa_message *req, int *fd)
1992+
int _dm_send_message_async(aaa_conn *_, aaa_message *req, int *fd, struct dm_cond **cond)
19421993
{
1943-
struct dm_cond *cond;
1994+
struct dm_cond *_cond;
19441995

19451996
if (!req)
19461997
return -1;
19471998

1948-
cond = dm_get_cond(DM_TYPE_EVENT, NULL, NULL);
1949-
if (!cond) {
1999+
_cond = dm_get_cond(DM_TYPE_EVENT, NULL, NULL);
2000+
if (!_cond) {
19502001
LM_ERR("out of memory for cond\n");
19512002
return -1;
19522003
}
19532004

1954-
*fd = cond->sync.event.fd;
1955-
dm_push_queue(req, cond);
2005+
*fd = _cond->sync.event.fd;
2006+
*cond = _cond;
2007+
dm_push_queue(req, _cond);
19562008

19572009
LM_DBG("message queued for async sending\n");
19582010

@@ -2191,6 +2243,7 @@ void _dm_destroy_message(aaa_message *msg)
21912243

21922244
dm = (struct dm_message *)msg->avpair;
21932245
dm_free_avps(&dm->avps);
2246+
dm_cond_unref(dm->reply_cond);
21942247
shm_free(dm);
21952248

21962249
shm_free(msg);

modules/aaa_diameter/dm_impl.h

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
#define AAA_DIAMETER_IMPL
2323

2424
#include "../../aaa/aaa.h"
25+
#include "../../mem/shm_mem.h"
2526
#include "diameter_api.h"
2627

2728
#define __FD_CHECK(__call__, __retok__, __retval__) \
@@ -122,6 +123,7 @@ struct dm_avp {
122123
#define DM_TYPE_CB (1<<2)
123124

124125
struct dm_cond {
126+
volatile int ref;
125127
int type;
126128
union {
127129
struct {
@@ -140,6 +142,23 @@ struct dm_cond {
140142

141143
diameter_reply rpl;
142144
};
145+
146+
static inline void dm_cond_ref(struct dm_cond *cond)
147+
{
148+
if (!cond || cond->type == DM_TYPE_COND)
149+
return;
150+
151+
__atomic_add_fetch(&cond->ref, 1, __ATOMIC_SEQ_CST);
152+
}
153+
154+
static inline void dm_cond_unref(struct dm_cond *cond)
155+
{
156+
if (!cond || cond->type == DM_TYPE_COND)
157+
return;
158+
159+
if (__atomic_sub_fetch(&cond->ref, 1, __ATOMIC_SEQ_CST) == 0)
160+
shm_free(cond);
161+
}
143162
int init_mutex_cond(pthread_mutex_t *mutex, pthread_cond_t *cond);
144163

145164
extern struct list_head dm_unreplied_req;
@@ -169,7 +188,8 @@ int dm_avp_add(aaa_conn *_, aaa_message *msg, aaa_map *avp, void *val,
169188
int dm_build_avps(struct list_head *subavps, cJSON *array);
170189
int dm_send_message(aaa_conn *_, aaa_message *req, aaa_message **__);
171190
int _dm_send_message(aaa_conn *_, aaa_message *req, struct dm_cond **reply_cond);
172-
int _dm_send_message_async(aaa_conn *_, aaa_message *req, int *fd);
191+
int _dm_send_message_async(aaa_conn *_, aaa_message *req, int *fd, struct dm_cond **cond);
192+
int dm_drop_pending_reply_cond(struct dm_cond *reply_cond);
173193
int _dm_get_message_response(struct dm_cond *cond, char **rpl_avps);
174194
void _dm_release_message_response(struct dm_cond *cond, char *rpl_avps);
175195
int dm_destroy_message(aaa_conn *con, aaa_message *msg);

0 commit comments

Comments
 (0)