Prevent crash in 'sip show peers' when the number of peers on a system is large

When you have lots of SIP peers (according to the issue reporter, around 3500),
the 'sip show peers' CLI command or AMI action can crash due to a poorly placed
string duplication that occurs on the stack. This patch refactors the command
to not allocate the string on the stack, and handles the formatting of a single
peer in a separate function call.

(closes issue ASTERISK-21466)
Reported by: Guillaume Knispel
patches:
  fix_sip_show_peers_stack_overflow_asterisk_11.3.0-v2.patch uploaded by gknispel (License 6492)
........

Merged revisions 387134 from http://svn.asterisk.org/svn/asterisk/branches/11


git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@387135 65c4cc65-6c06-0410-ace0-fbb531ad65f3
changes/78/78/1
Matthew Jordan 12 years ago
parent 3a5a0f3f26
commit b693a72378

@ -1118,6 +1118,8 @@ static struct ast_config *notify_types = NULL; /*!< The list of manual NOTIFY
struct ao2_container *sip_monitor_instances; struct ao2_container *sip_monitor_instances;
struct show_peers_context;
/*---------------------------- Forward declarations of functions in chan_sip.c */ /*---------------------------- Forward declarations of functions in chan_sip.c */
/* Note: This is added to help splitting up chan_sip.c into several files /* Note: This is added to help splitting up chan_sip.c into several files
in coming releases. */ in coming releases. */
@ -1295,6 +1297,7 @@ static char *transfermode2str(enum transfermodes mode) attribute_const;
static int peer_status(struct sip_peer *peer, char *status, int statuslen); static int peer_status(struct sip_peer *peer, char *status, int statuslen);
static char *sip_show_sched(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a); static char *sip_show_sched(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
static char * _sip_show_peers(int fd, int *total, struct mansession *s, const struct message *m, int argc, const char *argv[]); static char * _sip_show_peers(int fd, int *total, struct mansession *s, const struct message *m, int argc, const char *argv[]);
static struct sip_peer *_sip_show_peers_one(int fd, struct mansession *s, struct show_peers_context *cont, struct sip_peer *peer);
static char *sip_show_peers(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a); static char *sip_show_peers(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
static char *sip_show_objects(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a); static char *sip_show_objects(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
static void print_group(int fd, ast_group_t group, int crlf); static void print_group(int fd, ast_group_t group, int crlf);
@ -19214,46 +19217,58 @@ int peercomparefunc(const void *a, const void *b)
return strcmp((*ap)->name, (*bp)->name); return strcmp((*ap)->name, (*bp)->name);
} }
/* the last argument is left-aligned, so we don't need a size anyways */
#define PEERS_FORMAT2 "%-25.25s %-39.39s %-3.3s %-10.10s %-3.3s %-8s %-11s %-32.32s %s\n"
/*! \brief Used in the sip_show_peers functions to pass parameters */
struct show_peers_context {
regex_t regexbuf;
int havepattern;
char idtext[256];
int realtimepeers;
int peers_mon_online;
int peers_mon_offline;
int peers_unmon_offline;
int peers_unmon_online;
};
/*! \brief Execute sip show peers command */ /*! \brief Execute sip show peers command */
static char *_sip_show_peers(int fd, int *total, struct mansession *s, const struct message *m, int argc, const char *argv[]) static char *_sip_show_peers(int fd, int *total, struct mansession *s, const struct message *m, int argc, const char *argv[])
{ {
regex_t regexbuf; struct show_peers_context cont = {
int havepattern = FALSE; .havepattern = FALSE,
.idtext = "",
.peers_mon_online = 0,
.peers_mon_offline = 0,
.peers_unmon_online = 0,
.peers_unmon_offline = 0,
};
struct sip_peer *peer; struct sip_peer *peer;
struct ao2_iterator* it_peers; struct ao2_iterator* it_peers;
/* the last argument is left-aligned, so we don't need a size anyways */
#define FORMAT2 "%-25.25s %-39.39s %-3.3s %-10.10s %-3.3s %-8s %-11s %-32.32s %s\n"
char name[256];
int total_peers = 0; int total_peers = 0;
int peers_mon_online = 0;
int peers_mon_offline = 0;
int peers_unmon_offline = 0;
int peers_unmon_online = 0;
const char *id; const char *id;
char idtext[256] = "";
int realtimepeers;
struct sip_peer **peerarray; struct sip_peer **peerarray;
int k; int k;
realtimepeers = ast_check_realtime("sippeers"); cont.realtimepeers = ast_check_realtime("sippeers");
if (s) { /* Manager - get ActionID */ if (s) { /* Manager - get ActionID */
id = astman_get_header(m, "ActionID"); id = astman_get_header(m, "ActionID");
if (!ast_strlen_zero(id)) { if (!ast_strlen_zero(id)) {
snprintf(idtext, sizeof(idtext), "ActionID: %s\r\n", id); snprintf(cont.idtext, sizeof(cont.idtext), "ActionID: %s\r\n", id);
} }
} }
switch (argc) { switch (argc) {
case 5: case 5:
if (!strcasecmp(argv[3], "like")) { if (!strcasecmp(argv[3], "like")) {
if (regcomp(&regexbuf, argv[4], REG_EXTENDED | REG_NOSUB)) { if (regcomp(&cont.regexbuf, argv[4], REG_EXTENDED | REG_NOSUB)) {
return CLI_SHOWUSAGE; return CLI_SHOWUSAGE;
} }
havepattern = TRUE; cont.havepattern = TRUE;
} else { } else {
return CLI_SHOWUSAGE; return CLI_SHOWUSAGE;
} }
@ -19265,7 +19280,7 @@ static char *_sip_show_peers(int fd, int *total, struct mansession *s, const str
if (!s) { if (!s) {
/* Normal list */ /* Normal list */
ast_cli(fd, FORMAT2, "Name/username", "Host", "Dyn", "Forcerport", "ACL", "Port", "Status", "Description", (realtimepeers ? "Realtime" : "")); ast_cli(fd, PEERS_FORMAT2, "Name/username", "Host", "Dyn", "Forcerport", "ACL", "Port", "Status", "Description", (cont.realtimepeers ? "Realtime" : ""));
} }
ao2_lock(peers); ao2_lock(peers);
@ -19291,7 +19306,7 @@ static char *_sip_show_peers(int fd, int *total, struct mansession *s, const str
continue; continue;
} }
if (havepattern && regexec(&regexbuf, peer->name, 0, NULL, 0)) { if (cont.havepattern && regexec(&cont.regexbuf, peer->name, 0, NULL, 0)) {
ao2_unlock(peer); ao2_unlock(peer);
sip_unref_peer(peer, "toss iterator peer ptr before continue"); sip_unref_peer(peer, "toss iterator peer ptr before continue");
continue; continue;
@ -19305,6 +19320,35 @@ static char *_sip_show_peers(int fd, int *total, struct mansession *s, const str
qsort(peerarray, total_peers, sizeof(struct sip_peer *), peercomparefunc); qsort(peerarray, total_peers, sizeof(struct sip_peer *), peercomparefunc);
for(k = 0; k < total_peers; k++) { for(k = 0; k < total_peers; k++) {
peerarray[k] = _sip_show_peers_one(fd, s, &cont, peerarray[k]);
}
if (!s) {
ast_cli(fd, "%d sip peers [Monitored: %d online, %d offline Unmonitored: %d online, %d offline]\n",
total_peers, cont.peers_mon_online, cont.peers_mon_offline, cont.peers_unmon_online, cont.peers_unmon_offline);
}
if (cont.havepattern) {
regfree(&cont.regexbuf);
}
if (total) {
*total = total_peers;
}
ast_free(peerarray);
return CLI_SUCCESS;
}
/*! \brief Emit informations for one peer during sip show peers command */
static struct sip_peer *_sip_show_peers_one(int fd, struct mansession *s, struct show_peers_context *cont, struct sip_peer *peer)
{
/* _sip_show_peers_one() is separated from _sip_show_peers() to properly free the ast_strdupa
* (this is executed in a loop in _sip_show_peers() )
*/
char name[256];
char status[20] = ""; char status[20] = "";
char pstatus; char pstatus;
@ -19316,8 +19360,6 @@ static char *_sip_show_peers(int fd, int *total, struct mansession *s, const str
char *tmp_port; char *tmp_port;
char *tmp_host; char *tmp_host;
peer = peerarray[k];
tmp_port = ast_sockaddr_isnull(&peer->addr) ? tmp_port = ast_sockaddr_isnull(&peer->addr) ?
"0" : ast_strdupa(ast_sockaddr_stringify_port(&peer->addr)); "0" : ast_strdupa(ast_sockaddr_stringify_port(&peer->addr));
@ -19325,10 +19367,9 @@ static char *_sip_show_peers(int fd, int *total, struct mansession *s, const str
"(Unspecified)" : ast_strdupa(ast_sockaddr_stringify_addr(&peer->addr)); "(Unspecified)" : ast_strdupa(ast_sockaddr_stringify_addr(&peer->addr));
ao2_lock(peer); ao2_lock(peer);
if (havepattern && regexec(&regexbuf, peer->name, 0, NULL, 0)) { if (cont->havepattern && regexec(&cont->regexbuf, peer->name, 0, NULL, 0)) {
ao2_unlock(peer); ao2_unlock(peer);
peer = peerarray[k] = sip_unref_peer(peer, "toss iterator peer ptr before continue"); return sip_unref_peer(peer, "toss iterator peer ptr no match");
continue;
} }
if (!ast_strlen_zero(peer->username) && !s) { if (!ast_strlen_zero(peer->username) && !s) {
@ -19339,20 +19380,20 @@ static char *_sip_show_peers(int fd, int *total, struct mansession *s, const str
pstatus = peer_status(peer, status, sizeof(status)); pstatus = peer_status(peer, status, sizeof(status));
if (pstatus == 1) { if (pstatus == 1) {
peers_mon_online++; cont->peers_mon_online++;
} else if (pstatus == 0) { } else if (pstatus == 0) {
peers_mon_offline++; cont->peers_mon_offline++;
} else { } else {
if (ast_sockaddr_isnull(&peer->addr) || if (ast_sockaddr_isnull(&peer->addr) ||
!ast_sockaddr_port(&peer->addr)) { !ast_sockaddr_port(&peer->addr)) {
peers_unmon_offline++; cont->peers_unmon_offline++;
} else { } else {
peers_unmon_online++; cont->peers_unmon_online++;
} }
} }
if (!s) { /* Normal CLI list */ if (!s) { /* Normal CLI list */
ast_cli(fd, FORMAT2, name, ast_cli(fd, PEERS_FORMAT2, name,
tmp_host, tmp_host,
peer->host_dynamic ? " D " : " ", /* Dynamic or not? */ peer->host_dynamic ? " D " : " ", /* Dynamic or not? */
ast_test_flag(&peer->flags[2], SIP_PAGE3_NAT_AUTO_RPORT) ? ast_test_flag(&peer->flags[2], SIP_PAGE3_NAT_AUTO_RPORT) ?
@ -19361,7 +19402,7 @@ static char *_sip_show_peers(int fd, int *total, struct mansession *s, const str
(!ast_acl_list_is_empty(peer->acl)) ? " A " : " ", /* permit/deny */ (!ast_acl_list_is_empty(peer->acl)) ? " A " : " ", /* permit/deny */
tmp_port, status, tmp_port, status,
peer->description ? peer->description : "", peer->description ? peer->description : "",
realtimepeers ? (peer->is_realtime ? "Cached RT" : "") : ""); cont->realtimepeers ? (peer->is_realtime ? "Cached RT" : "") : "");
} else { /* Manager format */ } else { /* Manager format */
/* The names here need to be the same as other channels */ /* The names here need to be the same as other channels */
astman_append(s, astman_append(s,
@ -19382,7 +19423,7 @@ static char *_sip_show_peers(int fd, int *total, struct mansession *s, const str
"Status: %s\r\n" "Status: %s\r\n"
"RealtimeDevice: %s\r\n" "RealtimeDevice: %s\r\n"
"Description: %s\r\n\r\n", "Description: %s\r\n\r\n",
idtext, cont->idtext,
peer->name, peer->name,
ast_sockaddr_isnull(&peer->addr) ? "-none-" : tmp_host, ast_sockaddr_isnull(&peer->addr) ? "-none-" : tmp_host,
ast_sockaddr_isnull(&peer->addr) ? "0" : tmp_port, ast_sockaddr_isnull(&peer->addr) ? "0" : tmp_port,
@ -19395,31 +19436,14 @@ static char *_sip_show_peers(int fd, int *total, struct mansession *s, const str
ast_test_flag(&peer->flags[1], SIP_PAGE2_TEXTSUPPORT) ? "yes" : "no", /* TEXTSUPPORT=yes? */ ast_test_flag(&peer->flags[1], SIP_PAGE2_TEXTSUPPORT) ? "yes" : "no", /* TEXTSUPPORT=yes? */
ast_acl_list_is_empty(peer->acl) ? "no" : "yes", /* permit/deny/acl */ ast_acl_list_is_empty(peer->acl) ? "no" : "yes", /* permit/deny/acl */
status, status,
realtimepeers ? (peer->is_realtime ? "yes" : "no") : "no", cont->realtimepeers ? (peer->is_realtime ? "yes" : "no") : "no",
peer->description); peer->description);
} }
ao2_unlock(peer); ao2_unlock(peer);
peer = peerarray[k] = sip_unref_peer(peer, "toss iterator peer ptr");
}
if (!s) {
ast_cli(fd, "%d sip peers [Monitored: %d online, %d offline Unmonitored: %d online, %d offline]\n",
total_peers, peers_mon_online, peers_mon_offline, peers_unmon_online, peers_unmon_offline);
}
if (havepattern) {
regfree(&regexbuf);
}
if (total) { return sip_unref_peer(peer, "toss iterator peer ptr");
*total = total_peers;
}
ast_free(peerarray);
return CLI_SUCCESS;
#undef FORMAT2
} }
#undef PEERS_FORMAT2
static int peer_dump_func(void *userobj, void *arg, int flags) static int peer_dump_func(void *userobj, void *arg, int flags)
{ {

Loading…
Cancel
Save