void IRC_Analyzer::DeliverStream(int length, const u_char* line, bool orig)
	{
	tcp::TCP_ApplicationAnalyzer::DeliverStream(length, line, orig);

	if ( starttls )
		{
		ForwardStream(length, line, orig);
		return;
		}

	// check line size
	if ( length > 512 )
		{
		Weird("irc_line_size_exceeded");
		return;
		}

	string myline = string((const char*) line, length);
	SkipLeadingWhitespace(myline);

	if ( myline.length() < 3 )
		{
		Weird("irc_line_too_short");
		return;
		}

	// Check for prefix.
	string prefix = "";
	if ( myline[0] == ':' )
		{ // find end of prefix and extract it
		auto pos = myline.find(' ');
		if ( pos == string::npos )
			{
			Weird("irc_invalid_line");
			return;
			}

		prefix = myline.substr(1, pos - 1);
		myline = myline.substr(pos + 1);  // remove prefix from line
		SkipLeadingWhitespace(myline);
		}

	if ( orig )
		ProtocolConfirmation();

	int code = 0;
	string command = "";

	// Check if line is long enough to include status code or command.
	// (shortest command with optional params is "WHO")
	if ( myline.length() < 3 )
		{
		Weird("irc_invalid_line");
		ProtocolViolation("line too short");
		return;
		}

	// Check if this is a server reply.
	if ( isdigit(myline[0]) )
		{
		if ( isdigit(myline[1]) && isdigit(myline[2]) &&
		     myline[3] == ' ')
			{
			code = (myline[0] - '0') * 100 +
				(myline[1] - '0') * 10 + (myline[2] - '0');
			myline = myline.substr(4);
			}
		else
			{
			Weird("irc_invalid_reply_number");
			ProtocolViolation("invalid reply number");
			return;
			}
		}
	else
		{ // get command
		auto pos = myline.find(' ');
		// Not all commands require parameters
		if ( pos == string::npos )
			pos = myline.length();

		command = myline.substr(0, pos);
		for ( unsigned int i = 0; i < command.size(); ++i )
			command[i] = toupper(command[i]);

		// Adjust for the no-parameter case
		if ( pos == myline.length() )
			pos--;

		myline = myline.substr(pos + 1);
		SkipLeadingWhitespace(myline);
		}

	// Extract parameters.
	string params = myline;

	// special case
	if ( command == "STARTTLS" )
		return;

	// Check for Server2Server - connections with ZIP enabled.
	if ( orig && orig_status == WAIT_FOR_REGISTRATION )
		{
		if ( command == "PASS" )
			{
			vector<string> p = SplitWords(params,' ');
			if ( p.size() > 3 &&
			     (p[3].find('Z')<=p[3].size() ||
			      p[3].find('z')<=p[3].size()) )
				orig_zip_status = ACCEPT_ZIP;
			else
			        orig_zip_status = NO_ZIP;
			}

		// We do not check if SERVER command is successful, since
		// the connection will be terminated by the server if
		// authentication fails.
		//
		// (### This seems not quite prudent to me - VP)
		if ( command == "SERVER" && prefix == "")
			{
			orig_status = REGISTERED;
			}
		}

	if ( ! orig && resp_status == WAIT_FOR_REGISTRATION )
		{
		if ( command == "PASS" )
			{
			vector<string> p = SplitWords(params,' ');
			if ( p.size() > 3 &&
			     (p[3].find('Z')<=p[3].size() ||
			      p[3].find('z')<=p[3].size()) )
			        resp_zip_status = ACCEPT_ZIP;
			else
			        resp_zip_status = NO_ZIP;

			}

		// Again, don't bother checking whether SERVER command
		// is successful.
		if ( command == "SERVER" && prefix == "")
			resp_status = REGISTERED;
		}

	// Analyze server reply messages.
	if ( code > 0 )
		{
		switch ( code ) {
		/*
		case 1: // RPL_WELCOME
		case 2: // RPL_YOURHOST
		case 3: // RPL_CREATED
		case 4: // RPL_MYINFO
		case 5: // RPL_BOUNCE
		case 252: // number of ops online
		case 253: // number of unknown connections
		case 265: // RPL_LOCALUSERS
		case 312: // whois server reply
		case 315: // end of who list
		case 317: // whois idle reply
		case 318: // end of whois list
		case 366: // end of names list
		case 372: // RPL_MOTD
		case 375: // RPL_MOTDSTART
		case 376: // RPL_ENDOFMOTD
		case 331: // RPL_NOTOPIC
			break;
		*/

		// Count of users, services and servers in whole network.
		case 251:
			if ( ! irc_network_info )
				break;

			{
			vector<string> parts = SplitWords(params, ' ');
			int users = 0;
			int services = 0;
			int servers = 0;

			for ( unsigned int i = 1; i < parts.size(); ++i )
				{
				if ( parts[i] == "users" )
					users = atoi(parts[i-1].c_str());
				else if ( parts[i] == "services" )
					services = atoi(parts[i-1].c_str());
				else if ( parts[i] == "servers" )
					servers = atoi(parts[i-1].c_str());
				// else ###
				}

			val_list* vl = new val_list;
			vl->append(BuildConnVal());
			vl->append(new Val(orig, TYPE_BOOL));
			vl->append(new Val(users, TYPE_INT));
			vl->append(new Val(services, TYPE_INT));
			vl->append(new Val(servers, TYPE_INT));

			ConnectionEvent(irc_network_info, vl);
			}
			break;

		// List of users in a channel (names command).
		case 353:
			if ( ! irc_names_info )
				break;

			{
			vector<string> parts = SplitWords(params, ' ');

			// Remove nick name.
			parts.erase(parts.begin());
			if ( parts.size() < 2 )
				{
				Weird("irc_invalid_names_line");
				return;
				}

			string type = parts[0];
			string channel = parts[1];

			// Remove type and channel.
			parts.erase(parts.begin());
			parts.erase(parts.begin());

			if ( parts.size() > 0 && parts[0][0] == ':' )
				parts[0] = parts[0].substr(1);

			val_list* vl = new val_list;
			vl->append(BuildConnVal());
			vl->append(new Val(orig, TYPE_BOOL));
			vl->append(new StringVal(type.c_str()));
			vl->append(new StringVal(channel.c_str()));

			TableVal* set = new TableVal(string_set);
			for ( unsigned int i = 0; i < parts.size(); ++i )
				{
				if ( parts[i][0] == '@' )
					parts[i] = parts[i].substr(1);
				Val* idx = new StringVal(parts[i].c_str());
				set->Assign(idx, 0);
				Unref(idx);
				}
			vl->append(set);

			ConnectionEvent(irc_names_info, vl);
			}
			break;

		// Count of users and services on this server.
		case 255:
			if ( ! irc_server_info )
				break;

			{
			vector<string> parts = SplitWords(params, ' ');
			int users = 0;
			int services = 0;
			int servers = 0;

			for ( unsigned int i = 1; i < parts.size(); ++i )
				{
				if ( parts[i] == "users," )
					users = atoi(parts[i-1].c_str());
				else if ( parts[i] == "clients" )
					users = atoi(parts[i-1].c_str());
				else if ( parts[i] == "services" )
					services = atoi(parts[i-1].c_str());
				else if ( parts[i] == "servers" )
					servers = atoi(parts[i-1].c_str());
				// else ###
				}

			val_list* vl = new val_list;
			vl->append(BuildConnVal());
			vl->append(new Val(orig, TYPE_BOOL));
			vl->append(new Val(users, TYPE_INT));
			vl->append(new Val(services, TYPE_INT));
			vl->append(new Val(servers, TYPE_INT));

			ConnectionEvent(irc_server_info, vl);
			}
			break;

		// Count of channels.
		case 254:
			if ( ! irc_channel_info )
				break;

			{
			vector<string> parts = SplitWords(params, ' ');
			int channels = 0;
			for ( unsigned int i = 1; i < parts.size(); ++i )
				if ( parts[i] == ":channels" )
					channels = atoi(parts[i - 1].c_str());

			val_list* vl = new val_list;
			vl->append(BuildConnVal());
			vl->append(new Val(orig, TYPE_BOOL));
			vl->append(new Val(channels, TYPE_INT));

			ConnectionEvent(irc_channel_info, vl);
			}
			break;

		// RPL_GLOBALUSERS
		case 266:
			{
			// FIXME: We should really streamline all this
			// parsing code ...
			if ( ! irc_global_users )
				break;

			const char* prefix = params.c_str();

			const char* eop = strchr(prefix, ' ');
			if ( ! eop )
				{
				Weird("invalid_irc_global_users_reply");
				break;
				}

			const char *msg = strchr(++eop, ':');
			if ( ! msg )
				{
				Weird("invalid_irc_global_users_reply");
				break;
				}

			val_list* vl = new val_list;
			vl->append(BuildConnVal());
			vl->append(new Val(orig, TYPE_BOOL));
			vl->append(new StringVal(eop - prefix, prefix));
			vl->append(new StringVal(++msg));
			ConnectionEvent(irc_global_users, vl);
			break;
			}

		// WHOIS user reply line.
		case 311:
			if ( ! irc_whois_user_line )
				break;

			{
			vector<string> parts = SplitWords(params, ' ');

			if ( parts.size() > 1 )
				parts.erase(parts.begin());
			if ( parts.size() < 5 )
				{
				Weird("irc_invalid_whois_user_line");
				return;
				}

			val_list* vl = new val_list;
			vl->append(BuildConnVal());
			vl->append(new Val(orig, TYPE_BOOL));
			vl->append(new StringVal(parts[0].c_str()));
			vl->append(new StringVal(parts[1].c_str()));
			vl->append(new StringVal(parts[2].c_str()));

			parts.erase(parts.begin(), parts.begin() + 4);

			string real_name = parts[0];
			for ( unsigned int i = 1; i < parts.size(); ++i )
				real_name = real_name + " " + parts[i];

			if ( real_name[0] == ':' )
				real_name = real_name.substr(1);

			vl->append(new StringVal(real_name.c_str()));

			ConnectionEvent(irc_whois_user_line, vl);
			}
			break;

		// WHOIS operator reply line.
		case 313:
			if ( ! irc_whois_operator_line )
				break;

			{
			vector<string> parts = SplitWords(params, ' ');

			if ( parts.size() > 1 )
				parts.erase(parts.begin());

			if ( parts.size() < 2 )
				{
				Weird("irc_invalid_whois_operator_line");
				return;
				}

			val_list* vl = new val_list;
			vl->append(BuildConnVal());
			vl->append(new Val(orig, TYPE_BOOL));
			vl->append(new StringVal(parts[0].c_str()));

			ConnectionEvent(irc_whois_operator_line, vl);
			}
			break;

		// WHOIS channel reply.
		case 319:
			if ( ! irc_whois_channel_line )
				break;

			{
			vector<string> parts = SplitWords(params, ' ');

			// Remove nick name.
			parts.erase(parts.begin());
			if ( parts.size() < 2 )
				{
				Weird("irc_invalid_whois_channel_line");
				return;
				}

			string nick = parts[0];
			parts.erase(parts.begin());

			if ( parts.size() > 0 && parts[0][0] == ':' )
				parts[0] = parts[0].substr(1);

			val_list* vl = new val_list;
			vl->append(BuildConnVal());
			vl->append(new Val(orig, TYPE_BOOL));
			vl->append(new StringVal(nick.c_str()));
			TableVal* set = new TableVal(string_set);
			for ( unsigned int i = 0; i < parts.size(); ++i )
				{
				Val* idx = new StringVal(parts[i].c_str());
				set->Assign(idx, 0);
				Unref(idx);
				}

			vl->append(set);

			ConnectionEvent(irc_whois_channel_line, vl);
			}
			break;

		// RPL_TOPIC reply.
		case 332:
			{
			if ( ! irc_channel_topic )
				break;

			vector<string> parts = SplitWords(params, ' ');
			if ( parts.size() < 4 )
				{
				Weird("irc_invalid_topic_reply");
				return;
				}

			unsigned int pos = params.find(':');
			if ( pos < params.size() )
				{
				string topic = params.substr(pos + 1);
				val_list* vl = new val_list;

				vl->append(BuildConnVal());
				vl->append(new Val(orig, TYPE_BOOL));
				vl->append(new StringVal(parts[1].c_str()));

				const char* t = topic.c_str();
				if ( *t == ':' )
					++t;

				vl->append(new StringVal(t));

				ConnectionEvent(irc_channel_topic, vl);
				}
			else
				{
				Weird("irc_invalid_topic_reply");
				return;
				}
			break;
			}

		// WHO reply line.
		case 352:
			if ( ! irc_who_line )
				break;

			{
			vector<string> parts = SplitWords(params, ' ');
			if ( parts.size() < 9 )
				{
				Weird("irc_invalid_who_line");
				return;
				}

			val_list* vl = new val_list;
			vl->append(BuildConnVal());
			vl->append(new Val(orig, TYPE_BOOL));
			vl->append(new StringVal(parts[0].c_str()));
			vl->append(new StringVal(parts[1].c_str()));
			if ( parts[2][0] == '~' )
				parts[2] = parts[2].substr(1);
			vl->append(new StringVal(parts[2].c_str()));
			vl->append(new StringVal(parts[3].c_str()));
			vl->append(new StringVal(parts[4].c_str()));
			vl->append(new StringVal(parts[5].c_str()));
			vl->append(new StringVal(parts[6].c_str()));
			if ( parts[7][0] == ':' )
				parts[7] = parts[7].substr(1);
			vl->append(new Val(atoi(parts[7].c_str()), TYPE_INT));
			vl->append(new StringVal(parts[8].c_str()));

			ConnectionEvent(irc_who_line, vl);
			}
			break;

		// Invalid nick name.
		case 431:
		case 432:
		case 433:
		case 436:
			if ( irc_invalid_nick )
				{
				val_list* vl = new val_list;
				vl->append(BuildConnVal());
				vl->append(new Val(orig, TYPE_BOOL));
				ConnectionEvent(irc_invalid_nick, vl);
				}
			break;

		// Operator responses.
		case 381:  // User is operator
		case 491:  // user is not operator
			if ( irc_oper_response )
				{
				val_list* vl = new val_list;
				vl->append(BuildConnVal());
				vl->append(new Val(orig, TYPE_BOOL));
				vl->append(new Val(code == 381, TYPE_BOOL));
				ConnectionEvent(irc_oper_response, vl);
				}
			break;

		case 670:
			// StartTLS success reply to StartTLS
			StartTLS();
			break;

		// All other server replies.
		default:
			val_list* vl = new val_list;
			vl->append(BuildConnVal());
			vl->append(new Val(orig, TYPE_BOOL));
			vl->append(new StringVal(prefix.c_str()));
			vl->append(new Val(code, TYPE_COUNT));
			vl->append(new StringVal(params.c_str()));

			ConnectionEvent(irc_reply, vl);
			break;
		}
		return;
		}

	// Check if command is valid.
	if ( command.size() > 20 )
		{
		Weird("irc_invalid_command");
		if ( ++invalid_msg_count > invalid_msg_max_count )
			{
			Weird("irc_too_many_invalid");
			ProtocolViolation("too many long lines");
			return;
			}
		return;
		}

	else if ( ( irc_privmsg_message || irc_dcc_message ) && command == "PRIVMSG")
		{
		unsigned int pos = params.find(' ');
		if ( pos >= params.size() )
			{
			Weird("irc_invalid_privmsg_message_format");
			return;
			}

		string target = params.substr(0, pos);
		string message = params.substr(pos + 1);
		SkipLeadingWhitespace(message);

		if ( message.size() > 0 && message[0] == ':' )
			message = message.substr(1);
		if ( message.size() > 0 && message[0] == 1 )
			message = message.substr(1); // DCC

		// Check for DCC messages.
		if ( message.size() > 3 && message.substr(0, 3) == "DCC" )
			{
			if ( message.size() > 0 &&
			     message[message.size() - 1] == 1 )
				message = message.substr(0, message.size() - 1);

			vector<string> parts = SplitWords(message, ' ');
			if ( parts.size() < 5 || parts.size() > 6 )
				{
				// Turbo DCC extension appends a "T" at the end of handshake.
				if ( ! (parts.size() == 7 && parts[6] == "T") )
					{
					Weird("irc_invalid_dcc_message_format");
					return;
					}
				}

			// Calculate IP address.
			uint32 raw_ip = 0;
			for ( unsigned int i = 0; i < parts[3].size(); ++i )
				{
				string s = parts[3].substr(i, 1);
				raw_ip = (10 * raw_ip) + atoi(s.c_str());
				}

			val_list* vl = new val_list;
			vl->append(BuildConnVal());
			vl->append(new Val(orig, TYPE_BOOL));
			vl->append(new StringVal(prefix.c_str()));
			vl->append(new StringVal(target.c_str()));
			vl->append(new StringVal(parts[1].c_str()));
			vl->append(new StringVal(parts[2].c_str()));
			vl->append(new AddrVal(htonl(raw_ip)));
			vl->append(new Val(atoi(parts[4].c_str()), TYPE_COUNT));
			if ( parts.size() >= 6 )
				vl->append(new Val(atoi(parts[5].c_str()),
							TYPE_COUNT));
			else
				vl->append(new Val(0, TYPE_COUNT));

			ConnectionEvent(irc_dcc_message, vl);
			}

		else
			{
			val_list* vl = new val_list;
			vl->append(BuildConnVal());
			vl->append(new Val(orig, TYPE_BOOL));
			vl->append(new StringVal(prefix.c_str()));
			vl->append(new StringVal(target.c_str()));
			vl->append(new StringVal(message.c_str()));

			ConnectionEvent(irc_privmsg_message, vl);
			}
		}

	else if ( irc_notice_message && command == "NOTICE" )
		{
		unsigned int pos = params.find(' ');
		if ( pos >= params.size() )
			{
			Weird("irc_invalid_notice_message_format");
			return;
			}

		string target = params.substr(0, pos);
		string message = params.substr(pos + 1);
		SkipLeadingWhitespace(message);
		if ( message[0] == ':' )
			message = message.substr(1);

		val_list* vl = new val_list;
		vl->append(BuildConnVal());
		vl->append(new Val(orig, TYPE_BOOL));
		vl->append(new StringVal(prefix.c_str()));
		vl->append(new StringVal(target.c_str()));
		vl->append(new StringVal(message.c_str()));

		ConnectionEvent(irc_notice_message, vl);
		}

	else if ( irc_squery_message && command == "SQUERY" )
		{
		unsigned int pos = params.find(' ');
		if ( pos >= params.size() )
			{
			Weird("irc_invalid_squery_message_format");
			return;
			}

		string target = params.substr(0, pos);
		string message = params.substr(pos + 1);
		SkipLeadingWhitespace(message);
		if ( message[0] == ':' )
			message = message.substr(1);

		val_list* vl = new val_list;
		vl->append(BuildConnVal());
		vl->append(new Val(orig, TYPE_BOOL));
		vl->append(new StringVal(prefix.c_str()));
		vl->append(new StringVal(target.c_str()));
		vl->append(new StringVal(message.c_str()));

		ConnectionEvent(irc_squery_message, vl);
		}

	else if ( irc_user_message && command == "USER" )
		{
		// extract username and real name
		vector<string> parts = SplitWords(params, ' ');
		val_list* vl = new val_list;
		vl->append(BuildConnVal());
		vl->append(new Val(orig, TYPE_BOOL));

		if ( parts.size() > 0 )
			vl->append(new StringVal(parts[0].c_str()));
		else vl->append(new StringVal(""));

		if ( parts.size() > 1 )
			vl->append(new StringVal(parts[1].c_str()));
		else vl->append(new StringVal(""));

		if ( parts.size() > 2 )
			vl->append(new StringVal(parts[2].c_str()));
		else vl->append(new StringVal(""));

		string realname;
		for ( unsigned int i = 3; i < parts.size(); i++ )
			{
			realname += parts[i];
			if ( i > 3 )
				realname += " ";
			}

		const char* name = realname.c_str();
		vl->append(new StringVal(*name == ':' ? name + 1 : name));

		ConnectionEvent(irc_user_message, vl);
		}

	else if ( irc_oper_message && command == "OPER" )
		{
		// extract username and password
		vector<string> parts = SplitWords(params, ' ');
		if ( parts.size() == 2 )
			{
			val_list* vl = new val_list;
			vl->append(BuildConnVal());
			vl->append(new Val(orig, TYPE_BOOL));
			vl->append(new StringVal(parts[0].c_str()));
			vl->append(new StringVal(parts[1].c_str()));

			ConnectionEvent(irc_oper_message, vl);
			}

		else
			Weird("irc_invalid_oper_message_format");
		}

	else if ( irc_kick_message && command == "KICK" )
		{
		// Extract channels, users and comment.
		vector<string> parts = SplitWords(params, ' ');
		if ( parts.size() <= 1 )
			{
			Weird("irc_invalid_kick_message_format");
			return;
			}

		val_list* vl = new val_list;
		vl->append(BuildConnVal());
		vl->append(new Val(orig, TYPE_BOOL));
		vl->append(new StringVal(prefix.c_str()));
		vl->append(new StringVal(parts[0].c_str()));
		vl->append(new StringVal(parts[1].c_str()));
		if ( parts.size() > 2 )
			{
			string comment = parts[2];
			for ( unsigned int i = 3; i < parts.size(); ++i )
				comment += " " + parts[i];

			if ( comment[0] == ':' )
				comment = comment.substr(1);

			vl->append(new StringVal(comment.c_str()));
			}
		else
			vl->append(new StringVal(""));

		ConnectionEvent(irc_kick_message, vl);
		}

	else if ( irc_join_message && command == "JOIN" )
		{
		if ( params[0] == ':' )
			params = params.substr(1);

		vector<string> parts = SplitWords(params, ' ');

		if ( parts.size() < 1 )
			{
			Weird("irc_invalid_join_line");
			return;
			}

		string nickname = "";
		if ( prefix.size() > 0 )
			{
			unsigned int pos = prefix.find('!');
			if ( pos < prefix.size() )
				nickname = prefix.substr(0, pos);
			}

		val_list* vl = new val_list;
		vl->append(BuildConnVal());
		vl->append(new Val(orig, TYPE_BOOL));

		TableVal* list = new TableVal(irc_join_list);
		vector<string> channels = SplitWords(parts[0], ',');
		vector<string> passwords;

		if ( parts.size() > 1 )
			passwords = SplitWords(parts[1], ',');

		string empty_string = "";
		for ( unsigned int i = 0; i < channels.size(); ++i )
			{
			RecordVal* info = new RecordVal(irc_join_info);
			info->Assign(0, new StringVal(nickname.c_str()));
			info->Assign(1, new StringVal(channels[i].c_str()));
			if ( i < passwords.size() )
				info->Assign(2, new StringVal(passwords[i].c_str()));
			else
				info->Assign(2, new StringVal(empty_string.c_str()));
			// User mode.
			info->Assign(3, new StringVal(empty_string.c_str()));
			list->Assign(info, 0);
			Unref(info);
			}

		vl->append(list);

		ConnectionEvent(irc_join_message, vl);
		}

	else if ( irc_join_message && command == "NJOIN" )
		{
		vector<string> parts = SplitWords(params, ' ');
		if ( parts.size() != 2 )
			{
			Weird("irc_invalid_njoin_line");
			return;
			}

		string channel = parts[0];
		if ( parts[1][0] == ':' )
			parts[1] = parts[1].substr(1);

		vector<string> users = SplitWords(parts[1], ',');

		val_list* vl = new val_list;
		vl->append(BuildConnVal());
		vl->append(new Val(orig, TYPE_BOOL));

		TableVal* list = new TableVal(irc_join_list);
		string empty_string = "";

		for ( unsigned int i = 0; i < users.size(); ++i )
			{
			RecordVal* info = new RecordVal(irc_join_info);
			string nick = users[i];
			string mode = "none";

			if ( nick[0] == '@' )
				{
				if ( nick[1] == '@' )
					{
					nick = nick.substr(2);
					mode = "creator";
					}
				else
					{
					nick = nick.substr(1);
					mode = "operator";
					}
				}

			else if ( nick[0] == '+' )
				{
				nick = nick.substr(1);
				mode = "voice";
				}

			info->Assign(0, new StringVal(nick.c_str()));
			info->Assign(1, new StringVal(channel.c_str()));
			// Password:
			info->Assign(2, new StringVal(empty_string.c_str()));
			// User mode:
			info->Assign(3, new StringVal(mode.c_str()));
			list->Assign(info, 0);
			Unref(info);
			}

		vl->append(list);

		ConnectionEvent(irc_join_message, vl);
		}

	else if ( irc_part_message && command == "PART" )
		{
		string channels = params;
		string message = "";
		unsigned int pos = params.find(' ');

		if ( pos < params.size() )
			{
			channels = params.substr(0, pos);
			if ( params.size() > pos + 1 )
				{
				message = params.substr(pos + 1);
				SkipLeadingWhitespace(message);
				}
			if ( message[0] == ':' )
				message = message.substr(1);
			}

		string nick = prefix;
		pos = nick.find('!');
		if ( pos < nick.size() )
			nick = nick.substr(0, pos);

		vector<string> channelList = SplitWords(channels, ',');
		TableVal* set = new TableVal(string_set);

		for ( unsigned int i = 0; i < channelList.size(); ++i )
			{
			Val* idx = new StringVal(channelList[i].c_str());
			set->Assign(idx, 0);
			Unref(idx);
			}

		val_list* vl = new val_list;
		vl->append(BuildConnVal());
		vl->append(new Val(orig, TYPE_BOOL));
		vl->append(new StringVal(nick.c_str()));
		vl->append(set);
		vl->append(new StringVal(message.c_str()));

		ConnectionEvent(irc_part_message, vl);
		}

	else if ( irc_quit_message && command == "QUIT" )
		{
		string message = params;
		if ( message[0] == ':' )
			message = message.substr(1);

		string nickname = "";
		if ( prefix.size() > 0 )
			{
			unsigned int pos = prefix.find('!');
			if ( pos < prefix.size() )
				nickname = prefix.substr(0, pos);
			}

		val_list* vl = new val_list;
		vl->append(BuildConnVal());
		vl->append(new Val(orig, TYPE_BOOL));
		vl->append(new StringVal(nickname.c_str()));
		vl->append(new StringVal(message.c_str()));

		ConnectionEvent(irc_quit_message, vl);
		}

	else if ( irc_nick_message && command == "NICK" )
		{
		string nick = params;
		if ( nick[0] == ':' )
			nick = nick.substr(1);

		val_list* vl = new val_list;
		vl->append(BuildConnVal());
		vl->append(new Val(orig, TYPE_BOOL));
		vl->append(new StringVal(prefix.c_str()));
		vl->append(new StringVal(nick.c_str()));

		ConnectionEvent(irc_nick_message, vl);
		}

	else if ( irc_who_message && command == "WHO" )
		{
		vector<string> parts = SplitWords(params, ' ');
		if ( parts.size() > 2 )
			{
			Weird("irc_invalid_who_message_format");
			return;
			}

		bool oper = false;
		if ( parts.size() == 2 && parts[1] == "o" )
			oper = true;

		// Remove ":" from mask.
		if ( parts.size() > 0 && parts[0].size() > 0 && parts[0][0] == ':' )
			parts[0] = parts[0].substr(1);

		val_list* vl = new val_list;
		vl->append(BuildConnVal());
		vl->append(new Val(orig, TYPE_BOOL));
		if ( parts.size() > 0 )
			vl->append(new StringVal(parts[0].c_str()));
		else
			vl->append(new StringVal(""));
		vl->append(new Val(oper, TYPE_BOOL));

		ConnectionEvent(irc_who_message, vl);
		}

	else if ( irc_whois_message && command == "WHOIS" )
		{
		vector<string> parts = SplitWords(params, ' ');
		if ( parts.size() < 1 || parts.size() > 2 )
			{
			Weird("irc_invalid_whois_message_format");
			return;
			}

		string server = "";
		string users = "";

		if ( parts.size() == 2 )
			{
			server = parts[0];
			users = parts[1];
			}
		else
			users = parts[0];

		val_list* vl = new val_list;
		vl->append(BuildConnVal());
		vl->append(new Val(orig, TYPE_BOOL));
		vl->append(new StringVal(server.c_str()));
		vl->append(new StringVal(users.c_str()));

		ConnectionEvent(irc_whois_message, vl);
		}

	else if ( irc_error_message && command == "ERROR" )
		{
		val_list* vl = new val_list;
		vl->append(BuildConnVal());
		vl->append(new Val(orig, TYPE_BOOL));
		vl->append(new StringVal(prefix.c_str()));
		if ( params[0] == ':' )
			params = params.substr(1);
		vl->append(new StringVal(params.c_str()));

		ConnectionEvent(irc_error_message, vl);
		}

	else if ( irc_invite_message && command == "INVITE" )
		{
		vector<string> parts = SplitWords(params, ' ');
		if ( parts.size() == 2 )
			{ // remove ":" from channel
			if ( parts[1].size() > 0 && parts[1][0] == ':' )
				parts[1] = parts[1].substr(1);

			val_list* vl = new val_list;
			vl->append(BuildConnVal());
			vl->append(new Val(orig, TYPE_BOOL));
			vl->append(new StringVal(prefix.c_str()));
			vl->append(new StringVal(parts[0].c_str()));
			vl->append(new StringVal(parts[1].c_str()));

			ConnectionEvent(irc_invite_message, vl);
			}
		else
			Weird("irc_invalid_invite_message_format");
		}

	else if ( irc_mode_message && command == "MODE" )
		{
		if ( params.size() > 0 )
			{
			val_list* vl = new val_list;
			vl->append(BuildConnVal());
			vl->append(new Val(orig, TYPE_BOOL));
			vl->append(new StringVal(prefix.c_str()));
			vl->append(new StringVal(params.c_str()));

			ConnectionEvent(irc_mode_message, vl);
			}

		else
			Weird("irc_invalid_mode_message_format");
		}

	else if ( irc_password_message && command == "PASS" )
		{
		val_list* vl = new val_list;
		vl->append(BuildConnVal());
		vl->append(new Val(orig, TYPE_BOOL));
		vl->append(new StringVal(params.c_str()));
		ConnectionEvent(irc_password_message, vl);
		}

	else if ( irc_squit_message && command == "SQUIT" )
		{
		string server = params;
		string message = "";

		unsigned int pos = params.find(' ');
		if ( pos < params.size() )
			{
			server = params.substr(0, pos);
			message = params.substr(pos + 1);
			SkipLeadingWhitespace(message);
			if ( message[0] == ':' )
				message = message.substr(1);
			}

		val_list* vl = new val_list;
		vl->append(BuildConnVal());
		vl->append(new Val(orig, TYPE_BOOL));
		vl->append(new StringVal(prefix.c_str()));
		vl->append(new StringVal(server.c_str()));
		vl->append(new StringVal(message.c_str()));

		ConnectionEvent(irc_squit_message, vl);
		}

	else if ( orig )
		{
		if ( irc_request )
			{
			val_list* vl = new val_list;
			vl->append(BuildConnVal());
			vl->append(new Val(orig, TYPE_BOOL));
			vl->append(new StringVal(prefix.c_str()));
			vl->append(new StringVal(command.c_str()));
			vl->append(new StringVal(params.c_str()));

			ConnectionEvent(irc_request, vl);
			}
		}

	else
		{
		if ( irc_message )
			{
			val_list* vl = new val_list;
			vl->append(BuildConnVal());
			vl->append(new Val(orig, TYPE_BOOL));
			vl->append(new StringVal(prefix.c_str()));
			vl->append(new StringVal(command.c_str()));
			vl->append(new StringVal(params.c_str()));

			ConnectionEvent(irc_message, vl);
			}
		}

	if ( orig_status == REGISTERED && resp_status == REGISTERED &&
	     orig_zip_status == ACCEPT_ZIP && resp_zip_status == ACCEPT_ZIP )
		{
		orig_zip_status = ZIP_LOADED;
		resp_zip_status = ZIP_LOADED;
		AddSupportAnalyzer(new zip::ZIP_Analyzer(Conn(), true));
		AddSupportAnalyzer(new zip::ZIP_Analyzer(Conn(), false));
		}

	return;
	}

