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 show_peers_context;
/*---------------------------- Forward declarations of functions in chan_sip.c */
/* Note: This is added to help splitting up chan_sip.c into several files
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 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 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_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);
@ -19214,46 +19217,58 @@ int peercomparefunc(const void *a, const void *b)
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 */
static char *_sip_show_peers(int fd, int *total, struct mansession *s, const struct message *m, int argc, const char *argv[])
{
regex_t regexbuf;
int havepattern = FALSE;
struct show_peers_context cont = {
.havepattern = FALSE,
.idtext = "",
.peers_mon_online = 0,
.peers_mon_offline = 0,
.peers_unmon_online = 0,
.peers_unmon_offline = 0,
};
struct sip_peer *peer;
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 peers_mon_online = 0;
int peers_mon_offline = 0;
int peers_unmon_offline = 0;
int peers_unmon_online = 0;
const char *id;
char idtext[256] = "";
int realtimepeers;
struct sip_peer **peerarray;
int k;
realtimepeers = ast_check_realtime("sippeers");
cont.realtimepeers = ast_check_realtime("sippeers");
if (s) { /* Manager - get ActionID */
id = astman_get_header(m, "ActionID");
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) {
case 5:
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;
}
havepattern = TRUE;
cont.havepattern = TRUE;
} else {
return CLI_SHOWUSAGE;
}
@ -19265,7 +19280,7 @@ static char *_sip_show_peers(int fd, int *total, struct mansession *s, const str
if (!s) {
/* 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);
@ -19291,7 +19306,7 @@ static char *_sip_show_peers(int fd, int *total, struct mansession *s, const str
continue;
}
if (havepattern && regexec(&regexbuf, peer->name, 0, NULL, 0)) {
if (cont.havepattern && regexec(&cont.regexbuf, peer->name, 0, NULL, 0)) {
ao2_unlock(peer);
sip_unref_peer(peer, "toss iterator peer ptr before continue");
continue;
@ -19305,110 +19320,16 @@ static char *_sip_show_peers(int fd, int *total, struct mansession *s, const str
qsort(peerarray, total_peers, sizeof(struct sip_peer *), peercomparefunc);
for(k = 0; k < total_peers; k++) {
char status[20] = "";
char pstatus;
/*
* tmp_port and tmp_host store copies of ast_sockaddr_stringify strings since the
* string pointers for that function aren't valid between subsequent calls to
* ast_sockaddr_stringify functions
*/
char *tmp_port;
char *tmp_host;
peer = peerarray[k];
tmp_port = ast_sockaddr_isnull(&peer->addr) ?
"0" : ast_strdupa(ast_sockaddr_stringify_port(&peer->addr));
tmp_host = ast_sockaddr_isnull(&peer->addr) ?
"(Unspecified)" : ast_strdupa(ast_sockaddr_stringify_addr(&peer->addr));
ao2_lock(peer);
if (havepattern && regexec(&regexbuf, peer->name, 0, NULL, 0)) {
ao2_unlock(peer);
peer = peerarray[k] = sip_unref_peer(peer, "toss iterator peer ptr before continue");
continue;
}
if (!ast_strlen_zero(peer->username) && !s) {
snprintf(name, sizeof(name), "%s/%s", peer->name, peer->username);
} else {
ast_copy_string(name, peer->name, sizeof(name));
}
pstatus = peer_status(peer, status, sizeof(status));
if (pstatus == 1) {
peers_mon_online++;
} else if (pstatus == 0) {
peers_mon_offline++;
} else {
if (ast_sockaddr_isnull(&peer->addr) ||
!ast_sockaddr_port(&peer->addr)) {
peers_unmon_offline++;
} else {
peers_unmon_online++;
}
}
if (!s) { /* Normal CLI list */
ast_cli(fd, FORMAT2, name,
tmp_host,
peer->host_dynamic ? " D " : " ", /* Dynamic or not? */
ast_test_flag(&peer->flags[2], SIP_PAGE3_NAT_AUTO_RPORT) ?
ast_test_flag(&peer->flags[0], SIP_NAT_FORCE_RPORT) ? " A " : " a " :
ast_test_flag(&peer->flags[0], SIP_NAT_FORCE_RPORT) ? " N " : " ", /* NAT=yes? */
(!ast_acl_list_is_empty(peer->acl)) ? " A " : " ", /* permit/deny */
tmp_port, status,
peer->description ? peer->description : "",
realtimepeers ? (peer->is_realtime ? "Cached RT" : "") : "");
} else { /* Manager format */
/* The names here need to be the same as other channels */
astman_append(s,
"Event: PeerEntry\r\n%s"
"Channeltype: SIP\r\n"
"ObjectName: %s\r\n"
"ChanObjectType: peer\r\n" /* "peer" or "user" */
"IPaddress: %s\r\n"
"IPport: %s\r\n"
"Dynamic: %s\r\n"
"AutoForcerport: %s\r\n"
"Forcerport: %s\r\n"
"AutoComedia: %s\r\n"
"Comedia: %s\r\n"
"VideoSupport: %s\r\n"
"TextSupport: %s\r\n"
"ACL: %s\r\n"
"Status: %s\r\n"
"RealtimeDevice: %s\r\n"
"Description: %s\r\n\r\n",
idtext,
peer->name,
ast_sockaddr_isnull(&peer->addr) ? "-none-" : tmp_host,
ast_sockaddr_isnull(&peer->addr) ? "0" : tmp_port,
peer->host_dynamic ? "yes" : "no", /* Dynamic or not? */
ast_test_flag(&peer->flags[2], SIP_PAGE3_NAT_AUTO_RPORT) ? "yes" : "no",
ast_test_flag(&peer->flags[0], SIP_NAT_FORCE_RPORT) ? "yes" : "no", /* NAT=yes? */
ast_test_flag(&peer->flags[2], SIP_PAGE3_NAT_AUTO_COMEDIA) ? "yes" : "no",
ast_test_flag(&peer->flags[1], SIP_PAGE2_SYMMETRICRTP) ? "yes" : "no",
ast_test_flag(&peer->flags[1], SIP_PAGE2_VIDEOSUPPORT) ? "yes" : "no", /* VIDEOSUPPORT=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 */
status,
realtimepeers ? (peer->is_realtime ? "yes" : "no") : "no",
peer->description);
}
ao2_unlock(peer);
peer = peerarray[k] = sip_unref_peer(peer, "toss iterator peer ptr");
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, peers_mon_online, peers_mon_offline, peers_unmon_online, peers_unmon_offline);
total_peers, cont.peers_mon_online, cont.peers_mon_offline, cont.peers_unmon_online, cont.peers_unmon_offline);
}
if (havepattern) {
regfree(&regexbuf);
if (cont.havepattern) {
regfree(&cont.regexbuf);
}
if (total) {
@ -19418,9 +19339,112 @@ static char *_sip_show_peers(int fd, int *total, struct mansession *s, const str
ast_free(peerarray);
return CLI_SUCCESS;
#undef FORMAT2
}
/*! \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 pstatus;
/*
* tmp_port and tmp_host store copies of ast_sockaddr_stringify strings since the
* string pointers for that function aren't valid between subsequent calls to
* ast_sockaddr_stringify functions
*/
char *tmp_port;
char *tmp_host;
tmp_port = ast_sockaddr_isnull(&peer->addr) ?
"0" : ast_strdupa(ast_sockaddr_stringify_port(&peer->addr));
tmp_host = ast_sockaddr_isnull(&peer->addr) ?
"(Unspecified)" : ast_strdupa(ast_sockaddr_stringify_addr(&peer->addr));
ao2_lock(peer);
if (cont->havepattern && regexec(&cont->regexbuf, peer->name, 0, NULL, 0)) {
ao2_unlock(peer);
return sip_unref_peer(peer, "toss iterator peer ptr no match");
}
if (!ast_strlen_zero(peer->username) && !s) {
snprintf(name, sizeof(name), "%s/%s", peer->name, peer->username);
} else {
ast_copy_string(name, peer->name, sizeof(name));
}
pstatus = peer_status(peer, status, sizeof(status));
if (pstatus == 1) {
cont->peers_mon_online++;
} else if (pstatus == 0) {
cont->peers_mon_offline++;
} else {
if (ast_sockaddr_isnull(&peer->addr) ||
!ast_sockaddr_port(&peer->addr)) {
cont->peers_unmon_offline++;
} else {
cont->peers_unmon_online++;
}
}
if (!s) { /* Normal CLI list */
ast_cli(fd, PEERS_FORMAT2, name,
tmp_host,
peer->host_dynamic ? " D " : " ", /* Dynamic or not? */
ast_test_flag(&peer->flags[2], SIP_PAGE3_NAT_AUTO_RPORT) ?
ast_test_flag(&peer->flags[0], SIP_NAT_FORCE_RPORT) ? " A " : " a " :
ast_test_flag(&peer->flags[0], SIP_NAT_FORCE_RPORT) ? " N " : " ", /* NAT=yes? */
(!ast_acl_list_is_empty(peer->acl)) ? " A " : " ", /* permit/deny */
tmp_port, status,
peer->description ? peer->description : "",
cont->realtimepeers ? (peer->is_realtime ? "Cached RT" : "") : "");
} else { /* Manager format */
/* The names here need to be the same as other channels */
astman_append(s,
"Event: PeerEntry\r\n%s"
"Channeltype: SIP\r\n"
"ObjectName: %s\r\n"
"ChanObjectType: peer\r\n" /* "peer" or "user" */
"IPaddress: %s\r\n"
"IPport: %s\r\n"
"Dynamic: %s\r\n"
"AutoForcerport: %s\r\n"
"Forcerport: %s\r\n"
"AutoComedia: %s\r\n"
"Comedia: %s\r\n"
"VideoSupport: %s\r\n"
"TextSupport: %s\r\n"
"ACL: %s\r\n"
"Status: %s\r\n"
"RealtimeDevice: %s\r\n"
"Description: %s\r\n\r\n",
cont->idtext,
peer->name,
ast_sockaddr_isnull(&peer->addr) ? "-none-" : tmp_host,
ast_sockaddr_isnull(&peer->addr) ? "0" : tmp_port,
peer->host_dynamic ? "yes" : "no", /* Dynamic or not? */
ast_test_flag(&peer->flags[2], SIP_PAGE3_NAT_AUTO_RPORT) ? "yes" : "no",
ast_test_flag(&peer->flags[0], SIP_NAT_FORCE_RPORT) ? "yes" : "no", /* NAT=yes? */
ast_test_flag(&peer->flags[2], SIP_PAGE3_NAT_AUTO_COMEDIA) ? "yes" : "no",
ast_test_flag(&peer->flags[1], SIP_PAGE2_SYMMETRICRTP) ? "yes" : "no",
ast_test_flag(&peer->flags[1], SIP_PAGE2_VIDEOSUPPORT) ? "yes" : "no", /* VIDEOSUPPORT=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 */
status,
cont->realtimepeers ? (peer->is_realtime ? "yes" : "no") : "no",
peer->description);
}
ao2_unlock(peer);
return sip_unref_peer(peer, "toss iterator peer ptr");
}
#undef PEERS_FORMAT2
static int peer_dump_func(void *userobj, void *arg, int flags)
{
struct sip_peer *peer = userobj;

Loading…
Cancel
Save