diff -up pidgin-2.10.7/libpurple/protocols/yahoo/libymsg.c.CVE-2013-6481 pidgin-2.10.7/libpurple/protocols/yahoo/libymsg.c --- pidgin-2.10.7/libpurple/protocols/yahoo/libymsg.c.CVE-2013-6481 2014-01-27 10:20:14.473648650 -0500 +++ pidgin-2.10.7/libpurple/protocols/yahoo/libymsg.c 2014-01-28 20:57:13.990365865 -0500 @@ -2720,7 +2720,7 @@ static void yahoo_p2p_read_pkt_cb(gpoint int pos = 0; int pktlen; struct yahoo_packet *pkt; - guchar *start = NULL; + guchar *start; struct yahoo_p2p_data *p2p_data; YahooData *yd; @@ -2742,19 +2742,29 @@ static void yahoo_p2p_read_pkt_cb(gpoint return; } + /* TODO: It looks like there's a bug here (and above) where an incorrect + * assumtion is being made that the buffer will be added to when this + * is next called, but that's not really the case! */ if(len < YAHOO_PACKET_HDRLEN) return; - if(strncmp((char *)buf, "YMSG", MIN(4, len)) != 0) { + if(strncmp((char *)buf, "YMSG", 4) != 0) { /* Not a YMSG packet */ - purple_debug_warning("yahoo","p2p: Got something other than YMSG packet\n"); + purple_debug_warning("yahoo", "p2p: Got something other than YMSG packet\n"); - start = memchr(buf + 1, 'Y', len - 1); - if (start == NULL) + start = (guchar *) g_strstr_len((char *) buf + 1, len - 1 ,"YMSG"); + if (start == NULL) { + /* remove from p2p connection lists, also calls yahoo_p2p_disconnect_destroy_data */ + if (g_hash_table_lookup(yd->peers, p2p_data->host_username)) + g_hash_table_remove(yd->peers, p2p_data->host_username); + else + yahoo_p2p_disconnect_destroy_data(data); return; + } + purple_debug_warning("yahoo","p2p: Got something other than YMSG packet\n"); - g_memmove(buf, start, len - (start - buf)); - len -= start - buf; + len -= (start - buf); + g_memmove(buf, start, len); } pos += 4; /* YMSG */ @@ -2762,7 +2772,17 @@ static void yahoo_p2p_read_pkt_cb(gpoint pos += 2; pktlen = yahoo_get16(buf + pos); pos += 2; - purple_debug_misc("yahoo", "p2p: %d bytes to read\n", len); + if (len < (YAHOO_PACKET_HDRLEN + pktlen)) { + purple_debug_error("yahoo", "p2p: packet length(%d) > buffer length(%d)\n", + pktlen, (len - pos)); + /* remove from p2p connection lists, also calls yahoo_p2p_disconnect_destroy_data */ + if (g_hash_table_lookup(yd->peers, p2p_data->host_username)) + g_hash_table_remove(yd->peers, p2p_data->host_username); + else + yahoo_p2p_disconnect_destroy_data(data); + return; + } else + purple_debug_misc("yahoo", "p2p: %d bytes to read\n", pktlen); pkt = yahoo_packet_new(0, 0, 0); pkt->service = yahoo_get16(buf + pos); pos += 2;