In Bro through 2.5.5, there is a DoS in IRC protocol names command parsing in analyzer/protocol/irc/IRC.cc.


void IRC_Analyzer::DeliverStream(int length, const u_char* line, bool orig)
	{
	tcp::TCP_ApplicationAnalyzer::DeliverStream(length, line, orig);

	if ( starttls )
		{
		ForwardStream(length, line, orig);
		return;
		}

	// check line size
	if ( length > 512 )
		{
		Weird("irc_line_size_exceeded");
		return;
		}

	string myline = string((const char*) line, length);
	SkipLeadingWhitespace(myline);

	if ( myline.length() < 3 )
		{
		Weird("irc_line_too_short");
		return;
		}

	// Check for prefix.
	string prefix = "";
	if ( myline[0] == ':' )
		{ // find end of prefix and extract it
		auto pos = myline.find(' ');
		if ( pos == string::npos )
			{
			Weird("irc_invalid_line");
			return;
			}

		prefix = myline.substr(1, pos - 1);
		myline = myline.substr(pos + 1);  // remove prefix from line
		SkipLeadingWhitespace(myline);
		}

	if ( orig )
		ProtocolConfirmation();

	int code = 0;
	string command = "";

	// Check if line is long enough to include status code or command.
	// (shortest command with optional params is "WHO")
	if ( myline.length() < 3 )
		{
		Weird("irc_invalid_line");
		ProtocolViolation("line too short");
		return;
		}

	// Check if this is a server reply.
	if ( isdigit(myline[0]) )
		{
		if ( isdigit(myline[1]) && isdigit(myline[2]) &&
		     myline[3] == ' ')
			{
			code = (myline[0] - '0') * 100 +
				(myline[1] - '0') * 10 + (myline[2] - '0');
			myline = myline.substr(4);
			}
		else
			{
			Weird("irc_invalid_reply_number");
			ProtocolViolation("invalid reply number");
			return;
			}
		}
	else
		{ // get command
		auto pos = myline.find(' ');
		// Not all commands require parameters
		if ( pos == string::npos )
			pos = myline.length();

		command = myline.substr(0, pos);
		for ( unsigned int i = 0; i < command.size(); ++i )
			command[i] = toupper(command[i]);

		// Adjust for the no-parameter case
		if ( pos == myline.length() )
			pos--;

		myline = myline.substr(pos + 1);
		SkipLeadingWhitespace(myline);
		}

	// Extract parameters.
	string params = myline;

	// special case
	if ( command == "STARTTLS" )
		return;

	// Check for Server2Server - connections with ZIP enabled.
	if ( orig && orig_status == WAIT_FOR_REGISTRATION )
		{
		if ( command == "PASS" )
			{
			vector<string> p = SplitWords(params,' ');
			if ( p.size() > 3 &&
			     (p[3].find('Z')<=p[3].size() ||
			      p[3].find('z')<=p[3].size()) )
				orig_zip_status = ACCEPT_ZIP;
			else
			        orig_zip_status = NO_ZIP;
			}

		// We do not check if SERVER command is successful, since
		// the connection will be terminated by the server if
		// authentication fails.
		//
		// (### This seems not quite prudent to me - VP)
		if ( command == "SERVER" && prefix == "")
			{
			orig_status = REGISTERED;
			}
		}

	if ( ! orig && resp_status == WAIT_FOR_REGISTRATION )
		{
		if ( command == "PASS" )
			{
			vector<string> p = SplitWords(params,' ');
			if ( p.size() > 3 &&
			     (p[3].find('Z')<=p[3].size() ||
			      p[3].find('z')<=p[3].size()) )
			        resp_zip_status = ACCEPT_ZIP;
			else
			        resp_zip_status = NO_ZIP;

			}

		// Again, don't bother checking whether SERVER command
		// is successful.
		if ( command == "SERVER" && prefix == "")
			resp_status = REGISTERED;
		}

	// Analyze server reply messages.
	if ( code > 0 )
		{
		switch ( code ) {
		/*
		case 1: // RPL_WELCOME
		case 2: // RPL_YOURHOST
		case 3: // RPL_CREATED
		case 4: // RPL_MYINFO
		case 5: // RPL_BOUNCE
		case 252: // number of ops online
		case 253: // number of unknown connections
		case 265: // RPL_LOCALUSERS
		case 312: // whois server reply
		case 315: // end of who list
		case 317: // whois idle reply
		case 318: // end of whois list
		case 366: // end of names list
		case 372: // RPL_MOTD
		case 375: // RPL_MOTDSTART
		case 376: // RPL_ENDOFMOTD
		case 331: // RPL_NOTOPIC
			break;
		*/

		// Count of users, services and servers in whole network.
		case 251:
			if ( ! irc_network_info )
				break;

			{
			vector<string> parts = SplitWords(params, ' ');
			int users = 0;
			int services = 0;
			int servers = 0;

			for ( unsigned int i = 1; i < parts.size(); ++i )
				{
				if ( parts[i] == "users" )
					users = atoi(parts[i-1].c_str());
				else if ( parts[i] == "services" )
					services = atoi(parts[i-1].c_str());
				else if ( parts[i] == "servers" )
					servers = atoi(parts[i-1].c_str());
				// else ###
				}

			val_list* vl = new val_list;
			vl->append(BuildConnVal());
			vl->append(new Val(orig, TYPE_BOOL));
			vl->append(new Val(users, TYPE_INT));
			vl->append(new Val(services, TYPE_INT));
			vl->append(new Val(servers, TYPE_INT));

			ConnectionEvent(irc_network_info, vl);
			}
			break;

		// List of users in a channel (names command).
		case 353:
			if ( ! irc_names_info )
				break;

			{
			vector<string> parts = SplitWords(params, ' ');

			if ( parts.size() < 3 )
				{
				Weird("irc_invalid_names_line");
				return;
				}

			// Remove nick name.
			parts.erase(parts.begin());

			string type = parts[0];
			string channel = parts[1];

			// Remove type and channel.
			parts.erase(parts.begin());
			parts.erase(parts.begin());

			if ( parts.size() > 0 && parts[0][0] == ':' )
				parts[0] = parts[0].substr(1);

			val_list* vl = new val_list;
			vl->append(BuildConnVal());
			vl->append(new Val(orig, TYPE_BOOL));
			vl->append(new StringVal(type.c_str()));
			vl->append(new StringVal(channel.c_str()));

			TableVal* set = new TableVal(string_set);
			for ( unsigned int i = 0; i < parts.size(); ++i )
				{
				if ( parts[i][0] == '@' )
					parts[i] = parts[i].substr(1);
				Val* idx = new StringVal(parts[i].c_str());
				set->Assign(idx, 0);
				Unref(idx);
				}
			vl->append(set);

			ConnectionEvent(irc_names_info, vl);
			}
			break;

		// Count of users and services on this server.
		case 255:
			if ( ! irc_server_info )
				break;

			{
			vector<string> parts = SplitWords(params, ' ');
			int users = 0;
			int services = 0;
			int servers = 0;

			for ( unsigned int i = 1; i < parts.size(); ++i )
				{
				if ( parts[i] == "users," )
					users = atoi(parts[i-1].c_str());
				else if ( parts[i] == "clients" )
					users = atoi(parts[i-1].c_str());
				else if ( parts[i] == "services" )
					services = atoi(parts[i-1].c_str());
				else if ( parts[i] == "servers" )
					servers = atoi(parts[i-1].c_str());
				// else ###
				}

			val_list* vl = new val_list;
			vl->append(BuildConnVal());
			vl->append(new Val(orig, TYPE_BOOL));
			vl->append(new Val(users, TYPE_INT));
			vl->append(new Val(services, TYPE_INT));
			vl->append(new Val(servers, TYPE_INT));

			ConnectionEvent(irc_server_info, vl);
			}
			break;

		// Count of channels.
		case 254:
			if ( ! irc_channel_info )
				break;

			{
			vector<string> parts = SplitWords(params, ' ');
			int channels = 0;
			for ( unsigned int i = 1; i < parts.size(); ++i )
				if ( parts[i] == ":channels" )
					channels = atoi(parts[i - 1].c_str());

			val_list* vl = new val_list;
			vl->append(BuildConnVal());
			vl->append(new Val(orig, TYPE_BOOL));
			vl->append(new Val(channels, TYPE_INT));

			ConnectionEvent(irc_channel_info, vl);
			}
			break;

		// RPL_GLOBALUSERS
		case 266:
			{
			// FIXME: We should really streamline all this
			// parsing code ...
			if ( ! irc_global_users )
				break;

			const char* prefix = params.c_str();

			const char* eop = strchr(prefix, ' ');
			if ( ! eop )
				{
				Weird("invalid_irc_global_users_reply");
				break;
				}

			const char *msg = strchr(++eop, ':');
			if ( ! msg )
				{
				Weird("invalid_irc_global_users_reply");
				break;
				}

			val_list* vl = new val_list;
			vl->append(BuildConnVal());
			vl->append(new Val(orig, TYPE_BOOL));
			vl->append(new StringVal(eop - prefix, prefix));
			vl->append(new StringVal(++msg));
			ConnectionEvent(irc_global_users, vl);
			break;
			}

		// WHOIS user reply line.
		case 311:
			if ( ! irc_whois_user_line )
				break;

			{
			vector<string> parts = SplitWords(params, ' ');

			if ( parts.size() > 1 )
				parts.erase(parts.begin());
			if ( parts.size() < 5 )
				{
				Weird("irc_invalid_whois_user_line");
				return;
				}

			val_list* vl = new val_list;
			vl->append(BuildConnVal());
			vl->append(new Val(orig, TYPE_BOOL));
			vl->append(new StringVal(parts[0].c_str()));
			vl->append(new StringVal(parts[1].c_str()));
			vl->append(new StringVal(parts[2].c_str()));

			parts.erase(parts.begin(), parts.begin() + 4);

			string real_name = parts[0];
			for ( unsigned int i = 1; i < parts.size(); ++i )
				real_name = real_name + " " + parts[i];

			if ( real_name[0] == ':' )
				real_name = real_name.substr(1);

			vl->append(new StringVal(real_name.c_str()));

			ConnectionEvent(irc_whois_user_line, vl);
			}
			break;

		// WHOIS operator reply line.
		case 313:
			if ( ! irc_whois_operator_line )
				break;

			{
			vector<string> parts = SplitWords(params, ' ');

			if ( parts.size() > 1 )
				parts.erase(parts.begin());

			if ( parts.size() < 2 )
				{
				Weird("irc_invalid_whois_operator_line");
				return;
				}

			val_list* vl = new val_list;
			vl->append(BuildConnVal());
			vl->append(new Val(orig, TYPE_BOOL));
			vl->append(new StringVal(parts[0].c_str()));

			ConnectionEvent(irc_whois_operator_line, vl);
			}
			break;

		// WHOIS channel reply.
		case 319:
			if ( ! irc_whois_channel_line )
				break;

			{
			vector<string> parts = SplitWords(params, ' ');

			// Remove nick name.
			parts.erase(parts.begin());
			if ( parts.size() < 2 )
				{
				Weird("irc_invalid_whois_channel_line");
				return;
				}

			string nick = parts[0];
			parts.erase(parts.begin());

			if ( parts.size() > 0 && parts[0][0] == ':' )
				parts[0] = parts[0].substr(1);

			val_list* vl = new val_list;
			vl->append(BuildConnVal());
			vl->append(new Val(orig, TYPE_BOOL));
			vl->append(new StringVal(nick.c_str()));
			TableVal* set = new TableVal(string_set);
			for ( unsigned int i = 0; i < parts.size(); ++i )
				{
				Val* idx = new StringVal(parts[i].c_str());
				set->Assign(idx, 0);
				Unref(idx);
				}

			vl->append(set);

			ConnectionEvent(irc_whois_channel_line, vl);
			}
			break;

		// RPL_TOPIC reply.
		case 332:
			{
			if ( ! irc_channel_topic )
				break;

			vector<string> parts = SplitWords(params, ' ');
			if ( parts.size() < 4 )
				{
				Weird("irc_invalid_topic_reply");
				return;
				}

			unsigned int pos = params.find(':');
			if ( pos < params.size() )
				{
				string topic = params.substr(pos + 1);
				val_list* vl = new val_list;

				vl->append(BuildConnVal());
				vl->append(new Val(orig, TYPE_BOOL));
				vl->append(new StringVal(parts[1].c_str()));

				const char* t = topic.c_str();
				if ( *t == ':' )
					++t;

				vl->append(new StringVal(t));

				ConnectionEvent(irc_channel_topic, vl);
				}
			else
				{
				Weird("irc_invalid_topic_reply");
				return;
				}
			break;
			}

		// WHO reply line.
		case 352:
			if ( ! irc_who_line )
				break;

			{
			vector<string> parts = SplitWords(params, ' ');
			if ( parts.size() < 9 )
				{
				Weird("irc_invalid_who_line");
				return;
				}

			val_list* vl = new val_list;
			vl->append(BuildConnVal());
			vl->append(new Val(orig, TYPE_BOOL));
			vl->append(new StringVal(parts[0].c_str()));
			vl->append(new StringVal(parts[1].c_str()));
			if ( parts[2][0] == '~' )
				parts[2] = parts[2].substr(1);
			vl->append(new StringVal(parts[2].c_str()));
			vl->append(new StringVal(parts[3].c_str()));
			vl->append(new StringVal(parts[4].c_str()));
			vl->append(new StringVal(parts[5].c_str()));
			vl->append(new StringVal(parts[6].c_str()));
			if ( parts[7][0] == ':' )
				parts[7] = parts[7].substr(1);
			vl->append(new Val(atoi(parts[7].c_str()), TYPE_INT));
			vl->append(new StringVal(parts[8].c_str()));

			ConnectionEvent(irc_who_line, vl);
			}
			break;

		// Invalid nick name.
		case 431:
		case 432:
		case 433:
		case 436:
			if ( irc_invalid_nick )
				{
				val_list* vl = new val_list;
				vl->append(BuildConnVal());
				vl->append(new Val(orig, TYPE_BOOL));
				ConnectionEvent(irc_invalid_nick, vl);
				}
			break;

		// Operator responses.
		case 381:  // User is operator
		case 491:  // user is not operator
			if ( irc_oper_response )
				{
				val_list* vl = new val_list;
				vl->append(BuildConnVal());
				vl->append(new Val(orig, TYPE_BOOL));
				vl->append(new Val(code == 381, TYPE_BOOL));
				ConnectionEvent(irc_oper_response, vl);
				}
			break;

		case 670:
			// StartTLS success reply to StartTLS
			StartTLS();
			break;

		// All other server replies.
		default:
			val_list* vl = new val_list;
			vl->append(BuildConnVal());
			vl->append(new Val(orig, TYPE_BOOL));
			vl->append(new StringVal(prefix.c_str()));
			vl->append(new Val(code, TYPE_COUNT));
			vl->append(new StringVal(params.c_str()));

			ConnectionEvent(irc_reply, vl);
			break;
		}
		return;
		}

	// Check if command is valid.
	if ( command.size() > 20 )
		{
		Weird("irc_invalid_command");
		if ( ++invalid_msg_count > invalid_msg_max_count )
			{
			Weird("irc_too_many_invalid");
			ProtocolViolation("too many long lines");
			return;
			}
		return;
		}

	else if ( ( irc_privmsg_message || irc_dcc_message ) && command == "PRIVMSG")
		{
		unsigned int pos = params.find(' ');
		if ( pos >= params.size() )
			{
			Weird("irc_invalid_privmsg_message_format");
			return;
			}

		string target = params.substr(0, pos);
		string message = params.substr(pos + 1);
		SkipLeadingWhitespace(message);

		if ( message.size() > 0 && message[0] == ':' )
			message = message.substr(1);
		if ( message.size() > 0 && message[0] == 1 )
			message = message.substr(1); // DCC

		// Check for DCC messages.
		if ( message.size() > 3 && message.substr(0, 3) == "DCC" )
			{
			if ( message.size() > 0 &&
			     message[message.size() - 1] == 1 )
				message = message.substr(0, message.size() - 1);

			vector<string> parts = SplitWords(message, ' ');
			if ( parts.size() < 5 || parts.size() > 6 )
				{
				// Turbo DCC extension appends a "T" at the end of handshake.
				if ( ! (parts.size() == 7 && parts[6] == "T") )
					{
					Weird("irc_invalid_dcc_message_format");
					return;
					}
				}

			// Calculate IP address.
			uint32 raw_ip = 0;
			for ( unsigned int i = 0; i < parts[3].size(); ++i )
				{
				string s = parts[3].substr(i, 1);
				raw_ip = (10 * raw_ip) + atoi(s.c_str());
				}

			val_list* vl = new val_list;
			vl->append(BuildConnVal());
			vl->append(new Val(orig, TYPE_BOOL));
			vl->append(new StringVal(prefix.c_str()));
			vl->append(new StringVal(target.c_str()));
			vl->append(new StringVal(parts[1].c_str()));
			vl->append(new StringVal(parts[2].c_str()));
			vl->append(new AddrVal(htonl(raw_ip)));
			vl->append(new Val(atoi(parts[4].c_str()), TYPE_COUNT));
			if ( parts.size() >= 6 )
				vl->append(new Val(atoi(parts[5].c_str()),
							TYPE_COUNT));
			else
				vl->append(new Val(0, TYPE_COUNT));

			ConnectionEvent(irc_dcc_message, vl);
			}

		else
			{
			val_list* vl = new val_list;
			vl->append(BuildConnVal());
			vl->append(new Val(orig, TYPE_BOOL));
			vl->append(new StringVal(prefix.c_str()));
			vl->append(new StringVal(target.c_str()));
			vl->append(new StringVal(message.c_str()));

			ConnectionEvent(irc_privmsg_message, vl);
			}
		}

	else if ( irc_notice_message && command == "NOTICE" )
		{
		unsigned int pos = params.find(' ');
		if ( pos >= params.size() )
			{
			Weird("irc_invalid_notice_message_format");
			return;
			}

		string target = params.substr(0, pos);
		string message = params.substr(pos + 1);
		SkipLeadingWhitespace(message);
		if ( message[0] == ':' )
			message = message.substr(1);

		val_list* vl = new val_list;
		vl->append(BuildConnVal());
		vl->append(new Val(orig, TYPE_BOOL));
		vl->append(new StringVal(prefix.c_str()));
		vl->append(new StringVal(target.c_str()));
		vl->append(new StringVal(message.c_str()));

		ConnectionEvent(irc_notice_message, vl);
		}

	else if ( irc_squery_message && command == "SQUERY" )
		{
		unsigned int pos = params.find(' ');
		if ( pos >= params.size() )
			{
			Weird("irc_invalid_squery_message_format");
			return;
			}

		string target = params.substr(0, pos);
		string message = params.substr(pos + 1);
		SkipLeadingWhitespace(message);
		if ( message[0] == ':' )
			message = message.substr(1);

		val_list* vl = new val_list;
		vl->append(BuildConnVal());
		vl->append(new Val(orig, TYPE_BOOL));
		vl->append(new StringVal(prefix.c_str()));
		vl->append(new StringVal(target.c_str()));
		vl->append(new StringVal(message.c_str()));

		ConnectionEvent(irc_squery_message, vl);
		}

	else if ( irc_user_message && command == "USER" )
		{
		// extract username and real name
		vector<string> parts = SplitWords(params, ' ');
		val_list* vl = new val_list;
		vl->append(BuildConnVal());
		vl->append(new Val(orig, TYPE_BOOL));

		if ( parts.size() > 0 )
			vl->append(new StringVal(parts[0].c_str()));
		else vl->append(new StringVal(""));

		if ( parts.size() > 1 )
			vl->append(new StringVal(parts[1].c_str()));
		else vl->append(new StringVal(""));

		if ( parts.size() > 2 )
			vl->append(new StringVal(parts[2].c_str()));
		else vl->append(new StringVal(""));

		string realname;
		for ( unsigned int i = 3; i < parts.size(); i++ )
			{
			realname += parts[i];
			if ( i > 3 )
				realname += " ";
			}

		const char* name = realname.c_str();
		vl->append(new StringVal(*name == ':' ? name + 1 : name));

		ConnectionEvent(irc_user_message, vl);
		}

	else if ( irc_oper_message && command == "OPER" )
		{
		// extract username and password
		vector<string> parts = SplitWords(params, ' ');
		if ( parts.size() == 2 )
			{
			val_list* vl = new val_list;
			vl->append(BuildConnVal());
			vl->append(new Val(orig, TYPE_BOOL));
			vl->append(new StringVal(parts[0].c_str()));
			vl->append(new StringVal(parts[1].c_str()));

			ConnectionEvent(irc_oper_message, vl);
			}

		else
			Weird("irc_invalid_oper_message_format");
		}

	else if ( irc_kick_message && command == "KICK" )
		{
		// Extract channels, users and comment.
		vector<string> parts = SplitWords(params, ' ');
		if ( parts.size() <= 1 )
			{
			Weird("irc_invalid_kick_message_format");
			return;
			}

		val_list* vl = new val_list;
		vl->append(BuildConnVal());
		vl->append(new Val(orig, TYPE_BOOL));
		vl->append(new StringVal(prefix.c_str()));
		vl->append(new StringVal(parts[0].c_str()));
		vl->append(new StringVal(parts[1].c_str()));
		if ( parts.size() > 2 )
			{
			string comment = parts[2];
			for ( unsigned int i = 3; i < parts.size(); ++i )
				comment += " " + parts[i];

			if ( comment[0] == ':' )
				comment = comment.substr(1);

			vl->append(new StringVal(comment.c_str()));
			}
		else
			vl->append(new StringVal(""));

		ConnectionEvent(irc_kick_message, vl);
		}

	else if ( irc_join_message && command == "JOIN" )
		{
		if ( params[0] == ':' )
			params = params.substr(1);

		vector<string> parts = SplitWords(params, ' ');

		if ( parts.size() < 1 )
			{
			Weird("irc_invalid_join_line");
			return;
			}

		string nickname = "";
		if ( prefix.size() > 0 )
			{
			unsigned int pos = prefix.find('!');
			if ( pos < prefix.size() )
				nickname = prefix.substr(0, pos);
			}

		val_list* vl = new val_list;
		vl->append(BuildConnVal());
		vl->append(new Val(orig, TYPE_BOOL));

		TableVal* list = new TableVal(irc_join_list);
		vector<string> channels = SplitWords(parts[0], ',');
		vector<string> passwords;

		if ( parts.size() > 1 )
			passwords = SplitWords(parts[1], ',');

		string empty_string = "";
		for ( unsigned int i = 0; i < channels.size(); ++i )
			{
			RecordVal* info = new RecordVal(irc_join_info);
			info->Assign(0, new StringVal(nickname.c_str()));
			info->Assign(1, new StringVal(channels[i].c_str()));
			if ( i < passwords.size() )
				info->Assign(2, new StringVal(passwords[i].c_str()));
			else
				info->Assign(2, new StringVal(empty_string.c_str()));
			// User mode.
			info->Assign(3, new StringVal(empty_string.c_str()));
			list->Assign(info, 0);
			Unref(info);
			}

		vl->append(list);

		ConnectionEvent(irc_join_message, vl);
		}

	else if ( irc_join_message && command == "NJOIN" )
		{
		vector<string> parts = SplitWords(params, ' ');
		if ( parts.size() != 2 )
			{
			Weird("irc_invalid_njoin_line");
			return;
			}

		string channel = parts[0];
		if ( parts[1][0] == ':' )
			parts[1] = parts[1].substr(1);

		vector<string> users = SplitWords(parts[1], ',');

		val_list* vl = new val_list;
		vl->append(BuildConnVal());
		vl->append(new Val(orig, TYPE_BOOL));

		TableVal* list = new TableVal(irc_join_list);
		string empty_string = "";

		for ( unsigned int i = 0; i < users.size(); ++i )
			{
			RecordVal* info = new RecordVal(irc_join_info);
			string nick = users[i];
			string mode = "none";

			if ( nick[0] == '@' )
				{
				if ( nick[1] == '@' )
					{
					nick = nick.substr(2);
					mode = "creator";
					}
				else
					{
					nick = nick.substr(1);
					mode = "operator";
					}
				}

			else if ( nick[0] == '+' )
				{
				nick = nick.substr(1);
				mode = "voice";
				}

			info->Assign(0, new StringVal(nick.c_str()));
			info->Assign(1, new StringVal(channel.c_str()));
			// Password:
			info->Assign(2, new StringVal(empty_string.c_str()));
			// User mode:
			info->Assign(3, new StringVal(mode.c_str()));
			list->Assign(info, 0);
			Unref(info);
			}

		vl->append(list);

		ConnectionEvent(irc_join_message, vl);
		}

	else if ( irc_part_message && command == "PART" )
		{
		string channels = params;
		string message = "";
		unsigned int pos = params.find(' ');

		if ( pos < params.size() )
			{
			channels = params.substr(0, pos);
			if ( params.size() > pos + 1 )
				{
				message = params.substr(pos + 1);
				SkipLeadingWhitespace(message);
				}
			if ( message[0] == ':' )
				message = message.substr(1);
			}

		string nick = prefix;
		pos = nick.find('!');
		if ( pos < nick.size() )
			nick = nick.substr(0, pos);

		vector<string> channelList = SplitWords(channels, ',');
		TableVal* set = new TableVal(string_set);

		for ( unsigned int i = 0; i < channelList.size(); ++i )
			{
			Val* idx = new StringVal(channelList[i].c_str());
			set->Assign(idx, 0);
			Unref(idx);
			}

		val_list* vl = new val_list;
		vl->append(BuildConnVal());
		vl->append(new Val(orig, TYPE_BOOL));
		vl->append(new StringVal(nick.c_str()));
		vl->append(set);
		vl->append(new StringVal(message.c_str()));

		ConnectionEvent(irc_part_message, vl);
		}

	else if ( irc_quit_message && command == "QUIT" )
		{
		string message = params;
		if ( message[0] == ':' )
			message = message.substr(1);

		string nickname = "";
		if ( prefix.size() > 0 )
			{
			unsigned int pos = prefix.find('!');
			if ( pos < prefix.size() )
				nickname = prefix.substr(0, pos);
			}

		val_list* vl = new val_list;
		vl->append(BuildConnVal());
		vl->append(new Val(orig, TYPE_BOOL));
		vl->append(new StringVal(nickname.c_str()));
		vl->append(new StringVal(message.c_str()));

		ConnectionEvent(irc_quit_message, vl);
		}

	else if ( irc_nick_message && command == "NICK" )
		{
		string nick = params;
		if ( nick[0] == ':' )
			nick = nick.substr(1);

		val_list* vl = new val_list;
		vl->append(BuildConnVal());
		vl->append(new Val(orig, TYPE_BOOL));
		vl->append(new StringVal(prefix.c_str()));
		vl->append(new StringVal(nick.c_str()));

		ConnectionEvent(irc_nick_message, vl);
		}

	else if ( irc_who_message && command == "WHO" )
		{
		vector<string> parts = SplitWords(params, ' ');
		if ( parts.size() > 2 )
			{
			Weird("irc_invalid_who_message_format");
			return;
			}

		bool oper = false;
		if ( parts.size() == 2 && parts[1] == "o" )
			oper = true;

		// Remove ":" from mask.
		if ( parts.size() > 0 && parts[0].size() > 0 && parts[0][0] == ':' )
			parts[0] = parts[0].substr(1);

		val_list* vl = new val_list;
		vl->append(BuildConnVal());
		vl->append(new Val(orig, TYPE_BOOL));
		if ( parts.size() > 0 )
			vl->append(new StringVal(parts[0].c_str()));
		else
			vl->append(new StringVal(""));
		vl->append(new Val(oper, TYPE_BOOL));

		ConnectionEvent(irc_who_message, vl);
		}

	else if ( irc_whois_message && command == "WHOIS" )
		{
		vector<string> parts = SplitWords(params, ' ');
		if ( parts.size() < 1 || parts.size() > 2 )
			{
			Weird("irc_invalid_whois_message_format");
			return;
			}

		string server = "";
		string users = "";

		if ( parts.size() == 2 )
			{
			server = parts[0];
			users = parts[1];
			}
		else
			users = parts[0];

		val_list* vl = new val_list;
		vl->append(BuildConnVal());
		vl->append(new Val(orig, TYPE_BOOL));
		vl->append(new StringVal(server.c_str()));
		vl->append(new StringVal(users.c_str()));

		ConnectionEvent(irc_whois_message, vl);
		}

	else if ( irc_error_message && command == "ERROR" )
		{
		val_list* vl = new val_list;
		vl->append(BuildConnVal());
		vl->append(new Val(orig, TYPE_BOOL));
		vl->append(new StringVal(prefix.c_str()));
		if ( params[0] == ':' )
			params = params.substr(1);
		vl->append(new StringVal(params.c_str()));

		ConnectionEvent(irc_error_message, vl);
		}

	else if ( irc_invite_message && command == "INVITE" )
		{
		vector<string> parts = SplitWords(params, ' ');
		if ( parts.size() == 2 )
			{ // remove ":" from channel
			if ( parts[1].size() > 0 && parts[1][0] == ':' )
				parts[1] = parts[1].substr(1);

			val_list* vl = new val_list;
			vl->append(BuildConnVal());
			vl->append(new Val(orig, TYPE_BOOL));
			vl->append(new StringVal(prefix.c_str()));
			vl->append(new StringVal(parts[0].c_str()));
			vl->append(new StringVal(parts[1].c_str()));

			ConnectionEvent(irc_invite_message, vl);
			}
		else
			Weird("irc_invalid_invite_message_format");
		}

	else if ( irc_mode_message && command == "MODE" )
		{
		if ( params.size() > 0 )
			{
			val_list* vl = new val_list;
			vl->append(BuildConnVal());
			vl->append(new Val(orig, TYPE_BOOL));
			vl->append(new StringVal(prefix.c_str()));
			vl->append(new StringVal(params.c_str()));

			ConnectionEvent(irc_mode_message, vl);
			}

		else
			Weird("irc_invalid_mode_message_format");
		}

	else if ( irc_password_message && command == "PASS" )
		{
		val_list* vl = new val_list;
		vl->append(BuildConnVal());
		vl->append(new Val(orig, TYPE_BOOL));
		vl->append(new StringVal(params.c_str()));
		ConnectionEvent(irc_password_message, vl);
		}

	else if ( irc_squit_message && command == "SQUIT" )
		{
		string server = params;
		string message = "";

		unsigned int pos = params.find(' ');
		if ( pos < params.size() )
			{
			server = params.substr(0, pos);
			message = params.substr(pos + 1);
			SkipLeadingWhitespace(message);
			if ( message[0] == ':' )
				message = message.substr(1);
			}

		val_list* vl = new val_list;
		vl->append(BuildConnVal());
		vl->append(new Val(orig, TYPE_BOOL));
		vl->append(new StringVal(prefix.c_str()));
		vl->append(new StringVal(server.c_str()));
		vl->append(new StringVal(message.c_str()));

		ConnectionEvent(irc_squit_message, vl);
		}

	else if ( orig )
		{
		if ( irc_request )
			{
			val_list* vl = new val_list;
			vl->append(BuildConnVal());
			vl->append(new Val(orig, TYPE_BOOL));
			vl->append(new StringVal(prefix.c_str()));
			vl->append(new StringVal(command.c_str()));
			vl->append(new StringVal(params.c_str()));

			ConnectionEvent(irc_request, vl);
			}
		}

	else
		{
		if ( irc_message )
			{
			val_list* vl = new val_list;
			vl->append(BuildConnVal());
			vl->append(new Val(orig, TYPE_BOOL));
			vl->append(new StringVal(prefix.c_str()));
			vl->append(new StringVal(command.c_str()));
			vl->append(new StringVal(params.c_str()));

			ConnectionEvent(irc_message, vl);
			}
		}

	if ( orig_status == REGISTERED && resp_status == REGISTERED &&
	     orig_zip_status == ACCEPT_ZIP && resp_zip_status == ACCEPT_ZIP )
		{
		orig_zip_status = ZIP_LOADED;
		resp_zip_status = ZIP_LOADED;
		AddSupportAnalyzer(new zip::ZIP_Analyzer(Conn(), true));
		AddSupportAnalyzer(new zip::ZIP_Analyzer(Conn(), false));
		}

	return;
	}


