aboutsummaryrefslogtreecommitdiffstats
path: root/main/pbx.c
diff options
context:
space:
mode:
authormmichelson <mmichelson@f38db490-d61c-443f-a65b-d21fe96a405b>2010-03-24 21:10:38 +0000
committermmichelson <mmichelson@f38db490-d61c-443f-a65b-d21fe96a405b>2010-03-24 21:10:38 +0000
commita0bdb8508fc072893accea6c39fd8f115f79455f (patch)
tree87d304b57066536728830df70879401f4e6f18cc /main/pbx.c
parentf1d6a257e9ba14d1fa777aff2fa57f64ebd4e530 (diff)
Fix potential invalid reads that could occur in pbx.c
Here is a cut and paste of my review request for this change: This past weekend, Russell ran our current suite of unit tests for Asterisk under valgrind. The PBX pattern match test caused valgrind to spew forth two invalid read errors. This patch contains two changes that shut valgrind up and do not cause any new memory leaks. Change 1: In ast_context_remove_extension_callerid2, valgrind reported an invalid read in the for loop close to the function's end. Specifically, one of the the strcmp calls in the loop control was reading invalid memory. This was because the caller of ast_context_remove_extension_callerid2 (__ast_context destroy in this case) passed as a parameter a shallow copy of an ast_exten's exten field. This same ast_exten was what was destroyed inside the for loop, thus any iterations of the for loop beyond the destruction of the ast_exten would result in invalid reads. My fix for this is to make a copy of the ast_exten's exten field and pass the copy to ast_context_remove_extension_callerid2. In addition, I have also acted similarly with the ast_exten's matchcid field. Since in this case a NULL is handled quite differently than an empty string, I needed to be a bit more careful with its handling. Change 2: In __ast_context_destroy, we iterated over a hashtab and called ast_context_remove_extension_callerid2 on each item. Specifically, the hashtab over which we were iterating was an ast_exten's peer_table. Inside of ast_context_remove_extension_callerid2, we could possibly destroy this ast_exten, which also caused the hashtab to be freed. Attempting to call ast_hashtab_end_traversal on the hashtab iterator caused an invalid read to occur when trying to read the iterator->tab->do_locking field since iterator->tab had already been freed. My handling of this problem is a bit less straightforward. With each iteration over the hashtab's contents, we set a variable called "end_traversal" based on the return of ast_context_remove_extension_callerid2. If 0 is ever returned, then we know that the extension was found and destroyed. Because of this, we cannot call ast_hashtab_end_traversal because we will be guaranteeing a read of invalid memory. In such a case, we forego calling ast_hashtab_end_traversal and instead call ast_free on the hashtab iterator. Review: https://reviewboard.asterisk.org/r/585 git-svn-id: http://svn.digium.com/svn/asterisk/trunk@254362 f38db490-d61c-443f-a65b-d21fe96a405b
Diffstat (limited to 'main/pbx.c')
-rw-r--r--main/pbx.c23
1 files changed, 21 insertions, 2 deletions
diff --git a/main/pbx.c b/main/pbx.c
index 6a36f434d..60b2e949a 100644
--- a/main/pbx.c
+++ b/main/pbx.c
@@ -8693,17 +8693,36 @@ void __ast_context_destroy(struct ast_context *list, struct ast_hashtab *context
if (tmp->root_table) { /* it is entirely possible that the context is EMPTY */
exten_iter = ast_hashtab_start_traversal(tmp->root_table);
while ((exten_item=ast_hashtab_next(exten_iter))) {
+ int end_traversal = 1;
prio_iter = ast_hashtab_start_traversal(exten_item->peer_table);
while ((prio_item=ast_hashtab_next(prio_iter))) {
+ char extension[AST_MAX_EXTENSION];
+ char cidmatch[AST_MAX_EXTENSION];
if (!prio_item->registrar || strcmp(prio_item->registrar, registrar) != 0) {
continue;
}
ast_verb(3, "Remove %s/%s/%d, registrar=%s; con=%s(%p); con->root=%p\n",
tmp->name, prio_item->exten, prio_item->priority, registrar, con? con->name : "<nil>", con, con? con->root_table: NULL);
/* set matchcid to 1 to insure we get a direct match, and NULL registrar to make sure no wildcarding is done */
- ast_context_remove_extension_callerid2(tmp, prio_item->exten, prio_item->priority, prio_item->cidmatch, 1, NULL, 1);
+ ast_copy_string(extension, prio_item->exten, sizeof(extension));
+ if (prio_item->cidmatch) {
+ ast_copy_string(cidmatch, prio_item->cidmatch, sizeof(cidmatch));
+ }
+ end_traversal &= ast_context_remove_extension_callerid2(tmp, extension, prio_item->priority, prio_item->cidmatch ? cidmatch : NULL, 1, NULL, 1);
+ }
+ /* Explanation:
+ * ast_context_remove_extension_callerid2 will destroy the extension that it comes across. This
+ * destruction includes destroying the exten's peer_table, which we are currently traversing. If
+ * ast_context_remove_extension_callerid2 ever should return '0' then this means we have destroyed
+ * the hashtable which we are traversing, and thus calling ast_hashtab_end_traversal will result
+ * in reading invalid memory. Thus, if we detect that we destroyed the hashtable, then we will simply
+ * free the iterator
+ */
+ if (end_traversal) {
+ ast_hashtab_end_traversal(prio_iter);
+ } else {
+ ast_free(prio_iter);
}
- ast_hashtab_end_traversal(prio_iter);
}
ast_hashtab_end_traversal(exten_iter);
}