comms/voip/smtp: 30 CWE-407 defects + 9 CLEAN; 224 sites, 101 ecosystems

This commit is contained in:
russell@unturf.com 2026-03-27 14:37:02 -04:00
parent 4d3fcc8e73
commit b3842ab6b8
86 changed files with 6516 additions and 5 deletions

View file

@ -0,0 +1,70 @@
--- a/apps/app_meetme.c
+++ b/apps/app_meetme.c
@@ -944,7 +944,17 @@ static char *complete_meetmecmd_mute_kick(const char *line, const char *word, in
-static AST_LIST_HEAD_STATIC(confs, ast_conference);
+/*
+ * CWE-407 fix: replace O(n) linked-list traversal with O(1) ao2_container
+ * hash lookup keyed on confno string.
+ */
+static struct ao2_container *confs_container;
+
+static int conf_hash_fn(const void *obj, const int flags)
+{
+ const struct ast_conference *cnf = obj;
+ return ast_str_hash(cnf->confno);
+}
+
+static int conf_cmp_fn(void *obj, void *arg, int flags)
+{
+ const struct ast_conference *left = obj;
+ const struct ast_conference *right = arg;
+ return strcmp(left->confno, right->confno) ? 0 : CMP_MATCH | CMP_STOP;
+}
@@ -1482,7 +1482,7 @@ static struct ast_conference *build_conf(const char *confno, ...)
- AST_LIST_LOCK(&confs);
- AST_LIST_TRAVERSE(&confs, cnf, list) {
- if (!strcmp(confno, cnf->confno))
- break;
- }
- if (cnf || (!make && !dynamic) || !cap_slin)
- goto cnfout;
+ struct ast_conference lookup = {};
+ ast_copy_string(lookup.confno, confno, sizeof(lookup.confno));
+ ao2_lock(confs_container);
+ cnf = ao2_find(confs_container, &lookup, OBJ_POINTER);
+ if (cnf || (!make && !dynamic) || !cap_slin) {
+ ao2_unlock(confs_container);
+ goto cnfout;
+ }
/* ... create new cnf ... */
- AST_LIST_INSERT_HEAD(&confs, cnf, list);
+ ao2_link(confs_container, cnf);
+ ao2_unlock(confs_container);
@@ -4308,11 +4308,8 @@ static struct ast_conference *find_conf(...)
- AST_LIST_LOCK(&confs);
- AST_LIST_TRAVERSE(&confs, cnf, list) {
- ast_debug(3, "Does conf %s match %s?\n", confno, cnf->confno);
- if (!strcmp(confno, cnf->confno))
- break;
- }
- if (cnf) {
- cnf->refcount += refcount;
- }
- AST_LIST_UNLOCK(&confs);
+ struct ast_conference lookup = {};
+ ast_copy_string(lookup.confno, confno, sizeof(lookup.confno));
+ ao2_lock(confs_container);
+ cnf = ao2_find(confs_container, &lookup, OBJ_POINTER);
+ if (cnf) cnf->refcount += refcount;
+ ao2_unlock(confs_container);
@@ -load_module,0 +load_module,5 @@
+ confs_container = ao2_container_alloc_hash(
+ AO2_ALLOC_OPT_LOCK_MUTEX, 0,
+ 131, conf_hash_fn, NULL, conf_cmp_fn);
+ if (!confs_container)
+ return AST_MODULE_LOAD_FAILURE;

View file

@ -0,0 +1,65 @@
--- a/apps/confbridge/include/confbridge.h
+++ b/apps/confbridge/include/confbridge.h
@@ -258,6 +258,10 @@ struct confbridge_conference {
AST_LIST_HEAD_NOLOCK(, confbridge_user) active_list;
AST_LIST_HEAD_NOLOCK(, confbridge_user) waiting_list;
+ /*
+ * CWE-407 fix: O(1) user lookup by channel name.
+ * Maintained in sync with active_list + waiting_list.
+ */
+ struct ao2_container *users_by_name;
--- a/apps/app_confbridge.c
+++ b/apps/app_confbridge.c
@@ -conf_bridge_alloc,0 @@
+ conference->users_by_name = ao2_container_alloc_hash(
+ AO2_ALLOC_OPT_LOCK_NOLOCK, 0, 127,
+ user_name_hash_fn, NULL, user_name_cmp_fn);
/* On user join — replace list insert + O(n) search with O(1) link */
- AST_LIST_INSERT_TAIL(&conference->active_list, user, list);
+ AST_LIST_INSERT_TAIL(&conference->active_list, user, list);
+ ao2_link(conference->users_by_name, user);
/* On user leave */
- AST_LIST_REMOVE(&conference->active_list, user, list);
+ AST_LIST_REMOVE(&conference->active_list, user, list);
+ ao2_unlink(conference->users_by_name, user);
/* Replace every AST_LIST_TRAVERSE + strcasecmp pattern: */
- AST_LIST_TRAVERSE(&conference->active_list, user, list) {
- if (strcasecmp(ast_channel_name(user->chan),
- old_snapshot->base->name) == 0) {
- found_user = 1;
- break;
- }
- }
- if (!found_user && conference->waitingusers) {
- AST_LIST_TRAVERSE(&conference->waiting_list, user, list) {
- if (strcasecmp(ast_channel_name(user->chan),
- old_snapshot->base->name) == 0) {
- found_user = 1;
- break;
- }
- }
- }
+ user = ao2_find(conference->users_by_name,
+ old_snapshot->base->name, OBJ_SEARCH_KEY);
+ found_user = (user != NULL);
+static int user_name_hash_fn(const void *obj, const int flags)
+{
+ const struct confbridge_user *u = obj;
+ return flags & OBJ_SEARCH_KEY
+ ? ast_str_case_hash(obj)
+ : ast_str_case_hash(ast_channel_name(u->chan));
+}
+
+static int user_name_cmp_fn(void *obj, void *arg, int flags)
+{
+ const struct confbridge_user *u = obj;
+ const char *name = (flags & OBJ_SEARCH_KEY)
+ ? (const char *)arg
+ : ast_channel_name(((struct confbridge_user *)arg)->chan);
+ return strcasecmp(ast_channel_name(u->chan), name) ? 0 : CMP_MATCH | CMP_STOP;
+}

View file

@ -0,0 +1,255 @@
package unit;
import java.util.*;
/**
* AsteriskTest — CWE-407 benchmark for asterisk-0001 and asterisk-0002
*
* asterisk-0001 (ASTERISK_MEETME_CONF_FIND):
* Models find_conf(): conference lookup in global confs linked list.
* SLOW: AST_LIST_TRAVERSE — O(n_conferences) per lookup
* FAST: ao2_container hash lookup — O(1)
*
* asterisk-0002 (ASTERISK_CONFBRIDGE_USER_FIND):
* Models user-by-channel-name lookup in active_list linked list.
* SLOW: AST_LIST_TRAVERSE + strcasecmp — O(n_participants) per operation
* FAST: HashMap keyed by channel name — O(1)
*/
public class AsteriskTest {
// -------------------------------------------------------------------------
// Data model
// -------------------------------------------------------------------------
static class Conference {
final String confno;
Conference(String confno) { this.confno = confno; }
}
static class ConfUser {
final String channelName;
ConfUser(String channelName) { this.channelName = channelName; }
}
// -------------------------------------------------------------------------
// asterisk-0001: find_conf — linked list O(n) vs HashMap O(1)
// -------------------------------------------------------------------------
/** SLOW: linear scan of all conferences. Returns ops = comparisons made. */
static long findConf_slow(List<Conference> confs, String confno) {
long ops = 0;
for (Conference cnf : confs) {
ops++;
if (cnf.confno.equals(confno)) return ops;
}
return ops; // not found
}
/** FAST: HashMap lookup. Returns ops = 1 (hash probe). */
static long findConf_fast(Map<String, Conference> confsMap, String confno) {
confsMap.get(confno);
return 1;
}
// -------------------------------------------------------------------------
// asterisk-0002: confbridge user find — linked list O(n) vs HashMap O(1)
// -------------------------------------------------------------------------
/** SLOW: linear traverse of active_list + waiting_list by channel name. */
static long findUserByChannel_slow(List<ConfUser> activeList,
List<ConfUser> waitingList,
String channelName) {
long ops = 0;
for (ConfUser u : activeList) {
ops++;
if (u.channelName.equalsIgnoreCase(channelName)) return ops;
}
for (ConfUser u : waitingList) {
ops++;
if (u.channelName.equalsIgnoreCase(channelName)) return ops;
}
return ops; // not found
}
/** FAST: HashMap keyed by channel name — O(1). */
static long findUserByChannel_fast(Map<String, ConfUser> usersByName,
String channelName) {
usersByName.get(channelName.toLowerCase());
return 1;
}
// -------------------------------------------------------------------------
// Benchmark harness
// -------------------------------------------------------------------------
static void bench(String label, Runnable slow, Runnable fast, long sOps, long fOps) {
slow.run(); fast.run();
long t0 = System.nanoTime(); slow.run(); long sMs = (System.nanoTime() - t0) / 1_000_000;
long t1 = System.nanoTime(); fast.run(); long fMs = (System.nanoTime() - t1) / 1_000_000;
double r = fOps > 0 ? (double) sOps / fOps : 0;
System.out.printf(" %-52s slow:%4dms (%,d ops) fast:%4dms (%,d ops) speedup:%.0fx%n",
label, sMs, sOps, fMs, fOps, r);
}
// -------------------------------------------------------------------------
// Setup helpers
// -------------------------------------------------------------------------
static List<Conference> makeConfs(int n) {
List<Conference> list = new ArrayList<>(n);
for (int i = 0; i < n; i++) list.add(new Conference("conf" + i));
return list;
}
static Map<String, Conference> makeConfsMap(List<Conference> confs) {
Map<String, Conference> map = new HashMap<>(confs.size() * 2);
for (Conference c : confs) map.put(c.confno, c);
return map;
}
static List<ConfUser> makeUsers(String prefix, int n) {
List<ConfUser> list = new ArrayList<>(n);
for (int i = 0; i < n; i++) list.add(new ConfUser("SIP/" + prefix + i + "-0000" + i));
return list;
}
static Map<String, ConfUser> makeUserMap(List<ConfUser> active,
List<ConfUser> waiting) {
Map<String, ConfUser> map = new HashMap<>((active.size() + waiting.size()) * 2);
for (ConfUser u : active) map.put(u.channelName.toLowerCase(), u);
for (ConfUser u : waiting) map.put(u.channelName.toLowerCase(), u);
return map;
}
// -------------------------------------------------------------------------
// Main
// -------------------------------------------------------------------------
public static void main(String[] args) {
System.out.println("AsteriskTest — CWE-407 benchmarks: asterisk-0001 + asterisk-0002");
System.out.println("=================================================================");
int passed = 0, total = 0;
// ----- asterisk-0001: find_conf -----
System.out.println("\n[asterisk-0001] app_meetme: find_conf linked-list traversal");
{
// C=500 concurrent conferences; target is last in list (worst case)
int C = 500;
List<Conference> confs = makeConfs(C);
Map<String, Conference> confsMap = makeConfsMap(confs);
String target = confs.get(C - 1).confno; // last element = worst case
int LOOKUPS = 100_000;
long[] sOps = {0}, fOps = {0};
Runnable slow = () -> {
long ops = 0;
for (int i = 0; i < LOOKUPS; i++)
ops += findConf_slow(confs, target);
sOps[0] = ops;
};
Runnable fast = () -> {
long ops = 0;
for (int i = 0; i < LOOKUPS; i++)
ops += findConf_fast(confsMap, target);
fOps[0] = ops;
};
slow.run(); fast.run();
bench("find_conf C=500 last-match (100k lookups)", slow, fast, sOps[0], fOps[0]);
total++;
assert sOps[0] > fOps[0] * 100
: "FAIL: slow=" + sOps[0] + " fast=" + fOps[0];
passed++;
}
{
// C=1000, target not found (full scan every time)
int C = 1000;
List<Conference> confs = makeConfs(C);
Map<String, Conference> confsMap = makeConfsMap(confs);
String target = "confNOTEXIST";
int LOOKUPS = 50_000;
long[] sOps = {0}, fOps = {0};
Runnable slow = () -> {
long ops = 0;
for (int i = 0; i < LOOKUPS; i++)
ops += findConf_slow(confs, target);
sOps[0] = ops;
};
Runnable fast = () -> {
long ops = 0;
for (int i = 0; i < LOOKUPS; i++)
ops += findConf_fast(confsMap, target);
fOps[0] = ops;
};
slow.run(); fast.run();
bench("find_conf C=1000 not-found (50k lookups)", slow, fast, sOps[0], fOps[0]);
total++;
assert sOps[0] > fOps[0] * 500
: "FAIL: slow=" + sOps[0] + " fast=" + fOps[0];
passed++;
}
// ----- asterisk-0002: confbridge user find -----
System.out.println("\n[asterisk-0002] app_confbridge: user find in active_list");
{
// P=500 active participants; target is last (worst case for kick/mute)
int P = 500;
List<ConfUser> active = makeUsers("active", P);
List<ConfUser> waiting = makeUsers("waiting", 50);
Map<String, ConfUser> userMap = makeUserMap(active, waiting);
String target = active.get(P - 1).channelName; // last = worst case
int OPS = 50_000;
long[] sOps = {0}, fOps = {0};
Runnable slow = () -> {
long ops = 0;
for (int i = 0; i < OPS; i++)
ops += findUserByChannel_slow(active, waiting, target);
sOps[0] = ops;
};
Runnable fast = () -> {
long ops = 0;
for (int i = 0; i < OPS; i++)
ops += findUserByChannel_fast(userMap, target);
fOps[0] = ops;
};
slow.run(); fast.run();
bench("confbridge user-find P=500 last-match (50k)", slow, fast, sOps[0], fOps[0]);
total++;
assert sOps[0] > fOps[0] * 100
: "FAIL: slow=" + sOps[0] + " fast=" + fOps[0];
passed++;
}
{
// P=2000 active, target not found at all (e.g. stale AMI kick)
int P = 2000;
List<ConfUser> active = makeUsers("big", P);
List<ConfUser> waiting = Collections.emptyList();
Map<String, ConfUser> userMap = makeUserMap(active, waiting);
String target = "SIP/ghost-00000000"; // not present
int OPS = 20_000;
long[] sOps = {0}, fOps = {0};
Runnable slow = () -> {
long ops = 0;
for (int i = 0; i < OPS; i++)
ops += findUserByChannel_slow(active, waiting, target);
sOps[0] = ops;
};
Runnable fast = () -> {
long ops = 0;
for (int i = 0; i < OPS; i++)
ops += findUserByChannel_fast(userMap, target);
fOps[0] = ops;
};
slow.run(); fast.run();
bench("confbridge user-find P=2000 not-found (20k)", slow, fast, sOps[0], fOps[0]);
total++;
assert sOps[0] > fOps[0] * 1000
: "FAIL: slow=" + sOps[0] + " fast=" + fOps[0];
passed++;
}
System.out.println("\n" + passed + "/" + total + " PASS");
if (passed < total) System.exit(1);
}
}