net/mlx5: fix memory leak in mlx5_fw_fatal_reporter_dump

In mlx5_fw_fatal_reporter_dump if mlx5_crdump_collect fails the
allocated memory for cr_data must be released otherwise there will be
memory leak. To fix this, this commit changes the return instruction
into goto error handling.

Fixes: 9b1f29823605 ("net/mlx5: Add support for FW fatal reporter dump")
Signed-off-by: Navid Emamdoost <navid.emamdoost@gmail.com>
Signed-off-by: Saeed Mahameed <saeedm@mellanox.com>

mlx5_fw_fatal_reporter_dump(struct devlink_health_reporter *reporter,
			    struct devlink_fmsg *fmsg, void *priv_ctx)
{
	struct mlx5_core_dev *dev = devlink_health_reporter_priv(reporter);
	u32 crdump_size = dev->priv.health.crdump_size;
	u32 *cr_data;
	u32 data_size;
	u32 offset;
	int err;

	if (!mlx5_core_is_pf(dev))
		return -EPERM;

	cr_data = kvmalloc(crdump_size, GFP_KERNEL);
	if (!cr_data)
		return -ENOMEM;
	err = mlx5_crdump_collect(dev, cr_data);
	if (err)
		return err;

	if (priv_ctx) {
		struct mlx5_fw_reporter_ctx *fw_reporter_ctx = priv_ctx;

		err = mlx5_fw_reporter_ctx_pairs_put(fmsg, fw_reporter_ctx);
		if (err)
			goto free_data;
	}

