Commit message: Fix sending malformed pong messages in some cases.
CVE: CVE-2021-33586
CWE: CWE-200
Vulnerability description: InspIRCd 3.8.0 through 3.9.x before 3.10.0 allows any user (able to connect to the server) to access recently deallocated memory, aka the "malformed PONG" issue.

Project: inspircd
File: clientprotocolmsg.h

	Topic(User* source, const Channel* chan, const std::string& newtopic)
		: ClientProtocol::Message("TOPIC", source)
	{
		PushParamRef(chan->name);
		PushParamRef(newtopic);
	}

Project: inspircd
File: clientprotocolmsg.h

	Privmsg(NoCopy, const std::string& source, const std::string& target, const std::string& text, MessageType mt = MSG_PRIVMSG)
		: ClientProtocol::Message(CommandStrFromMsgType(mt), source)
	{
		PushParam(target);
		PushParamRef(text);
	}

Project: inspircd
File: clientprotocolmsg.h

	Pong(const std::string& cookie, const std::string& server = "")
		: ClientProtocol::Message("PONG", ServerInstance->Config->GetServerName())
	{
		if (server.empty())
			PushParamRef(ServerInstance->Config->GetServerName());
		else
			PushParam(server);
		PushParamRef(cookie);
	}

Project: inspircd
File: core_user.cpp

void MessageWrapper::Wrap(const std::string& message, std::string& out)
{
	// If there is a fixed message, it is stored in prefix. Otherwise prefix contains
	// only the prefix, so append the message and the suffix
	out.assign(prefix);
	if (!fixed)
		out.append(message).append(suffix);
}

Project: inspircd
File: clientprotocolmsg.h

	Privmsg(const std::string& source, const std::string& target, const std::string& text, MessageType mt = MSG_PRIVMSG, char status = 0)
		: ClientProtocol::Message(CommandStrFromMsgType(mt), source)
	{
		if (status)
		{
			std::string rawtarget(1, status);
			rawtarget.append(target);
			PushParam(rawtarget);
		}
		else
		{
			PushParam(target);
		}
		PushParam(text);
	}

Project: inspircd
File: core_user.cpp

	CoreModUser()
		: cmdaway(this)
		, cmdnick(this)
		, cmdpart(this)
		, cmdpass(this)
		, cmdping(this)
		, cmdpong(this)
		, cmdquit(this)
		, cmduser(this)
		, cmdison(this)
		, cmduserhost(this)
		, invisiblemode(this, "invisible", 'i')
		, operatormode(this)
		, snomaskmode(this)
	{
	}

Project: inspircd
File: clientprotocolmsg.h

	Privmsg(const std::string& source, const Channel* targetchan, const std::string& text, MessageType mt = MSG_PRIVMSG, char status = 0)
		: ClientProtocol::Message(CommandStrFromMsgType(mt), source)
	{
		PushTargetChan(status, targetchan);
		PushParam(text);
	}

Project: inspircd
File: clientprotocolmsg.h

	Numeric(const ::Numeric::Numeric& num, User* user)
		: ClientProtocol::Message(NULL, (num.GetServer() ? num.GetServer() : ServerInstance->FakeClient->server)->GetName())
	{
		if (user->registered & REG_NICK)
			PushParamRef(user->nick);
		else
			PushParam("*");
		InitFromNumeric(num);
	}

Project: inspircd
File: clientprotocolmsg.h

	Join(Membership* Memb)
		: ClientProtocol::Message("JOIN", Memb->user)
	{
		SetParams(Memb);
	}

Project: inspircd
File: clientprotocolmsg.h

	Privmsg(NoCopy, User* source, const Channel* targetchan, const std::string& text, MessageType mt = MSG_PRIVMSG, char status = 0)
		: ClientProtocol::Message(CommandStrFromMsgType(mt), source)
	{
		PushTargetChan(status, targetchan);
		PushParamRef(text);
	}

Project: inspircd
File: clientprotocolmsg.h

	Privmsg(User* source, const Channel* targetchan, const std::string& text, MessageType mt = MSG_PRIVMSG, char status = 0)
		: ClientProtocol::Message(CommandStrFromMsgType(mt), source)
	{
		PushTargetChan(status, targetchan);
		PushParam(text);
	}

Project: inspircd
File: core_user.cpp

void MessageWrapper::ReadConfig(const char* prefixname, const char* suffixname, const char* fixedname)
{
	ConfigTag* tag = ServerInstance->Config->ConfValue("options");
	prefix = tag->getString(fixedname);
	fixed = (!prefix.empty());
	if (!fixed)
	{
		prefix = tag->getString(prefixname);
		suffix = tag->getString(suffixname);
	}
}

