Index: ipc/ipc_fuzzing_tests.cc |
diff --git a/ipc/ipc_fuzzing_tests.cc b/ipc/ipc_fuzzing_tests.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..c79d05a176cd366f02276c2157113807a96f0fe1 |
--- /dev/null |
+++ b/ipc/ipc_fuzzing_tests.cc |
@@ -0,0 +1,430 @@ |
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+#include <stdio.h> |
+#include <iostream> |
+#include <string> |
+#include <sstream> |
+ |
+#include "base/message_loop.h" |
+#include "base/platform_thread.h" |
+#include "base/process_util.h" |
+#include "ipc/ipc_channel.h" |
+#include "ipc/ipc_channel_proxy.h" |
+#include "ipc/ipc_message_utils.h" |
+#include "ipc/ipc_tests.h" |
+#include "testing/gtest/include/gtest/gtest.h" |
+#include "testing/multiprocess_func_list.h" |
+ |
+TEST(IPCMessageIntegrity, ReadBeyondBufferStr) { |
+ //This was BUG 984408. |
+ uint32 v1 = kuint32max - 1; |
+ int v2 = 666; |
+ IPC::Message m(0, 1, IPC::Message::PRIORITY_NORMAL); |
+ EXPECT_TRUE(m.WriteInt(v1)); |
+ EXPECT_TRUE(m.WriteInt(v2)); |
+ |
+ void* iter = NULL; |
+ std::string vs; |
+ EXPECT_FALSE(m.ReadString(&iter, &vs)); |
+} |
+ |
+TEST(IPCMessageIntegrity, ReadBeyondBufferWStr) { |
+ //This was BUG 984408. |
+ uint32 v1 = kuint32max - 1; |
+ int v2 = 777; |
+ IPC::Message m(0, 1, IPC::Message::PRIORITY_NORMAL); |
+ EXPECT_TRUE(m.WriteInt(v1)); |
+ EXPECT_TRUE(m.WriteInt(v2)); |
+ |
+ void* iter = NULL; |
+ std::wstring vs; |
+ EXPECT_FALSE(m.ReadWString(&iter, &vs)); |
+} |
+ |
+TEST(IPCMessageIntegrity, ReadBytesBadIterator) { |
+ // This was BUG 1035467. |
+ IPC::Message m(0, 1, IPC::Message::PRIORITY_NORMAL); |
+ EXPECT_TRUE(m.WriteInt(1)); |
+ EXPECT_TRUE(m.WriteInt(2)); |
+ |
+ void* iter = NULL; |
+ const char* data = NULL; |
+ EXPECT_FALSE(m.ReadBytes(&iter, &data, sizeof(int))); |
+} |
+ |
+TEST(IPCMessageIntegrity, ReadVectorNegativeSize) { |
+ // A slight variation of BUG 984408. Note that the pickling of vector<char> |
+ // has a specialized template which is not vulnerable to this bug. So here |
+ // try to hit the non-specialized case vector<P>. |
+ IPC::Message m(0, 1, IPC::Message::PRIORITY_NORMAL); |
+ EXPECT_TRUE(m.WriteInt(-1)); // This is the count of elements. |
+ EXPECT_TRUE(m.WriteInt(1)); |
+ EXPECT_TRUE(m.WriteInt(2)); |
+ EXPECT_TRUE(m.WriteInt(3)); |
+ |
+ std::vector<double> vec; |
+ void* iter = 0; |
+ EXPECT_FALSE(ReadParam(&m, &iter, &vec)); |
+} |
+ |
+TEST(IPCMessageIntegrity, ReadVectorTooLarge1) { |
+ // This was BUG 1006367. This is the large but positive length case. Again |
+ // we try to hit the non-specialized case vector<P>. |
+ IPC::Message m(0, 1, IPC::Message::PRIORITY_NORMAL); |
+ EXPECT_TRUE(m.WriteInt(0x21000003)); // This is the count of elements. |
+ EXPECT_TRUE(m.WriteInt64(1)); |
+ EXPECT_TRUE(m.WriteInt64(2)); |
+ |
+ std::vector<int64> vec; |
+ void* iter = 0; |
+ EXPECT_FALSE(ReadParam(&m, &iter, &vec)); |
+} |
+ |
+TEST(IPCMessageIntegrity, ReadVectorTooLarge2) { |
+ // This was BUG 1006367. This is the large but positive with an additional |
+ // integer overflow when computing the actual byte size. Again we try to hit |
+ // the non-specialized case vector<P>. |
+ IPC::Message m(0, 1, IPC::Message::PRIORITY_NORMAL); |
+ EXPECT_TRUE(m.WriteInt(0x71000000)); // This is the count of elements. |
+ EXPECT_TRUE(m.WriteInt64(1)); |
+ EXPECT_TRUE(m.WriteInt64(2)); |
+ |
+ std::vector<int64> vec; |
+ void* iter = 0; |
+ EXPECT_FALSE(ReadParam(&m, &iter, &vec)); |
+} |
+ |
+// We don't actually use the messages defined in this file, but we do this |
+// to get to the IPC macros. |
+#define MESSAGES_INTERNAL_FILE "ipc/ipc_sync_message_unittest.h" |
+#include "ipc/ipc_message_macros.h" |
+ |
+enum IPCMessageIds { |
+ UNUSED_IPC_TYPE, |
+ SERVER_FIRST_IPC_TYPE, // 1st Test message tag. |
+ SERVER_SECOND_IPC_TYPE, // 2nd Test message tag. |
+ SERVER_THIRD_IPC_TYPE, // 3rd Test message tag. |
+ CLIENT_MALFORMED_IPC, // Sent to client if server detects bad message. |
+ CLIENT_UNHANDLED_IPC // Sent to client if server detects unhanded IPC. |
+}; |
+ |
+// Generic message class that is an int followed by a wstring. |
+class MsgClassIS : public IPC::MessageWithTuple< Tuple2<int, std::wstring> > { |
+ public: |
+ enum { ID = SERVER_FIRST_IPC_TYPE }; |
+ MsgClassIS(const int& arg1, const std::wstring& arg2) |
+ : IPC::MessageWithTuple< Tuple2<int, std::wstring> >( |
+ MSG_ROUTING_CONTROL, ID, MakeRefTuple(arg1, arg2)) {} |
+}; |
+ |
+// Generic message class that is a wstring followed by an int. |
+class MsgClassSI : public IPC::MessageWithTuple< Tuple2<std::wstring, int> > { |
+ public: |
+ enum { ID = SERVER_SECOND_IPC_TYPE }; |
+ MsgClassSI(const std::wstring& arg1, const int& arg2) |
+ : IPC::MessageWithTuple< Tuple2<std::wstring, int> >( |
+ MSG_ROUTING_CONTROL, ID, MakeRefTuple(arg1, arg2)) {} |
+}; |
+ |
+// Message to create a mutex in the IPC server, using the received name. |
+class MsgDoMutex : public IPC::MessageWithTuple< Tuple2<std::wstring, int> > { |
+ public: |
+ enum { ID = SERVER_THIRD_IPC_TYPE }; |
+ MsgDoMutex(const std::wstring& mutex_name, const int& unused) |
+ : IPC::MessageWithTuple< Tuple2<std::wstring, int> >( |
+ MSG_ROUTING_CONTROL, ID, MakeRefTuple(mutex_name, unused)) {} |
+}; |
+ |
+class SimpleListener : public IPC::Channel::Listener { |
+ public: |
+ SimpleListener() : other_(NULL) { |
+ } |
+ void Init(IPC::Message::Sender* s) { |
+ other_ = s; |
+ } |
+ protected: |
+ IPC::Message::Sender* other_; |
+}; |
+ |
+enum { |
+ FUZZER_ROUTING_ID = 5 |
+}; |
+ |
+// The fuzzer server class. It runs in a child process and expects |
+// only two IPC calls; after that it exits the message loop which |
+// terminates the child process. |
+class FuzzerServerListener : public SimpleListener { |
+ public: |
+ FuzzerServerListener() : message_count_(2), pending_messages_(0) { |
+ } |
+ virtual void OnMessageReceived(const IPC::Message& msg) { |
+ if (msg.routing_id() == MSG_ROUTING_CONTROL) { |
+ ++pending_messages_; |
+ IPC_BEGIN_MESSAGE_MAP(FuzzerServerListener, msg) |
+ IPC_MESSAGE_HANDLER(MsgClassIS, OnMsgClassISMessage) |
+ IPC_MESSAGE_HANDLER(MsgClassSI, OnMsgClassSIMessage) |
+ IPC_END_MESSAGE_MAP() |
+ if (pending_messages_) { |
+ // Probably a problem de-serializing the message. |
+ ReplyMsgNotHandled(msg.type()); |
+ } |
+ } |
+ } |
+ |
+ private: |
+ void OnMsgClassISMessage(int value, const std::wstring& text) { |
+ UseData(MsgClassIS::ID, value, text); |
+ RoundtripAckReply(FUZZER_ROUTING_ID, MsgClassIS::ID, value); |
+ Cleanup(); |
+ } |
+ |
+ void OnMsgClassSIMessage(const std::wstring& text, int value) { |
+ UseData(MsgClassSI::ID, value, text); |
+ RoundtripAckReply(FUZZER_ROUTING_ID, MsgClassSI::ID, value); |
+ Cleanup(); |
+ } |
+ |
+ bool RoundtripAckReply(int routing, int type_id, int reply) { |
+ IPC::Message* message = new IPC::Message(routing, type_id, |
+ IPC::Message::PRIORITY_NORMAL); |
+ message->WriteInt(reply + 1); |
+ message->WriteInt(reply); |
+ return other_->Send(message); |
+ } |
+ |
+ void Cleanup() { |
+ --message_count_; |
+ --pending_messages_; |
+ if (0 == message_count_) |
+ MessageLoop::current()->Quit(); |
+ } |
+ |
+ void ReplyMsgNotHandled(int type_id) { |
+ RoundtripAckReply(FUZZER_ROUTING_ID, CLIENT_UNHANDLED_IPC, type_id); |
+ Cleanup(); |
+ } |
+ |
+ void UseData(int caller, int value, const std::wstring& text) { |
+ std::wostringstream wos; |
+ wos << L"IPC fuzzer:" << caller << " [" << value << L" " << text << L"]\n"; |
+ std::wstring output = wos.str(); |
+ LOG(WARNING) << output.c_str(); |
+ }; |
+ |
+ int message_count_; |
+ int pending_messages_; |
+}; |
+ |
+class FuzzerClientListener : public SimpleListener { |
+ public: |
+ FuzzerClientListener() : last_msg_(NULL) { |
+ } |
+ |
+ virtual void OnMessageReceived(const IPC::Message& msg) { |
+ last_msg_ = new IPC::Message(msg); |
+ MessageLoop::current()->Quit(); |
+ } |
+ |
+ bool ExpectMessage(int value, int type_id) { |
+ if (!MsgHandlerInternal(type_id)) |
+ return false; |
+ int msg_value1 = 0; |
+ int msg_value2 = 0; |
+ void* iter = NULL; |
+ if (!last_msg_->ReadInt(&iter, &msg_value1)) |
+ return false; |
+ if (!last_msg_->ReadInt(&iter, &msg_value2)) |
+ return false; |
+ if ((msg_value2 + 1) != msg_value1) |
+ return false; |
+ if (msg_value2 != value) |
+ return false; |
+ |
+ delete last_msg_; |
+ last_msg_ = NULL; |
+ return true; |
+ } |
+ |
+ bool ExpectMsgNotHandled(int type_id) { |
+ return ExpectMessage(type_id, CLIENT_UNHANDLED_IPC); |
+ } |
+ |
+ private: |
+ bool MsgHandlerInternal(int type_id) { |
+ MessageLoop::current()->Run(); |
+ if (NULL == last_msg_) |
+ return false; |
+ if (FUZZER_ROUTING_ID != last_msg_->routing_id()) |
+ return false; |
+ return (type_id == last_msg_->type()); |
+ }; |
+ |
+ IPC::Message* last_msg_; |
+}; |
+ |
+// Runs the fuzzing server child mode. Returns when the preset number |
+// of messages have been received. |
+MULTIPROCESS_TEST_MAIN(RunFuzzServer) { |
+ MessageLoopForIO main_message_loop; |
+ FuzzerServerListener listener; |
+ IPC::Channel chan(kFuzzerChannel, IPC::Channel::MODE_CLIENT, &listener); |
+ chan.Connect(); |
+ listener.Init(&chan); |
+ MessageLoop::current()->Run(); |
+ return 0; |
+} |
+ |
+class IPCFuzzingTest : public IPCChannelTest { |
+}; |
+ |
+// This test makes sure that the FuzzerClientListener and FuzzerServerListener |
+// are working properly by generating two well formed IPC calls. |
+TEST_F(IPCFuzzingTest, SanityTest) { |
+ FuzzerClientListener listener; |
+ IPC::Channel chan(kFuzzerChannel, IPC::Channel::MODE_SERVER, |
+ &listener); |
+ base::ProcessHandle server_process = SpawnChild(FUZZER_SERVER, &chan); |
+ ASSERT_TRUE(server_process); |
+ PlatformThread::Sleep(1000); |
+ ASSERT_TRUE(chan.Connect()); |
+ listener.Init(&chan); |
+ |
+ IPC::Message* msg = NULL; |
+ int value = 43; |
+ msg = new MsgClassIS(value, L"expect 43"); |
+ chan.Send(msg); |
+ EXPECT_TRUE(listener.ExpectMessage(value, MsgClassIS::ID)); |
+ |
+ msg = new MsgClassSI(L"expect 44", ++value); |
+ chan.Send(msg); |
+ EXPECT_TRUE(listener.ExpectMessage(value, MsgClassSI::ID)); |
+ |
+ EXPECT_TRUE(base::WaitForSingleProcess(server_process, 5000)); |
+ base::CloseProcessHandle(server_process); |
+} |
+ |
+// This test uses a payload that is smaller than expected. |
+// This generates an error while unpacking the IPC buffer which in |
+// In debug this triggers an assertion and in release it is ignored(!!). Right |
+// after we generate another valid IPC to make sure framing is working |
+// properly. |
+#ifdef NDEBUG |
+TEST_F(IPCFuzzingTest, MsgBadPayloadShort) { |
+ FuzzerClientListener listener; |
+ IPC::Channel chan(kFuzzerChannel, IPC::Channel::MODE_SERVER, |
+ &listener); |
+ base::ProcessHandle server_process = SpawnChild(FUZZER_SERVER, &chan); |
+ ASSERT_TRUE(server_process); |
+ PlatformThread::Sleep(1000); |
+ ASSERT_TRUE(chan.Connect()); |
+ listener.Init(&chan); |
+ |
+ IPC::Message* msg = new IPC::Message(MSG_ROUTING_CONTROL, MsgClassIS::ID, |
+ IPC::Message::PRIORITY_NORMAL); |
+ msg->WriteInt(666); |
+ chan.Send(msg); |
+ EXPECT_TRUE(listener.ExpectMsgNotHandled(MsgClassIS::ID)); |
+ |
+ msg = new MsgClassSI(L"expect one", 1); |
+ chan.Send(msg); |
+ EXPECT_TRUE(listener.ExpectMessage(1, MsgClassSI::ID)); |
+ |
+ EXPECT_TRUE(base::WaitForSingleProcess(server_process, 5000)); |
+ base::CloseProcessHandle(server_process); |
+} |
+#endif // NDEBUG |
+ |
+// This test uses a payload that has too many arguments, but so the payload |
+// size is big enough so the unpacking routine does not generate an error as |
+// in the case of MsgBadPayloadShort test. |
+// This test does not pinpoint a flaw (per se) as by design we don't carry |
+// type information on the IPC message. |
+TEST_F(IPCFuzzingTest, MsgBadPayloadArgs) { |
+ FuzzerClientListener listener; |
+ IPC::Channel chan(kFuzzerChannel, IPC::Channel::MODE_SERVER, |
+ &listener); |
+ base::ProcessHandle server_process = SpawnChild(FUZZER_SERVER, &chan); |
+ ASSERT_TRUE(server_process); |
+ PlatformThread::Sleep(1000); |
+ ASSERT_TRUE(chan.Connect()); |
+ listener.Init(&chan); |
+ |
+ IPC::Message* msg = new IPC::Message(MSG_ROUTING_CONTROL, MsgClassSI::ID, |
+ IPC::Message::PRIORITY_NORMAL); |
+ msg->WriteWString(L"d"); |
+ msg->WriteInt(0); |
+ msg->WriteInt(0x65); // Extra argument. |
+ |
+ chan.Send(msg); |
+ EXPECT_TRUE(listener.ExpectMessage(0, MsgClassSI::ID)); |
+ |
+ // Now send a well formed message to make sure the receiver wasn't |
+ // thrown out of sync by the extra argument. |
+ msg = new MsgClassIS(3, L"expect three"); |
+ chan.Send(msg); |
+ EXPECT_TRUE(listener.ExpectMessage(3, MsgClassIS::ID)); |
+ |
+ EXPECT_TRUE(base::WaitForSingleProcess(server_process, 5000)); |
+ base::CloseProcessHandle(server_process); |
+} |
+ |
+// This class is for testing the IPC_BEGIN_MESSAGE_MAP_EX macros. |
+class ServerMacroExTest { |
+ public: |
+ ServerMacroExTest() : unhandled_msgs_(0) { |
+ } |
+ virtual bool OnMessageReceived(const IPC::Message& msg) { |
+ bool msg_is_ok = false; |
+ IPC_BEGIN_MESSAGE_MAP_EX(ServerMacroExTest, msg, msg_is_ok) |
+ IPC_MESSAGE_HANDLER(MsgClassIS, OnMsgClassISMessage) |
+ IPC_MESSAGE_HANDLER(MsgClassSI, OnMsgClassSIMessage) |
+ IPC_MESSAGE_UNHANDLED(++unhandled_msgs_) |
+ IPC_END_MESSAGE_MAP_EX() |
+ return msg_is_ok; |
+ } |
+ |
+ int unhandled_msgs() const { |
+ return unhandled_msgs_; |
+ } |
+ |
+ private: |
+ void OnMsgClassISMessage(int value, const std::wstring& text) { |
+ } |
+ void OnMsgClassSIMessage(const std::wstring& text, int value) { |
+ } |
+ |
+ int unhandled_msgs_; |
+}; |
+ |
+TEST_F(IPCFuzzingTest, MsgMapExMacro) { |
+ IPC::Message* msg = NULL; |
+ ServerMacroExTest server; |
+ |
+ // Test the regular messages. |
+ msg = new MsgClassIS(3, L"text3"); |
+ EXPECT_TRUE(server.OnMessageReceived(*msg)); |
+ delete msg; |
+ msg = new MsgClassSI(L"text2", 2); |
+ EXPECT_TRUE(server.OnMessageReceived(*msg)); |
+ delete msg; |
+ |
+#ifdef NDEBUG |
+ // Test a bad message. |
+ msg = new IPC::Message(MSG_ROUTING_CONTROL, MsgClassSI::ID, |
+ IPC::Message::PRIORITY_NORMAL); |
+ msg->WriteInt(2); |
+ EXPECT_FALSE(server.OnMessageReceived(*msg)); |
+ delete msg; |
+ |
+ msg = new IPC::Message(MSG_ROUTING_CONTROL, MsgClassIS::ID, |
+ IPC::Message::PRIORITY_NORMAL); |
+ msg->WriteInt(0x64); |
+ msg->WriteInt(0x32); |
+ EXPECT_FALSE(server.OnMessageReceived(*msg)); |
+ delete msg; |
+ |
+ EXPECT_EQ(0, server.unhandled_msgs()); |
+#endif |
+} |