	err = devlink_fmsg_arr_pair_nest_start(fmsg, "crdump_data");
	if (err)
		goto free_data;
	for (offset = 0; offset < crdump_size; offset += data_size) {
		if (crdump_size - offset < MLX5_CR_DUMP_CHUNK_SIZE)
			data_size = crdump_size - offset;
		else
			data_size = MLX5_CR_DUMP_CHUNK_SIZE;
		err = devlink_fmsg_binary_put(fmsg, (char *)cr_data + offset,
					      data_size);
		if (err)
			goto free_data;
	}
	err = devlink_fmsg_arr_pair_nest_end(fmsg);

free_data:
	kvfree(cr_data);
	return err;
}

A memory leak in the mlx5_fw_fatal_reporter_dump() function in drivers/net/ethernet/mellanox/mlx5/core/health.c in the Linux kernel before 5.3.11 allows attackers to cause a denial of service (memory consumption) by triggering mlx5_crdump_collect() failures, aka CID-c7ed6d0183d5.


mlx5_fw_fatal_reporter_dump(struct devlink_health_reporter *reporter,
			    struct devlink_fmsg *fmsg, void *priv_ctx)
{
	struct mlx5_core_dev *dev = devlink_health_reporter_priv(reporter);
	u32 crdump_size = dev->priv.health.crdump_size;
	u32 *cr_data;
	u32 data_size;
	u32 offset;
	int err;