Project: inspircd
File: clientprotocolmsg.h

	Quit(User* source, const std::string& reason)
		: ClientProtocol::Message("QUIT", source)
	{
		if (!reason.empty())
			PushParamRef(reason);
	}

Project: inspircd
File: clientprotocolmsg.h

	Join(Membership* Memb, const std::string& sourcestrref)
		: ClientProtocol::Message("JOIN", sourcestrref, Memb->user)
	{
		SetParams(Memb);
	}

Project: inspircd
File: clientprotocolmsg.h

	Modes::ChangeList::List::const_iterator	GetBeginIterator() const { return beginit; }

Project: inspircd
File: clientprotocolmsg.h

	void SetParams(Channel* Chantarget, User* Usertarget, const Modes::ChangeList& changelist)
	{
		ClearParams();

		chantarget = Chantarget;
		usertarget = Usertarget;
		beginit = changelist.getlist().begin();

		PushParamRef(GetStrTarget());
		PushParam(ToModeLetters(changelist.getlist(), 450, beginit, lastit));
		PushModeParams();
	}

Project: inspircd
File: clientprotocolmsg.h

	Mode()
		: ClientProtocol::Message("MODE", ServerInstance->FakeClient)
		, chantarget(NULL)
		, usertarget(NULL)
	{
	}

Project: inspircd
File: clientprotocolmsg.h

	Privmsg(User* source, const std::string& target, const std::string& text, MessageType mt = MSG_PRIVMSG)
		: ClientProtocol::Message(CommandStrFromMsgType(mt), source)
	{
		PushParam(target);
		PushParam(text);
	}

Project: inspircd
File: clientprotocolmsg.h

	Invite(User* source, User* target, Channel* chan)
		: ClientProtocol::Message("INVITE", source)
	{
		PushParamRef(target->nick);
		PushParamRef(chan->name);
	}

Project: inspircd
File: clientprotocolmsg.h

	void PushModeParams()
	{
		for (Modes::ChangeList::List::const_iterator i = beginit; i != lastit; ++i)
		{
			const Modes::Change& item = *i;
			if (!item.param.empty())
				PushParamRef(item.param);
		}
	}

Project: inspircd
File: clientprotocolmsg.h

	const std::string& GetStrTarget() const { return (chantarget ? chantarget->name : usertarget->nick); }

Project: inspircd
File: clientprotocolmsg.h

	Channel* GetChanTarget() const { return chantarget; }

Project: inspircd
File: clientprotocolmsg.h

	Privmsg(User* source, const User* targetuser, const std::string& text, MessageType mt = MSG_PRIVMSG)
		: ClientProtocol::Message(CommandStrFromMsgType(mt), source)
	{
		PushTargetUser(targetuser);
		PushParam(text);
	}

Project: inspircd
File: clientprotocolmsg.h

	Error(const std::string& text)
			: ClientProtocol::Message("ERROR")
	{
		PushParam(text);
	}

Project: inspircd
File: clientprotocolmsg.h

	void PushTargetChan(char status, const Channel* targetchan)
	{
		if (status)
		{
			std::string rawtarget(1, status);
			rawtarget.append(targetchan->name);
			PushParam(rawtarget);
		}
		else
		{
			PushParamRef(targetchan->name);
		}
	}

Project: inspircd
File: clientprotocolmsg.h

	static std::string ToModeLetters(const Modes::ChangeList& changelist)
	{
		// TODO: This assumes that std::string::max_size() >= UINT_MAX
		Modes::ChangeList::List::const_iterator dummy;
		return ToModeLetters(changelist.getlist(), UINT_MAX, changelist.getlist().begin(), dummy);
	}

Project: inspircd
File: clientprotocolmsg.h

	Nick(User* source, const std::string& newnick)
		: ClientProtocol::Message("NICK", source)
	{
		PushParamRef(newnick);
	}

Project: inspircd
File: clientprotocolmsg.h

	Part(Membership* memb, const std::string& reason)
		: ClientProtocol::Message("PART", memb->user)
	{
		PushParamRef(memb->chan->name);
		if (!reason.empty())
			PushParamRef(reason);
	}

Project: inspircd
File: clientprotocolmsg.h

	static const char* CommandStrFromMsgType(MessageType mt)
	{
		return ((mt == MSG_PRIVMSG) ? "PRIVMSG" : "NOTICE");
	}