	if (!mlx5_core_is_pf(dev))
		return -EPERM;

	cr_data = kvmalloc(crdump_size, GFP_KERNEL);
	if (!cr_data)
		return -ENOMEM;
	err = mlx5_crdump_collect(dev, cr_data);
	if (err)
		goto free_data;

	if (priv_ctx) {
		struct mlx5_fw_reporter_ctx *fw_reporter_ctx = priv_ctx;

		err = mlx5_fw_reporter_ctx_pairs_put(fmsg, fw_reporter_ctx);
		if (err)
			goto free_data;
	}

	err = devlink_fmsg_arr_pair_nest_start(fmsg, "crdump_data");
	if (err)
		goto free_data;
	for (offset = 0; offset < crdump_size; offset += data_size) {
		if (crdump_size - offset < MLX5_CR_DUMP_CHUNK_SIZE)
			data_size = crdump_size - offset;
		else
			data_size = MLX5_CR_DUMP_CHUNK_SIZE;
		err = devlink_fmsg_binary_put(fmsg, (char *)cr_data + offset,
					      data_size);
		if (err)
			goto free_data;
	}
	err = devlink_fmsg_arr_pair_nest_end(fmsg);

free_data:
	kvfree(cr_data);
	return err;
}


epan: Fix a memory leak.

Make sure _proto_tree_add_bits_ret_val allocates a bits array using the
packet scope, otherwise we leak memory. Fixes #17032.

_proto_tree_add_bits_ret_val(proto_tree *tree, const int hfindex, tvbuff_t *tvb,
			    const guint bit_offset, const gint no_of_bits,
			    guint64 *return_value, const guint encoding)
{
	gint     offset;
	guint    length;
	guint8   tot_no_bits;
	char    *bf_str;
	char     lbl_str[ITEM_LABEL_LENGTH];
	guint64  value = 0;
	guint8  *bytes = NULL;
	size_t bytes_length = 0;

	proto_item        *pi;
	header_field_info *hf_field;

	const true_false_string *tfstring;

	/* We can't fake it just yet. We have to fill in the 'return_value' parameter */
	PROTO_REGISTRAR_GET_NTH(hfindex, hf_field);

	if (hf_field->bitmask != 0) {
		REPORT_DISSECTOR_BUG("Incompatible use of proto_tree_add_bits_ret_val"
				     " with field '%s' (%s) with bitmask != 0",
				     hf_field->abbrev, hf_field->name);
	}

	if (no_of_bits == 0) {
		REPORT_DISSECTOR_BUG("field %s passed to proto_tree_add_bits_ret_val() has a bit width of 0",
				     hf_field->abbrev);
	}

	/* Byte align offset */
	offset = bit_offset>>3;

	/*
	 * Calculate the number of octets used to hold the bits
	 */
	tot_no_bits = ((bit_offset&0x7) + no_of_bits);
	length = (tot_no_bits + 7) >> 3;

	if (no_of_bits < 65) {
		value = tvb_get_bits64(tvb, bit_offset, no_of_bits, encoding);
	} else if (hf_field->type != FT_BYTES) {
		REPORT_DISSECTOR_BUG("field %s passed to proto_tree_add_bits_ret_val() has a bit width of %u > 65",
				     hf_field->abbrev, no_of_bits);
		return NULL;
	}

	/* Sign extend for signed types */
	switch (hf_field->type) {
		case FT_INT8:
		case FT_INT16:
		case FT_INT24:
		case FT_INT32:
		case FT_INT40:
		case FT_INT48:
		case FT_INT56:
		case FT_INT64:
			value = ws_sign_ext64(value, no_of_bits);
			break;

		default:
			break;
	}

	if (return_value) {
		*return_value = value;
	}

	/* Coast clear. Try and fake it */
	CHECK_FOR_NULL_TREE(tree);
	TRY_TO_FAKE_THIS_ITEM(tree, hfindex, hf_field);

	bf_str = decode_bits_in_field(bit_offset, no_of_bits, value);

	switch (hf_field->type) {
	case FT_BOOLEAN:
		/* Boolean field */
		tfstring = &tfs_true_false;
		if (hf_field->strings)
			tfstring = (const true_false_string *)hf_field->strings;
		return proto_tree_add_boolean_format(tree, hfindex, tvb, offset, length, (guint32)value,
			"%s = %s: %s",
			bf_str, hf_field->name, tfs_get_string(!!value, tfstring));
		break;

	case FT_CHAR:
		pi = proto_tree_add_uint(tree, hfindex, tvb, offset, length, (guint32)value);
		fill_label_char(PITEM_FINFO(pi), lbl_str);
		break;

	case FT_UINT8:
	case FT_UINT16:
	case FT_UINT24:
	case FT_UINT32:
		pi = proto_tree_add_uint(tree, hfindex, tvb, offset, length, (guint32)value);
		fill_label_number(PITEM_FINFO(pi), lbl_str, FALSE);
		break;

	case FT_INT8:
	case FT_INT16:
	case FT_INT24:
	case FT_INT32:
		pi = proto_tree_add_int(tree, hfindex, tvb, offset, length, (gint32)value);
		fill_label_number(PITEM_FINFO(pi), lbl_str, TRUE);
		break;

	case FT_UINT40:
	case FT_UINT48:
	case FT_UINT56:
	case FT_UINT64:
		pi = proto_tree_add_uint64(tree, hfindex, tvb, offset, length, value);
		fill_label_number64(PITEM_FINFO(pi), lbl_str, FALSE);
		break;

	case FT_INT40:
	case FT_INT48:
	case FT_INT56:
	case FT_INT64:
		pi = proto_tree_add_int64(tree, hfindex, tvb, offset, length, (gint64)value);
		fill_label_number64(PITEM_FINFO(pi), lbl_str, TRUE);
		break;

	case FT_BYTES:
		bytes = tvb_get_bits_array(NULL, tvb, bit_offset, no_of_bits, &bytes_length);
		pi = proto_tree_add_bytes_with_length(tree, hfindex, tvb, offset, length, bytes, (gint) bytes_length);
		proto_item_fill_label(PITEM_FINFO(pi), lbl_str);
		proto_item_set_text(pi, "%s", lbl_str);
		return pi;
		break;

	default:
		REPORT_DISSECTOR_BUG("field %s has type %d (%s) not handled in proto_tree_add_bits_ret_val()",
				     hf_field->abbrev,
				     hf_field->type,
				     ftype_name(hf_field->type));
		return NULL;
		break;
	}

	proto_item_set_text(pi, "%s = %s", bf_str, lbl_str);
	return pi;
}