Project: inspircd
File: clientprotocolmsg.h

	static std::string ToModeLetters(const Modes::ChangeList::List& list, std::string::size_type maxlinelen, Modes::ChangeList::List::const_iterator beginit, Modes::ChangeList::List::const_iterator& lastit)
	{
		std::string ret;
		std::string::size_type paramlength = 0;
		char output_pm = '\0'; // current output state, '+' or '-'

		Modes::ChangeList::List::const_iterator i;
		for (i = beginit; i != list.end(); ++i)
		{
			const Modes::Change& item = *i;

			const char needed_pm = (item.adding ? '+' : '-');
			if (needed_pm != output_pm)
			{
				output_pm = needed_pm;
				ret.push_back(output_pm);
			}

			if (!item.param.empty())
				paramlength += item.param.length() + 1;
			if (ret.length() + 1 + paramlength > maxlinelen)
			{
				// Mode sequence is getting too long
				const char c = *ret.rbegin();
				if ((c == '+') || (c == '-'))
					ret.erase(ret.size()-1);
				break;
			}

			ret.push_back(item.mh->GetModeChar());
		}

		lastit = i;
		return ret;
	}

Project: inspircd
File: clientprotocolmsg.h

	Membership* GetMember() const { return memb; }

Project: inspircd
File: clientprotocolmsg.h

	void PushTargetUser(const User* targetuser)
	{
		if (targetuser->registered & REG_NICK)
			PushParamRef(targetuser->nick);
		else
			PushParam("*");
	}

Project: inspircd
File: clientprotocolmsg.h

	Ping()
		: ClientProtocol::Message("PING")
	{
		PushParamRef(ServerInstance->Config->GetServerName());
	}

Project: inspircd
File: clientprotocolmsg.h

	Mode(User* source, Channel* Chantarget, User* Usertarget, const Modes::ChangeList& changelist, Modes::ChangeList::List::const_iterator beginiter)
		: ClientProtocol::Message("MODE", source)
		, chantarget(Chantarget)
		, usertarget(Usertarget)
		, beginit(beginiter)
	{
		PushParamRef(GetStrTarget());
		PushParam(ToModeLetters(changelist.getlist(), 450, beginit, lastit));
		PushModeParams();
	}

Project: inspircd
File: clientprotocolmsg.h

	Join()
		: ClientProtocol::Message("JOIN")
		, memb(NULL)
	{
	}

Project: inspircd
File: core_user.cpp

	CmdResult Handle(User* user, const Params& parameters) CXX11_OVERRIDE
	{
		size_t origin = parameters.size() > 1 ? 1 : 0;
		if (parameters[origin].empty())
		{
			user->WriteNumeric(ERR_NOORIGIN, "No origin specified");
			return CMD_FAILURE;
		}

		// set the user as alive so they survive to next ping
		LocalUser* localuser = IS_LOCAL(user);
		if (localuser)
		{
			// Increase penalty unless we've sent a PING and this is the reply
			if (localuser->lastping)
				localuser->CommandFloodPenalty += 1000;
			else
				localuser->lastping = 1;
		}
		return CMD_SUCCESS;
	}

Project: inspircd
File: core_user.cpp

	CommandPass(Module* parent)
		: SplitCommand(parent, "PASS", 1, 1)
	{
		works_before_reg = true;
		Penalty = 0;
		syntax = "<password>";
	}

Project: inspircd
File: core_user.cpp

	CmdResult HandleLocal(LocalUser* user, const Params& parameters) CXX11_OVERRIDE
	{
		size_t origin = parameters.size() > 1 ? 1 : 0;
		if (parameters[origin].empty())
		{
			user->WriteNumeric(ERR_NOORIGIN, "No origin specified");
			return CMD_FAILURE;
		}

		ClientProtocol::Messages::Pong pong(parameters[0], origin ? parameters[1] : ServerInstance->Config->GetServerName());
		user->Send(ServerInstance->GetRFCEvents().pong, pong);
		return CMD_SUCCESS;
	}

Project: inspircd
File: core_user.cpp

	CmdResult HandleLocal(LocalUser* user, const Params& parameters) CXX11_OVERRIDE
	{
		// Check to make sure they haven't registered -- Fix by FCS
		if (user->registered == REG_ALL)
		{
			user->CommandFloodPenalty += 1000;
			user->WriteNumeric(ERR_ALREADYREGISTERED, "You may not reregister");
			return CMD_FAILURE;
		}
		user->password = parameters[0];

		return CMD_SUCCESS;
	}

Project: inspircd
File: clientprotocolmsg.h

	User* GetUserTarget() const { return usertarget; }

Project: inspircd
File: clientprotocolmsg.h

	void InitCommand(unsigned int number)
	{
		snprintf(numericstr, sizeof(numericstr), "%03u", number);
		SetCommand(numericstr);
	}

Project: inspircd
File: clientprotocolmsg.h

	Privmsg(NoCopy, User* source, const User* targetuser, const std::string& text, MessageType mt = MSG_PRIVMSG)
		: ClientProtocol::Message(CommandStrFromMsgType(mt), source)
	{
		PushTargetUser(targetuser);
		PushParamRef(text);
	}