Memory leak in the dissection engine in Wireshark 3.4.0 allows denial of service via packet injection or crafted capture file.


_proto_tree_add_bits_ret_val(proto_tree *tree, const int hfindex, tvbuff_t *tvb,
			    const guint bit_offset, const gint no_of_bits,
			    guint64 *return_value, const guint encoding)
{
	gint     offset;
	guint    length;
	guint8   tot_no_bits;
	char    *bf_str;
	char     lbl_str[ITEM_LABEL_LENGTH];
	guint64  value = 0;
	guint8  *bytes = NULL;
	size_t bytes_length = 0;

	proto_item        *pi;
	header_field_info *hf_field;

	const true_false_string *tfstring;

	/* We can't fake it just yet. We have to fill in the 'return_value' parameter */
	PROTO_REGISTRAR_GET_NTH(hfindex, hf_field);

	if (hf_field->bitmask != 0) {
		REPORT_DISSECTOR_BUG("Incompatible use of proto_tree_add_bits_ret_val"
				     " with field '%s' (%s) with bitmask != 0",
				     hf_field->abbrev, hf_field->name);
	}

	if (no_of_bits == 0) {
		REPORT_DISSECTOR_BUG("field %s passed to proto_tree_add_bits_ret_val() has a bit width of 0",
				     hf_field->abbrev);
	}

	/* Byte align offset */
	offset = bit_offset>>3;

	/*
	 * Calculate the number of octets used to hold the bits
	 */
	tot_no_bits = ((bit_offset&0x7) + no_of_bits);
	length = (tot_no_bits + 7) >> 3;

	if (no_of_bits < 65) {
		value = tvb_get_bits64(tvb, bit_offset, no_of_bits, encoding);
	} else if (hf_field->type != FT_BYTES) {
		REPORT_DISSECTOR_BUG("field %s passed to proto_tree_add_bits_ret_val() has a bit width of %u > 65",
				     hf_field->abbrev, no_of_bits);
		return NULL;
	}

	/* Sign extend for signed types */
	switch (hf_field->type) {
		case FT_INT8:
		case FT_INT16:
		case FT_INT24:
		case FT_INT32:
		case FT_INT40:
		case FT_INT48:
		case FT_INT56:
		case FT_INT64:
			value = ws_sign_ext64(value, no_of_bits);
			break;

		default:
			break;
	}

	if (return_value) {
		*return_value = value;
	}

	/* Coast clear. Try and fake it */
	CHECK_FOR_NULL_TREE(tree);
	TRY_TO_FAKE_THIS_ITEM(tree, hfindex, hf_field);

	bf_str = decode_bits_in_field(bit_offset, no_of_bits, value);

	switch (hf_field->type) {
	case FT_BOOLEAN:
		/* Boolean field */
		tfstring = &tfs_true_false;
		if (hf_field->strings)
			tfstring = (const true_false_string *)hf_field->strings;
		return proto_tree_add_boolean_format(tree, hfindex, tvb, offset, length, (guint32)value,
			"%s = %s: %s",
			bf_str, hf_field->name, tfs_get_string(!!value, tfstring));
		break;

	case FT_CHAR:
		pi = proto_tree_add_uint(tree, hfindex, tvb, offset, length, (guint32)value);
		fill_label_char(PITEM_FINFO(pi), lbl_str);
		break;

	case FT_UINT8:
	case FT_UINT16:
	case FT_UINT24:
	case FT_UINT32:
		pi = proto_tree_add_uint(tree, hfindex, tvb, offset, length, (guint32)value);
		fill_label_number(PITEM_FINFO(pi), lbl_str, FALSE);
		break;

	case FT_INT8:
	case FT_INT16:
	case FT_INT24:
	case FT_INT32:
		pi = proto_tree_add_int(tree, hfindex, tvb, offset, length, (gint32)value);
		fill_label_number(PITEM_FINFO(pi), lbl_str, TRUE);
		break;

	case FT_UINT40:
	case FT_UINT48:
	case FT_UINT56:
	case FT_UINT64:
		pi = proto_tree_add_uint64(tree, hfindex, tvb, offset, length, value);
		fill_label_number64(PITEM_FINFO(pi), lbl_str, FALSE);
		break;

	case FT_INT40:
	case FT_INT48:
	case FT_INT56:
	case FT_INT64:
		pi = proto_tree_add_int64(tree, hfindex, tvb, offset, length, (gint64)value);
		fill_label_number64(PITEM_FINFO(pi), lbl_str, TRUE);
		break;

	case FT_BYTES:
		bytes = tvb_get_bits_array(wmem_packet_scope(), tvb, bit_offset, no_of_bits, &bytes_length);
		pi = proto_tree_add_bytes_with_length(tree, hfindex, tvb, offset, length, bytes, (gint) bytes_length);
		proto_item_fill_label(PITEM_FINFO(pi), lbl_str);
		proto_item_set_text(pi, "%s", lbl_str);
		return pi;
		break;

	default:
		REPORT_DISSECTOR_BUG("field %s has type %d (%s) not handled in proto_tree_add_bits_ret_val()",
				     hf_field->abbrev,
				     hf_field->type,
				     ftype_name(hf_field->type));
		return NULL;
		break;
	}

	proto_item_set_text(pi, "%s = %s", bf_str, lbl_str);
	return pi;
}


patch #14211 heap buffer overflow in large ragg2

inputs. this should be refactored to use an RBuffer to enable dynamic
resizing, but for now just patching it to bail out if we are about to
overwrite the allocated statically sized buffer

R_API int r_egg_compile(REgg *egg) {
	r_buf_seek (egg->src, 0, R_BUF_SET);
	char b;
	int r = r_buf_read (egg->src, (ut8 *)&b, sizeof (b));
	if (r != sizeof (b) || !egg->remit) {
		return true;
	}
	// only emit begin if code is found
	r_egg_lang_init (egg);
	for (; b; ) {
		r_egg_lang_parsechar (egg, b);
		int r = r_buf_read (egg->src, (ut8 *)&b, sizeof (b));
		if (r != sizeof (b)) {
			break;
		}
		// XXX: some parse fail errors are false positives :(
	}
	if (egg->context>0) {
		eprintf ("ERROR: expected '}' at the end of the file. %d left\n", egg->context);
		return false;
	}
	// TODO: handle errors here
	return true;
}

In radare2 through 3.5.1, there is a heap-based buffer over-read in the r_egg_lang_parsechar function of egg_lang.c. This allows remote attackers to cause a denial of service (application crash) or possibly have unspecified other impact because of missing length validation in libr/egg/egg.c.


R_API int r_egg_compile(REgg *egg) {
	r_buf_seek (egg->src, 0, R_BUF_SET);
	char b;
	int r = r_buf_read (egg->src, (ut8 *)&b, sizeof (b));
	if (r != sizeof (b) || !egg->remit) {
		return true;
	}
	// only emit begin if code is found
	r_egg_lang_init (egg);
	for (; b; ) {
		r_egg_lang_parsechar (egg, b);
		if (egg->lang.elem_n >= sizeof (egg->lang.elem)) {
			eprintf ("ERROR: elem too large.\n");
			break;
		}
		int r = r_buf_read (egg->src, (ut8 *)&b, sizeof (b));
		if (r != sizeof (b)) {
			break;
		}
		// XXX: some parse fail errors are false positives :(
	}
	if (egg->context>0) {
		eprintf ("ERROR: expected '}' at the end of the file. %d left\n", egg->context);
		return false;
	}
	// TODO: handle errors here
	return true;
}


Check image size when reading targa file

Throw an error when image width or height is 0.

Fixes mozilla/mozjpeg#140, closes #7.

start_input_tga (j_compress_ptr cinfo, cjpeg_source_ptr sinfo)
{
  tga_source_ptr source = (tga_source_ptr) sinfo;
  U_CHAR targaheader[18];
  int idlen, cmaptype, subtype, flags, interlace_type, components;
  unsigned int width, height, maplen;
  boolean is_bottom_up;

#define GET_2B(offset)  ((unsigned int) UCH(targaheader[offset]) + \
                         (((unsigned int) UCH(targaheader[offset+1])) << 8))

  if (! ReadOK(source->pub.input_file, targaheader, 18))
    ERREXIT(cinfo, JERR_INPUT_EOF);

  /* Pretend "15-bit" pixels are 16-bit --- we ignore attribute bit anyway */
  if (targaheader[16] == 15)
    targaheader[16] = 16;

  idlen = UCH(targaheader[0]);
  cmaptype = UCH(targaheader[1]);
  subtype = UCH(targaheader[2]);
  maplen = GET_2B(5);
  width = GET_2B(12);
  height = GET_2B(14);
  source->pixel_size = UCH(targaheader[16]) >> 3;
  flags = UCH(targaheader[17]); /* Image Descriptor byte */

  is_bottom_up = ((flags & 0x20) == 0); /* bit 5 set => top-down */
  interlace_type = flags >> 6;  /* bits 6/7 are interlace code */

  if (cmaptype > 1 ||           /* cmaptype must be 0 or 1 */
      source->pixel_size < 1 || source->pixel_size > 4 ||
      (UCH(targaheader[16]) & 7) != 0 || /* bits/pixel must be multiple of 8 */
      interlace_type != 0)      /* currently don't allow interlaced image */
    ERREXIT(cinfo, JERR_TGA_BADPARMS);

  if (subtype > 8) {
    /* It's an RLE-coded file */
    source->read_pixel = read_rle_pixel;
    source->block_count = source->dup_pixel_count = 0;
    subtype -= 8;
  } else {
    /* Non-RLE file */
    source->read_pixel = read_non_rle_pixel;
  }

  /* Now should have subtype 1, 2, or 3 */
  components = 3;               /* until proven different */
  cinfo->in_color_space = JCS_RGB;

  switch (subtype) {
  case 1:                       /* Colormapped image */
    if (source->pixel_size == 1 && cmaptype == 1)
      source->get_pixel_rows = get_8bit_row;
    else
      ERREXIT(cinfo, JERR_TGA_BADPARMS);
    TRACEMS2(cinfo, 1, JTRC_TGA_MAPPED, width, height);
    break;
  case 2:                       /* RGB image */
    switch (source->pixel_size) {
    case 2:
      source->get_pixel_rows = get_16bit_row;
      break;
    case 3:
      source->get_pixel_rows = get_24bit_row;
      break;
    case 4:
      source->get_pixel_rows = get_32bit_row;
      break;
    default:
      ERREXIT(cinfo, JERR_TGA_BADPARMS);
      break;
    }
    TRACEMS2(cinfo, 1, JTRC_TGA, width, height);
    break;
  case 3:                       /* Grayscale image */
    components = 1;
    cinfo->in_color_space = JCS_GRAYSCALE;
    if (source->pixel_size == 1)
      source->get_pixel_rows = get_8bit_gray_row;
    else
      ERREXIT(cinfo, JERR_TGA_BADPARMS);
    TRACEMS2(cinfo, 1, JTRC_TGA_GRAY, width, height);
    break;
  default:
    ERREXIT(cinfo, JERR_TGA_BADPARMS);
    break;
  }

  if (is_bottom_up) {
    /* Create a virtual array to buffer the upside-down image. */
    source->whole_image = (*cinfo->mem->request_virt_sarray)
      ((j_common_ptr) cinfo, JPOOL_IMAGE, FALSE,
       (JDIMENSION) width * components, (JDIMENSION) height, (JDIMENSION) 1);
    if (cinfo->progress != NULL) {
      cd_progress_ptr progress = (cd_progress_ptr) cinfo->progress;
      progress->total_extra_passes++; /* count file input as separate pass */
    }
    /* source->pub.buffer will point to the virtual array. */
    source->pub.buffer_height = 1; /* in case anyone looks at it */
    source->pub.get_pixel_rows = preload_image;
  } else {
    /* Don't need a virtual array, but do need a one-row input buffer. */
    source->whole_image = NULL;
    source->pub.buffer = (*cinfo->mem->alloc_sarray)
      ((j_common_ptr) cinfo, JPOOL_IMAGE,
       (JDIMENSION) width * components, (JDIMENSION) 1);
    source->pub.buffer_height = 1;
    source->pub.get_pixel_rows = source->get_pixel_rows;
  }

  while (idlen--)               /* Throw away ID field */
    (void) read_byte(source);

  if (maplen > 0) {
    if (maplen > 256 || GET_2B(3) != 0)
      ERREXIT(cinfo, JERR_TGA_BADCMAP);
    /* Allocate space to store the colormap */
    source->colormap = (*cinfo->mem->alloc_sarray)
      ((j_common_ptr) cinfo, JPOOL_IMAGE, (JDIMENSION) maplen, (JDIMENSION) 3);
    /* and read it from the file */
    read_colormap(source, (int) maplen, UCH(targaheader[7]));
  } else {
    if (cmaptype)               /* but you promised a cmap! */
      ERREXIT(cinfo, JERR_TGA_BADPARMS);
    source->colormap = NULL;
  }

  cinfo->input_components = components;
  cinfo->data_precision = 8;
  cinfo->image_width = width;
  cinfo->image_height = height;
}

An issue was discovered in libjpeg 9a and 9d. The alloc_sarray function in jmemmgr.c allows remote attackers to cause a denial of service (divide-by-zero error) via a crafted file.


start_input_tga (j_compress_ptr cinfo, cjpeg_source_ptr sinfo)
{
  tga_source_ptr source = (tga_source_ptr) sinfo;
  U_CHAR targaheader[18];
  int idlen, cmaptype, subtype, flags, interlace_type, components;
  unsigned int width, height, maplen;
  boolean is_bottom_up;

#define GET_2B(offset)  ((unsigned int) UCH(targaheader[offset]) + \
                         (((unsigned int) UCH(targaheader[offset+1])) << 8))

  if (! ReadOK(source->pub.input_file, targaheader, 18))
    ERREXIT(cinfo, JERR_INPUT_EOF);

  /* Pretend "15-bit" pixels are 16-bit --- we ignore attribute bit anyway */
  if (targaheader[16] == 15)
    targaheader[16] = 16;

  idlen = UCH(targaheader[0]);
  cmaptype = UCH(targaheader[1]);
  subtype = UCH(targaheader[2]);
  maplen = GET_2B(5);
  width = GET_2B(12);
  height = GET_2B(14);
  source->pixel_size = UCH(targaheader[16]) >> 3;
  flags = UCH(targaheader[17]); /* Image Descriptor byte */

  is_bottom_up = ((flags & 0x20) == 0); /* bit 5 set => top-down */
  interlace_type = flags >> 6;  /* bits 6/7 are interlace code */

  if (cmaptype > 1 ||           /* cmaptype must be 0 or 1 */
      source->pixel_size < 1 || source->pixel_size > 4 ||
      (UCH(targaheader[16]) & 7) != 0 || /* bits/pixel must be multiple of 8 */
      interlace_type != 0 ||      /* currently don't allow interlaced image */
      width == 0 || height == 0)  /* image width/height must be non-zero */
    ERREXIT(cinfo, JERR_TGA_BADPARMS);

  if (subtype > 8) {
    /* It's an RLE-coded file */
    source->read_pixel = read_rle_pixel;
    source->block_count = source->dup_pixel_count = 0;
    subtype -= 8;
  } else {
    /* Non-RLE file */
    source->read_pixel = read_non_rle_pixel;
  }

  /* Now should have subtype 1, 2, or 3 */
  components = 3;               /* until proven different */
  cinfo->in_color_space = JCS_RGB;

  switch (subtype) {
  case 1:                       /* Colormapped image */
    if (source->pixel_size == 1 && cmaptype == 1)
      source->get_pixel_rows = get_8bit_row;
    else
      ERREXIT(cinfo, JERR_TGA_BADPARMS);
    TRACEMS2(cinfo, 1, JTRC_TGA_MAPPED, width, height);
    break;
  case 2:                       /* RGB image */
    switch (source->pixel_size) {
    case 2:
      source->get_pixel_rows = get_16bit_row;
      break;
    case 3:
      source->get_pixel_rows = get_24bit_row;
      break;
    case 4:
      source->get_pixel_rows = get_32bit_row;
      break;
    default:
      ERREXIT(cinfo, JERR_TGA_BADPARMS);
      break;
    }
    TRACEMS2(cinfo, 1, JTRC_TGA, width, height);
    break;
  case 3:                       /* Grayscale image */
    components = 1;
    cinfo->in_color_space = JCS_GRAYSCALE;
    if (source->pixel_size == 1)
      source->get_pixel_rows = get_8bit_gray_row;
    else
      ERREXIT(cinfo, JERR_TGA_BADPARMS);
    TRACEMS2(cinfo, 1, JTRC_TGA_GRAY, width, height);
    break;
  default:
    ERREXIT(cinfo, JERR_TGA_BADPARMS);
    break;
  }

  if (is_bottom_up) {
    /* Create a virtual array to buffer the upside-down image. */
    source->whole_image = (*cinfo->mem->request_virt_sarray)
      ((j_common_ptr) cinfo, JPOOL_IMAGE, FALSE,
       (JDIMENSION) width * components, (JDIMENSION) height, (JDIMENSION) 1);
    if (cinfo->progress != NULL) {
      cd_progress_ptr progress = (cd_progress_ptr) cinfo->progress;
      progress->total_extra_passes++; /* count file input as separate pass */
    }
    /* source->pub.buffer will point to the virtual array. */
    source->pub.buffer_height = 1; /* in case anyone looks at it */
    source->pub.get_pixel_rows = preload_image;
  } else {
    /* Don't need a virtual array, but do need a one-row input buffer. */
    source->whole_image = NULL;
    source->pub.buffer = (*cinfo->mem->alloc_sarray)
      ((j_common_ptr) cinfo, JPOOL_IMAGE,
       (JDIMENSION) width * components, (JDIMENSION) 1);
    source->pub.buffer_height = 1;
    source->pub.get_pixel_rows = source->get_pixel_rows;
  }

  while (idlen--)               /* Throw away ID field */
    (void) read_byte(source);

  if (maplen > 0) {
    if (maplen > 256 || GET_2B(3) != 0)
      ERREXIT(cinfo, JERR_TGA_BADCMAP);
    /* Allocate space to store the colormap */
    source->colormap = (*cinfo->mem->alloc_sarray)
      ((j_common_ptr) cinfo, JPOOL_IMAGE, (JDIMENSION) maplen, (JDIMENSION) 3);
    /* and read it from the file */
    read_colormap(source, (int) maplen, UCH(targaheader[7]));
  } else {
    if (cmaptype)               /* but you promised a cmap! */
      ERREXIT(cinfo, JERR_TGA_BADPARMS);
    source->colormap = NULL;
  }

  cinfo->input_components = components;
  cinfo->data_precision = 8;
  cinfo->image_width = width;
  cinfo->image_height = height;
}


listdir(): reuse a single buffer to store every file name to display

Allocating a new buffer for each entry is useless.

And as these buffers are allocated on the stack, on systems with a
small stack size, with many entries, the limit can easily be reached,
causing a stack exhaustion and aborting the user session.

Reported by Antonio Morales from the GitHub Security Lab team, thanks!

static void listdir(unsigned int depth, int f, void * const tls_fd,
                    const char *name)
{
    PureFileInfo *dir;
    char *names;
    PureFileInfo *s;
    PureFileInfo *r;
    int d;

    if (depth >= max_ls_depth || matches >= max_ls_files) {
        return;
    }
    if ((dir = sreaddir(&names)) == NULL) {
        addreply(226, MSG_CANT_READ_FILE, name);
        return;
    }
    s = dir;
    while (s->name_offset != (size_t) -1) {
        d = 0;
        if (FI_NAME(s)[0] != '.') {
            d = listfile(s, NULL);
        } else if (opt_a) {
            if (FI_NAME(s)[1] == 0 ||
                (FI_NAME(s)[1] == '.' && FI_NAME(s)[2] == 0)) {
                listfile(s, NULL);
            } else {
                d = listfile(s, NULL);
            }
        }
        if (!d) {
            s->name_offset = (size_t) -1;
        }
        s++;
    }
    outputfiles(f, tls_fd);
    r = dir;
    while (opt_R && r != s) {
        if (r->name_offset != (size_t) -1 && !chdir(FI_NAME(r))) {
            char *alloca_subdir;
            const size_t sizeof_subdir = PATH_MAX + 1U;

            if ((alloca_subdir = ALLOCA(sizeof_subdir)) == NULL) {
                goto toomany;
            }
            if (SNCHECK(snprintf(alloca_subdir, sizeof_subdir, "%s/%s",
                                 name, FI_NAME(r)), sizeof_subdir)) {
                goto nolist;
            }
            wrstr(f, tls_fd, "\r\n\r\n");
            wrstr(f, tls_fd, alloca_subdir);
            wrstr(f, tls_fd, ":\r\n\r\n");
            listdir(depth + 1U, f, tls_fd, alloca_subdir);
            nolist:
            ALLOCA_FREE(alloca_subdir);
            if (matches >= max_ls_files) {
                goto toomany;
            }
            if (chdir("..")) {    /* defensive in the extreme... */
                if (chdir(wd) || chdir(name)) {    /* someone rmdir()'d it? */
                    die(421, LOG_ERR, "chdir: %s", strerror(errno));
                }
            }
        }
        r++;
    }
    toomany:
    free(names);
    free(dir);
    names = NULL;
}

In Pure-FTPd 1.0.49, a stack exhaustion issue was discovered in the listdir function in ls.c.


static void listdir(unsigned int depth, int f, void * const tls_fd,
                    const char *name)
{
    PureFileInfo *dir;
    char *names;
    PureFileInfo *s;
    PureFileInfo *r;
    char *alloca_subdir;
    size_t sizeof_subdir;
    int d;

    if (depth >= max_ls_depth || matches >= max_ls_files) {
        return;
    }
    if ((dir = sreaddir(&names)) == NULL) {
        addreply(226, MSG_CANT_READ_FILE, name);
        return;
    }
    s = dir;
    while (s->name_offset != (size_t) -1) {
        d = 0;
        if (FI_NAME(s)[0] != '.') {
            d = listfile(s, NULL);
        } else if (opt_a) {
            if (FI_NAME(s)[1] == 0 ||
                (FI_NAME(s)[1] == '.' && FI_NAME(s)[2] == 0)) {
                listfile(s, NULL);
            } else {
                d = listfile(s, NULL);
            }
        }
        if (!d) {
            s->name_offset = (size_t) -1;
        }
        s++;
    }
    outputfiles(f, tls_fd);
    r = dir;
    sizeof_subdir = PATH_MAX + 1U;
    if ((alloca_subdir = ALLOCA(sizeof_subdir)) == NULL) {
        goto toomany;
    }
    while (opt_R && r != s) {
        if (r->name_offset != (size_t) -1 && !chdir(FI_NAME(r))) {
            if (SNCHECK(snprintf(alloca_subdir, sizeof_subdir, "%s/%s",
                                 name, FI_NAME(r)), sizeof_subdir)) {
                goto nolist;
            }
            wrstr(f, tls_fd, "\r\n\r\n");
            wrstr(f, tls_fd, alloca_subdir);
            wrstr(f, tls_fd, ":\r\n\r\n");
            listdir(depth + 1U, f, tls_fd, alloca_subdir);

            nolist:
            if (matches >= max_ls_files) {
                goto toomany;
            }
            if (chdir("..")) {    /* defensive in the extreme... */
                if (chdir(wd) || chdir(name)) {    /* someone rmdir()'d it? */
                    die(421, LOG_ERR, "chdir: %s", strerror(errno));
                }
            }
        }
        r++;
    }
    toomany:
    ALLOCA_FREE(alloca_subdir);
    free(names);
    free(dir);
    names = NULL;
}


ALSA: usb-audio: Fix a stack buffer overflow bug in check_input_term

`check_input_term` recursively calls itself with input from
device side (e.g., uac_input_terminal_descriptor.bCSourceID)
as argument (id). In `check_input_term`, if `check_input_term`
is called with the same `id` argument as the caller, it triggers
endless recursive call, resulting kernel space stack overflow.

This patch fixes the bug by adding a bitmap to `struct mixer_build`
to keep track of the checked ids and stop the execution if some id
has been checked (similar to how parse_audio_unit handles unitid
argument).

Reported-by: Hui Peng <benquike@gmail.com>
Reported-by: Mathias Payer <mathias.payer@nebelwelt.net>
Signed-off-by: Hui Peng <benquike@gmail.com>
Cc: <stable@vger.kernel.org>
Signed-off-by: Takashi Iwai <tiwai@suse.de>

static int check_input_term(struct mixer_build *state, int id,
			    struct usb_audio_term *term)
{
	int protocol = state->mixer->protocol;
	int err;
	void *p1;

	memset(term, 0, sizeof(*term));
	while ((p1 = find_audio_control_unit(state, id)) != NULL) {
		unsigned char *hdr = p1;
		term->id = id;

		if (protocol == UAC_VERSION_1 || protocol == UAC_VERSION_2) {
			switch (hdr[2]) {
			case UAC_INPUT_TERMINAL:
				if (protocol == UAC_VERSION_1) {
					struct uac_input_terminal_descriptor *d = p1;

					term->type = le16_to_cpu(d->wTerminalType);
					term->channels = d->bNrChannels;
					term->chconfig = le16_to_cpu(d->wChannelConfig);
					term->name = d->iTerminal;
				} else { /* UAC_VERSION_2 */
					struct uac2_input_terminal_descriptor *d = p1;

					/* call recursively to verify that the
					 * referenced clock entity is valid */
					err = check_input_term(state, d->bCSourceID, term);
					if (err < 0)
						return err;

					/* save input term properties after recursion,
					 * to ensure they are not overriden by the
					 * recursion calls */
					term->id = id;
					term->type = le16_to_cpu(d->wTerminalType);
					term->channels = d->bNrChannels;
					term->chconfig = le32_to_cpu(d->bmChannelConfig);
					term->name = d->iTerminal;
				}
				return 0;
			case UAC_FEATURE_UNIT: {
				/* the header is the same for v1 and v2 */
				struct uac_feature_unit_descriptor *d = p1;

				id = d->bSourceID;
				break; /* continue to parse */
			}
			case UAC_MIXER_UNIT: {
				struct uac_mixer_unit_descriptor *d = p1;

				term->type = UAC3_MIXER_UNIT << 16; /* virtual type */
				term->channels = uac_mixer_unit_bNrChannels(d);
				term->chconfig = uac_mixer_unit_wChannelConfig(d, protocol);
				term->name = uac_mixer_unit_iMixer(d);
				return 0;
			}
			case UAC_SELECTOR_UNIT:
			case UAC2_CLOCK_SELECTOR: {
				struct uac_selector_unit_descriptor *d = p1;
				/* call recursively to retrieve the channel info */
				err = check_input_term(state, d->baSourceID[0], term);
				if (err < 0)
					return err;
				term->type = UAC3_SELECTOR_UNIT << 16; /* virtual type */
				term->id = id;
				term->name = uac_selector_unit_iSelector(d);
				return 0;
			}
			case UAC1_PROCESSING_UNIT:
			/* UAC2_EFFECT_UNIT */
				if (protocol == UAC_VERSION_1)
					term->type = UAC3_PROCESSING_UNIT << 16; /* virtual type */
				else /* UAC_VERSION_2 */
					term->type = UAC3_EFFECT_UNIT << 16; /* virtual type */
				/* fall through */
			case UAC1_EXTENSION_UNIT:
			/* UAC2_PROCESSING_UNIT_V2 */
				if (protocol == UAC_VERSION_1 && !term->type)
					term->type = UAC3_EXTENSION_UNIT << 16; /* virtual type */
				else if (protocol == UAC_VERSION_2 && !term->type)
					term->type = UAC3_PROCESSING_UNIT << 16; /* virtual type */
				/* fall through */
			case UAC2_EXTENSION_UNIT_V2: {
				struct uac_processing_unit_descriptor *d = p1;

				if (protocol == UAC_VERSION_2 &&
					hdr[2] == UAC2_EFFECT_UNIT) {
					/* UAC2/UAC1 unit IDs overlap here in an
					 * uncompatible way. Ignore this unit for now.
					 */
					return 0;
				}

				if (d->bNrInPins) {
					id = d->baSourceID[0];
					break; /* continue to parse */
				}
				if (!term->type)
					term->type = UAC3_EXTENSION_UNIT << 16; /* virtual type */

				term->channels = uac_processing_unit_bNrChannels(d);
				term->chconfig = uac_processing_unit_wChannelConfig(d, protocol);
				term->name = uac_processing_unit_iProcessing(d, protocol);
				return 0;
			}
			case UAC2_CLOCK_SOURCE: {
				struct uac_clock_source_descriptor *d = p1;

				term->type = UAC3_CLOCK_SOURCE << 16; /* virtual type */
				term->id = id;
				term->name = d->iClockSource;
				return 0;
			}
			default:
				return -ENODEV;
			}
		} else { /* UAC_VERSION_3 */
			switch (hdr[2]) {
			case UAC_INPUT_TERMINAL: {
				struct uac3_input_terminal_descriptor *d = p1;

				/* call recursively to verify that the
				 * referenced clock entity is valid */
				err = check_input_term(state, d->bCSourceID, term);
				if (err < 0)
					return err;

				/* save input term properties after recursion,
				 * to ensure they are not overriden by the
				 * recursion calls */
				term->id = id;
				term->type = le16_to_cpu(d->wTerminalType);

				err = get_cluster_channels_v3(state, le16_to_cpu(d->wClusterDescrID));
				if (err < 0)
					return err;
				term->channels = err;

				/* REVISIT: UAC3 IT doesn't have channels cfg */
				term->chconfig = 0;

				term->name = le16_to_cpu(d->wTerminalDescrStr);
				return 0;
			}
			case UAC3_FEATURE_UNIT: {
				struct uac3_feature_unit_descriptor *d = p1;

				id = d->bSourceID;
				break; /* continue to parse */
			}
			case UAC3_CLOCK_SOURCE: {
				struct uac3_clock_source_descriptor *d = p1;

				term->type = UAC3_CLOCK_SOURCE << 16; /* virtual type */
				term->id = id;
				term->name = le16_to_cpu(d->wClockSourceStr);
				return 0;
			}
			case UAC3_MIXER_UNIT: {
				struct uac_mixer_unit_descriptor *d = p1;

				err = uac_mixer_unit_get_channels(state, d);
				if (err <= 0)
					return err;

				term->channels = err;
				term->type = UAC3_MIXER_UNIT << 16; /* virtual type */

				return 0;
			}
			case UAC3_SELECTOR_UNIT:
			case UAC3_CLOCK_SELECTOR: {
				struct uac_selector_unit_descriptor *d = p1;
				/* call recursively to retrieve the channel info */
				err = check_input_term(state, d->baSourceID[0], term);
				if (err < 0)
					return err;
				term->type = UAC3_SELECTOR_UNIT << 16; /* virtual type */
				term->id = id;
				term->name = 0; /* TODO: UAC3 Class-specific strings */

				return 0;
			}
			case UAC3_PROCESSING_UNIT: {
				struct uac_processing_unit_descriptor *d = p1;

				if (!d->bNrInPins)
					return -EINVAL;

				/* call recursively to retrieve the channel info */
				err = check_input_term(state, d->baSourceID[0], term);
				if (err < 0)
					return err;

				term->type = UAC3_PROCESSING_UNIT << 16; /* virtual type */
				term->id = id;
				term->name = 0; /* TODO: UAC3 Class-specific strings */

				return 0;
			}
			default:
				return -ENODEV;
			}
		}
	}
	return -ENODEV;
}