Project: inspircd
File: core_user.cpp

	void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
	{
		cmdpart.msgwrap.ReadConfig("prefixpart", "suffixpart", "fixedpart");
		cmdquit.msgwrap.ReadConfig("prefixquit", "suffixquit", "fixedquit");
	}

Project: inspircd
File: clientprotocolmsg.h

	Numeric(const ::Numeric::Numeric& num, const std::string& target)
		: ClientProtocol::Message(NULL, (num.GetServer() ? num.GetServer() : ServerInstance->FakeClient->server)->GetName())
	{
		PushParamRef(target);
		InitFromNumeric(num);
	}

Project: inspircd
File: clientprotocolmsg.h

	Ping(const std::string& cookie)
		: ClientProtocol::Message("PING")
	{
		PushParamRef(cookie);
	}

Project: inspircd
File: core_user.cpp

	CommandPong(Module* parent)
		: Command(parent, "PONG", 1)
	{
		Penalty = 0;
		syntax = "<cookie> [<servername>]";
	}

Project: inspircd
File: clientprotocolmsg.h

	Privmsg(NoCopy, User* source, const std::string& target, const std::string& text, MessageType mt = MSG_PRIVMSG)
		: ClientProtocol::Message(CommandStrFromMsgType(mt), source)
	{
		PushParam(target);
		PushParamRef(text);
	}

Project: inspircd
File: clientprotocolmsg.h

	Privmsg(const std::string& source, const User* targetuser, const std::string& text, MessageType mt = MSG_PRIVMSG)
		: ClientProtocol::Message(CommandStrFromMsgType(mt), source)
	{
		PushTargetUser(targetuser);
		PushParam(text);
	}

Project: inspircd
File: clientprotocolmsg.h

	void InitFromNumeric(const ::Numeric::Numeric& numeric)
	{
		InitCommand(numeric.GetNumeric());
		for (std::vector<std::string>::const_iterator i = numeric.GetParams().begin(); i != numeric.GetParams().end(); ++i)
			PushParamRef(*i);
	}

Project: inspircd
File: clientprotocolmsg.h

	Mode(User* source, Channel* Chantarget, User* Usertarget, const Modes::ChangeList& changelist)
		: ClientProtocol::Message("MODE", source)
		, chantarget(Chantarget)
		, usertarget(Usertarget)
		, beginit(changelist.getlist().begin())
	{
		PushParamRef(GetStrTarget());
		PushParam(ToModeLetters(changelist.getlist(), 450, beginit, lastit));
		PushModeParams();
	}

Project: inspircd
File: clientprotocolmsg.h

	Modes::ChangeList::List::const_iterator GetEndIterator() const { return lastit; }

Project: inspircd
File: clientprotocolmsg.h

	Numeric(unsigned int num)
		: ClientProtocol::Message(NULL, ServerInstance->Config->GetServerName())
	{
		InitCommand(num);
		PushParam("*");
	}

Project: inspircd
File: core_user.cpp

	Version GetVersion() CXX11_OVERRIDE
	{
		return Version("Provides the AWAY, ISON, NICK, PART, PASS, PING, PONG, QUIT, USERHOST, and USER commands", VF_VENDOR|VF_CORE);
	}

Project: inspircd
File: clientprotocolmsg.h

	void SetParams(Membership* Memb)
	{
		memb = Memb;
		PushParamRef(memb->chan->name);
	}

Project: inspircd
File: clientprotocolmsg.h

	Kick(User* source, Membership* memb, const std::string& reason)
		: ClientProtocol::Message("KICK", source)
	{
		PushParamRef(memb->chan->name);
		PushParamRef(memb->user->nick);
		PushParamRef(reason);
	}

Project: inspircd
File: clientprotocolmsg.h

	Privmsg(NoCopy, const std::string& source, const User* targetuser, const std::string& text, MessageType mt = MSG_PRIVMSG)
		: ClientProtocol::Message(CommandStrFromMsgType(mt), source)
	{
		PushTargetUser(targetuser);
		PushParamRef(text);
	}

Project: inspircd
File: core_user.cpp

	CommandPing(Module* parent)
		: SplitCommand(parent, "PING", 1)
	{
		syntax = "<cookie> [<servername>]";
	}

Project: inspircd
File: clientprotocolmsg.h

	Privmsg(NoCopy, const std::string& source, const Channel* targetchan, const std::string& text, MessageType mt = MSG_PRIVMSG, char status = 0)
		: ClientProtocol::Message(CommandStrFromMsgType(mt), source)
	{
		PushTargetChan(status, targetchan);
		PushParamRef(text);
	}