check_input_term in sound/usb/mixer.c in the Linux kernel through 5.2.9 mishandles recursion, leading to kernel stack exhaustion.


static int __check_input_term(struct mixer_build *state, int id,
			    struct usb_audio_term *term)
{
	int protocol = state->mixer->protocol;
	int err;
	void *p1;
	unsigned char *hdr;

	memset(term, 0, sizeof(*term));
	for (;;) {
		/* a loop in the terminal chain? */
		if (test_and_set_bit(id, state->termbitmap))
			return -EINVAL;

		p1 = find_audio_control_unit(state, id);
		if (!p1)
			break;

		hdr = p1;
		term->id = id;

		if (protocol == UAC_VERSION_1 || protocol == UAC_VERSION_2) {
			switch (hdr[2]) {
			case UAC_INPUT_TERMINAL:
				if (protocol == UAC_VERSION_1) {
					struct uac_input_terminal_descriptor *d = p1;

					term->type = le16_to_cpu(d->wTerminalType);
					term->channels = d->bNrChannels;
					term->chconfig = le16_to_cpu(d->wChannelConfig);
					term->name = d->iTerminal;
				} else { /* UAC_VERSION_2 */
					struct uac2_input_terminal_descriptor *d = p1;

					/* call recursively to verify that the
					 * referenced clock entity is valid */
					err = __check_input_term(state, d->bCSourceID, term);
					if (err < 0)
						return err;

					/* save input term properties after recursion,
					 * to ensure they are not overriden by the
					 * recursion calls */
					term->id = id;
					term->type = le16_to_cpu(d->wTerminalType);
					term->channels = d->bNrChannels;
					term->chconfig = le32_to_cpu(d->bmChannelConfig);
					term->name = d->iTerminal;
				}
				return 0;
			case UAC_FEATURE_UNIT: {
				/* the header is the same for v1 and v2 */
				struct uac_feature_unit_descriptor *d = p1;

				id = d->bSourceID;
				break; /* continue to parse */
			}
			case UAC_MIXER_UNIT: {
				struct uac_mixer_unit_descriptor *d = p1;

				term->type = UAC3_MIXER_UNIT << 16; /* virtual type */
				term->channels = uac_mixer_unit_bNrChannels(d);
				term->chconfig = uac_mixer_unit_wChannelConfig(d, protocol);
				term->name = uac_mixer_unit_iMixer(d);
				return 0;
			}
			case UAC_SELECTOR_UNIT:
			case UAC2_CLOCK_SELECTOR: {
				struct uac_selector_unit_descriptor *d = p1;
				/* call recursively to retrieve the channel info */
				err = __check_input_term(state, d->baSourceID[0], term);
				if (err < 0)
					return err;
				term->type = UAC3_SELECTOR_UNIT << 16; /* virtual type */
				term->id = id;
				term->name = uac_selector_unit_iSelector(d);
				return 0;
			}
			case UAC1_PROCESSING_UNIT:
			/* UAC2_EFFECT_UNIT */
				if (protocol == UAC_VERSION_1)
					term->type = UAC3_PROCESSING_UNIT << 16; /* virtual type */
				else /* UAC_VERSION_2 */
					term->type = UAC3_EFFECT_UNIT << 16; /* virtual type */
				/* fall through */
			case UAC1_EXTENSION_UNIT:
			/* UAC2_PROCESSING_UNIT_V2 */
				if (protocol == UAC_VERSION_1 && !term->type)
					term->type = UAC3_EXTENSION_UNIT << 16; /* virtual type */
				else if (protocol == UAC_VERSION_2 && !term->type)
					term->type = UAC3_PROCESSING_UNIT << 16; /* virtual type */
				/* fall through */
			case UAC2_EXTENSION_UNIT_V2: {
				struct uac_processing_unit_descriptor *d = p1;

				if (protocol == UAC_VERSION_2 &&
					hdr[2] == UAC2_EFFECT_UNIT) {
					/* UAC2/UAC1 unit IDs overlap here in an
					 * uncompatible way. Ignore this unit for now.
					 */
					return 0;
				}

				if (d->bNrInPins) {
					id = d->baSourceID[0];
					break; /* continue to parse */
				}
				if (!term->type)
					term->type = UAC3_EXTENSION_UNIT << 16; /* virtual type */

				term->channels = uac_processing_unit_bNrChannels(d);
				term->chconfig = uac_processing_unit_wChannelConfig(d, protocol);
				term->name = uac_processing_unit_iProcessing(d, protocol);
				return 0;
			}
			case UAC2_CLOCK_SOURCE: {
				struct uac_clock_source_descriptor *d = p1;

				term->type = UAC3_CLOCK_SOURCE << 16; /* virtual type */
				term->id = id;
				term->name = d->iClockSource;
				return 0;
			}
			default:
				return -ENODEV;
			}
		} else { /* UAC_VERSION_3 */
			switch (hdr[2]) {
			case UAC_INPUT_TERMINAL: {
				struct uac3_input_terminal_descriptor *d = p1;

				/* call recursively to verify that the
				 * referenced clock entity is valid */
				err = __check_input_term(state, d->bCSourceID, term);
				if (err < 0)
					return err;

				/* save input term properties after recursion,
				 * to ensure they are not overriden by the
				 * recursion calls */
				term->id = id;
				term->type = le16_to_cpu(d->wTerminalType);

				err = get_cluster_channels_v3(state, le16_to_cpu(d->wClusterDescrID));
				if (err < 0)
					return err;
				term->channels = err;

				/* REVISIT: UAC3 IT doesn't have channels cfg */
				term->chconfig = 0;

				term->name = le16_to_cpu(d->wTerminalDescrStr);
				return 0;
			}
			case UAC3_FEATURE_UNIT: {
				struct uac3_feature_unit_descriptor *d = p1;

				id = d->bSourceID;
				break; /* continue to parse */
			}
			case UAC3_CLOCK_SOURCE: {
				struct uac3_clock_source_descriptor *d = p1;

				term->type = UAC3_CLOCK_SOURCE << 16; /* virtual type */
				term->id = id;
				term->name = le16_to_cpu(d->wClockSourceStr);
				return 0;
			}
			case UAC3_MIXER_UNIT: {
				struct uac_mixer_unit_descriptor *d = p1;

				err = uac_mixer_unit_get_channels(state, d);
				if (err <= 0)
					return err;

				term->channels = err;
				term->type = UAC3_MIXER_UNIT << 16; /* virtual type */

				return 0;
			}
			case UAC3_SELECTOR_UNIT:
			case UAC3_CLOCK_SELECTOR: {
				struct uac_selector_unit_descriptor *d = p1;
				/* call recursively to retrieve the channel info */
				err = __check_input_term(state, d->baSourceID[0], term);
				if (err < 0)
					return err;
				term->type = UAC3_SELECTOR_UNIT << 16; /* virtual type */
				term->id = id;
				term->name = 0; /* TODO: UAC3 Class-specific strings */

				return 0;
			}
			case UAC3_PROCESSING_UNIT: {
				struct uac_processing_unit_descriptor *d = p1;

				if (!d->bNrInPins)
					return -EINVAL;

				/* call recursively to retrieve the channel info */
				err = __check_input_term(state, d->baSourceID[0], term);
				if (err < 0)
					return err;

				term->type = UAC3_PROCESSING_UNIT << 16; /* virtual type */
				term->id = id;
				term->name = 0; /* TODO: UAC3 Class-specific strings */

				return 0;
			}
			default:
				return -ENODEV;
			}
		}
	}
	return -ENODEV;
}


rtlwifi: prevent memory leak in rtl_usb_probe

In rtl_usb_probe if allocation for usb_data fails the allocated hw
should be released. In addition the allocated rtlpriv->usb_data should
be released on error handling path.

Signed-off-by: Navid Emamdoost <navid.emamdoost@gmail.com>
Signed-off-by: Kalle Valo <kvalo@codeaurora.org>

int rtl_usb_probe(struct usb_interface *intf,
		  const struct usb_device_id *id,
		  struct rtl_hal_cfg *rtl_hal_cfg)
{
	int err;
	struct ieee80211_hw *hw = NULL;
	struct rtl_priv *rtlpriv = NULL;
	struct usb_device	*udev;
	struct rtl_usb_priv *usb_priv;

	hw = ieee80211_alloc_hw(sizeof(struct rtl_priv) +
				sizeof(struct rtl_usb_priv), &rtl_ops);
	if (!hw) {
		WARN_ONCE(true, "rtl_usb: ieee80211 alloc failed\n");
		return -ENOMEM;
	}
	rtlpriv = hw->priv;
	rtlpriv->hw = hw;
	rtlpriv->usb_data = kcalloc(RTL_USB_MAX_RX_COUNT, sizeof(u32),
				    GFP_KERNEL);
	if (!rtlpriv->usb_data)
		return -ENOMEM;

	/* this spin lock must be initialized early */
	spin_lock_init(&rtlpriv->locks.usb_lock);
	INIT_WORK(&rtlpriv->works.fill_h2c_cmd,
		  rtl_fill_h2c_cmd_work_callback);
	INIT_WORK(&rtlpriv->works.lps_change_work,
		  rtl_lps_change_work_callback);

	rtlpriv->usb_data_index = 0;
	init_completion(&rtlpriv->firmware_loading_complete);
	SET_IEEE80211_DEV(hw, &intf->dev);
	udev = interface_to_usbdev(intf);
	usb_get_dev(udev);
	usb_priv = rtl_usbpriv(hw);
	memset(usb_priv, 0, sizeof(*usb_priv));
	usb_priv->dev.intf = intf;
	usb_priv->dev.udev = udev;
	usb_set_intfdata(intf, hw);
	/* init cfg & intf_ops */
	rtlpriv->rtlhal.interface = INTF_USB;
	rtlpriv->cfg = rtl_hal_cfg;
	rtlpriv->intf_ops = &rtl_usb_ops;
	/* Init IO handler */
	_rtl_usb_io_handler_init(&udev->dev, hw);
	rtlpriv->cfg->ops->read_chip_version(hw);
	/*like read eeprom and so on */
	rtlpriv->cfg->ops->read_eeprom_info(hw);
	err = _rtl_usb_init(hw);
	if (err)
		goto error_out2;
	rtl_usb_init_sw(hw);
	/* Init mac80211 sw */
	err = rtl_init_core(hw);
	if (err) {
		pr_err("Can't allocate sw for mac80211\n");
		goto error_out2;
	}
	if (rtlpriv->cfg->ops->init_sw_vars(hw)) {
		pr_err("Can't init_sw_vars\n");
		goto error_out;
	}
	rtlpriv->cfg->ops->init_sw_leds(hw);

	err = ieee80211_register_hw(hw);
	if (err) {
		pr_err("Can't register mac80211 hw.\n");
		err = -ENODEV;
		goto error_out;
	}
	rtlpriv->mac80211.mac80211_registered = 1;

	set_bit(RTL_STATUS_INTERFACE_START, &rtlpriv->status);
	return 0;

error_out:
	rtl_deinit_core(hw);
error_out2:
	_rtl_usb_io_handler_release(hw);
	usb_put_dev(udev);
	complete(&rtlpriv->firmware_loading_complete);
	return -ENODEV;
}

Two memory leaks in the rtl_usb_probe() function in drivers/net/wireless/realtek/rtlwifi/usb.c in the Linux kernel through 5.3.11 allow attackers to cause a denial of service (memory consumption), aka CID-3f9361695113.