diff options
author | tsepez@chromium.org <tsepez@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-05-04 17:14:16 +0000 |
---|---|---|
committer | tsepez@chromium.org <tsepez@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-05-04 17:14:16 +0000 |
commit | 894803199a98888bcbf913557e0952ae64cd0bf5 (patch) | |
tree | 53de7430f6db15e914e3ec5c965e2f3735cc4f91 | |
parent | 2ad3f3364a83bf499a43fdc8967f32d34c52ce7c (diff) | |
download | chromium_src-894803199a98888bcbf913557e0952ae64cd0bf5.zip chromium_src-894803199a98888bcbf913557e0952ae64cd0bf5.tar.gz chromium_src-894803199a98888bcbf913557e0952ae64cd0bf5.tar.bz2 |
IPC outgoing message filters interpose yourself in a message stream. Minimally invasive baseline for building IPC tests to abuse browser along the lines of a compromised renderer.
Review URL: http://codereview.chromium.org/6711024
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@84076 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | chrome/chrome.gyp | 28 | ||||
-rw-r--r-- | chrome/chrome_common.gypi | 2 | ||||
-rw-r--r-- | chrome/common/chrome_switches.cc | 3 | ||||
-rw-r--r-- | chrome/common/chrome_switches.h | 1 | ||||
-rw-r--r-- | chrome/common/external_ipc_fuzzer.cc | 40 | ||||
-rw-r--r-- | chrome/common/external_ipc_fuzzer.h | 13 | ||||
-rw-r--r-- | chrome/renderer/chrome_content_renderer_client.cc | 5 | ||||
-rw-r--r-- | chrome/tools/ipclist/all_messages.h | 1 | ||||
-rw-r--r-- | chrome/tools/ipclist/ipcfuzz.cc | 688 | ||||
-rw-r--r-- | chrome/tools/ipclist/ipclist.cc | 47 | ||||
-rw-r--r-- | content/browser/renderer_host/browser_render_process_host.cc | 1 | ||||
-rw-r--r-- | content/common/child_thread.h | 4 | ||||
-rw-r--r-- | ipc/ipc_channel_proxy.cc | 9 | ||||
-rw-r--r-- | ipc/ipc_channel_proxy.h | 19 |
14 files changed, 834 insertions, 27 deletions
diff --git a/chrome/chrome.gyp b/chrome/chrome.gyp index 6e69196..48178b6 100644 --- a/chrome/chrome.gyp +++ b/chrome/chrome.gyp @@ -835,15 +835,8 @@ 'target_name': 'ipclist', 'type': 'executable', 'dependencies': [ - 'chrome', - 'chrome_resources', - 'chrome_strings', - 'test_support_common', - 'test_support_ui', - '../skia/skia.gyp:skia', - '../testing/gtest.gyp:gtest', - '../third_party/libxslt/libxslt.gyp:libxslt', - '../third_party/npapi/npapi.gyp:npapi', + 'test_support_common', + '../skia/skia.gyp:skia', ], 'include_dirs': [ '..', @@ -1259,7 +1252,22 @@ ], }], ], - } + }, + { + 'target_name': 'ipcfuzz', + 'type': 'loadable_module', + 'include_dirs': [ + '..', + ], + 'dependencies': [ + 'test_support_common', + '../skia/skia.gyp:skia', + ], + 'sources': [ + 'tools/ipclist/all_messages.h', + 'tools/ipclist/ipcfuzz.cc', + ], + }, ], },], # OS=="linux" ['OS=="win"', diff --git a/chrome/chrome_common.gypi b/chrome/chrome_common.gypi index 451402d6..d488416 100644 --- a/chrome/chrome_common.gypi +++ b/chrome/chrome_common.gypi @@ -45,6 +45,8 @@ 'common/content_settings_helper.h', 'common/content_settings_types.h', 'common/devtools_messages.h', + 'common/external_ipc_fuzzer.h', + 'common/external_ipc_fuzzer.cc', 'common/guid.cc', 'common/guid.h', 'common/guid_posix.cc', diff --git a/chrome/common/chrome_switches.cc b/chrome/common/chrome_switches.cc index d8d34ad..3c7b72a 100644 --- a/chrome/common/chrome_switches.cc +++ b/chrome/common/chrome_switches.cc @@ -435,6 +435,9 @@ const char kEnableInBrowserThumbnailing[] = "enable-in-browser-thumbnailing"; // This flag overrides "disable-ipv6" which appears elswhere in this file. const char kEnableIPv6[] = "enable-ipv6"; +/// Enable the IPC fuzzer for reliability testing +const char kEnableIPCFuzzing[] = "enable-ipc-fuzzing"; + // Enables IP Pooling within the networks stack (SPDY only). When a connection // is needed for a domain which shares an IP with an existing connection, // attempt to use the existing connection. diff --git a/chrome/common/chrome_switches.h b/chrome/common/chrome_switches.h index 56bfbf3..3487030 100644 --- a/chrome/common/chrome_switches.h +++ b/chrome/common/chrome_switches.h @@ -131,6 +131,7 @@ extern const char kEnableFullScreen[]; extern const char kEnableHistoryQuickProvider[]; extern const char kEnableInBrowserThumbnailing[]; extern const char kEnableIPv6[]; +extern const char kEnableIPCFuzzing[]; extern const char kEnableIPPooling[]; extern const char kEnableMemoryInfo[]; extern const char kEnableNaCl[]; diff --git a/chrome/common/external_ipc_fuzzer.cc b/chrome/common/external_ipc_fuzzer.cc new file mode 100644 index 0000000..9e125cc --- /dev/null +++ b/chrome/common/external_ipc_fuzzer.cc @@ -0,0 +1,40 @@ +// Copyright (c) 2011 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 "chrome/common/external_ipc_fuzzer.h" + +#if defined(OS_LINUX) +#include <dlfcn.h> +#endif + +typedef IPC::ChannelProxy::OutgoingMessageFilter *(*GetFuzzerFunction)(); +const char kFuzzLibraryName[] = "libipcfuzz.so"; +const char kFuzzEntryName[] = "GetFilter"; + +IPC::ChannelProxy::OutgoingMessageFilter* LoadExternalIPCFuzzer() { + IPC::ChannelProxy::OutgoingMessageFilter* result = NULL; + +#if defined(OS_LINUX) + + // Fuzz is currently linux-only feature + void *fuzz_library = dlopen(kFuzzLibraryName, RTLD_NOW); + if (fuzz_library) { + GetFuzzerFunction fuzz_entry_point = + reinterpret_cast<GetFuzzerFunction>( + dlsym(fuzz_library, kFuzzEntryName)); + + if (fuzz_entry_point) + result = fuzz_entry_point(); + } + + if (!result) + LOG(WARNING) << dlerror() << "\n"; + +#endif // OS_LINUX + + return result; +} + + + diff --git a/chrome/common/external_ipc_fuzzer.h b/chrome/common/external_ipc_fuzzer.h new file mode 100644 index 0000000..7f8fc63 --- /dev/null +++ b/chrome/common/external_ipc_fuzzer.h @@ -0,0 +1,13 @@ +// Copyright (c) 2011 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. + +#ifndef CHROME_COMMON_EXTERNAL_IPC_FUZZER_H_ +#define CHROME_COMMON_EXTERNAL_IPC_FUZZER_H_ + +#include "ipc/ipc_channel_proxy.h" + +IPC::ChannelProxy::OutgoingMessageFilter* LoadExternalIPCFuzzer(); + +#endif // CHROME_COMMON_EXTERNAL_IPC_FUZZER_H_ + diff --git a/chrome/renderer/chrome_content_renderer_client.cc b/chrome/renderer/chrome_content_renderer_client.cc index 679b795..856d710 100644 --- a/chrome/renderer/chrome_content_renderer_client.cc +++ b/chrome/renderer/chrome_content_renderer_client.cc @@ -15,6 +15,7 @@ #include "chrome/common/child_process_logging.h" #include "chrome/common/chrome_paths.h" #include "chrome/common/chrome_switches.h" +#include "chrome/common/external_ipc_fuzzer.h" #include "chrome/common/extensions/extension.h" #include "chrome/common/extensions/extension_constants.h" #include "chrome/common/extensions/extension_set.h" @@ -164,6 +165,10 @@ void ChromeContentRendererClient::RenderThreadStarted() { thread->RegisterExtension(DomAutomationV8Extension::Get()); } + if (CommandLine::ForCurrentProcess()->HasSwitch( + switches::kEnableIPCFuzzing)) { + thread->channel()->set_outgoing_message_filter(LoadExternalIPCFuzzer()); + } // chrome: pages should not be accessible by normal content, and should // also be unable to script anything but themselves (to help limit the damage // that a corrupt chrome: page could cause). diff --git a/chrome/tools/ipclist/all_messages.h b/chrome/tools/ipclist/all_messages.h index 1d5f69b..6870f2b 100644 --- a/chrome/tools/ipclist/all_messages.h +++ b/chrome/tools/ipclist/all_messages.h @@ -11,4 +11,5 @@ #include "chrome/common/automation_messages.h" #include "chrome/common/nacl_messages.h" #include "content/common/content_message_generator.h" +#include "content/common/pepper_messages.h" #include "ppapi/proxy/ppapi_messages.h" diff --git a/chrome/tools/ipclist/ipcfuzz.cc b/chrome/tools/ipclist/ipcfuzz.cc new file mode 100644 index 0000000..91a3d2e --- /dev/null +++ b/chrome/tools/ipclist/ipcfuzz.cc @@ -0,0 +1,688 @@ +// Copyright (c) 2011 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 <algorithm> +#include <iostream> +#include <set> +#include <vector> + +#include "base/command_line.h" +#include "base/hash_tables.h" +#include "base/memory/singleton.h" +#include "base/message_loop.h" +#include "base/pickle.h" +#include "base/process_util.h" +#include "base/scoped_ptr.h" +#include "base/string_number_conversions.h" +#include "base/string_util.h" +#include "base/threading/thread.h" +#include "base/time.h" +#include "base/utf_string_conversions.h" +#include "chrome/tools/ipclist/all_messages.h" +#include "ipc/ipc_message.h" +#include "ipc/ipc_message_utils.h" +#include "ipc/ipc_switches.h" +#include "ipc/ipc_sync_channel.h" +#include "ipc/ipc_sync_message.h" + +#if defined(OS_POSIX) +#include <unistd.h> +#endif + +namespace IPC { +class Message; + +// Interface implemented by those who fuzz basic types. The types all +// correspond to the types which a pickle from base/pickle.h can pickle, +// plus the floating point types. +class Fuzzer { + public: + // Select a message for fuzzing. + virtual bool FuzzThisMessage(const IPC::Message *msg) = 0; + + // Tweak individual values within a message. + virtual void FuzzBool(bool* value) = 0; + virtual void FuzzInt(int* value) = 0; + virtual void FuzzLong(long* value) = 0; + virtual void FuzzSize(size_t* value) = 0; + virtual void FuzzUChar(unsigned char *value) = 0; + virtual void FuzzUInt16(uint16* value) = 0; + virtual void FuzzUInt32(uint32* value) = 0; + virtual void FuzzInt64(int64* value) = 0; + virtual void FuzzUInt64(uint64* value) = 0; + virtual void FuzzFloat(float *value) = 0; + virtual void FuzzDouble(double *value) = 0; + virtual void FuzzString(std::string* value) = 0; + virtual void FuzzWString(std::wstring* value) = 0; + virtual void FuzzString16(string16* value) = 0; + virtual void FuzzData(char* data, int length) = 0; + virtual void FuzzBytes(void* data, int data_len) = 0; +}; + +} // Namespace IPC + +namespace { + +template <typename T> +void FuzzIntegralType(T* value, unsigned int frequency) { + if (rand() % frequency == 0) { + switch (rand() % 4) { + case 0: (*value) = 0; break; + case 1: (*value)--; break; + case 2: (*value)++; break; + case 3: (*value) ^= rand(); break; + } + } +} + +template <typename T> +void FuzzStringType(T* value, unsigned int frequency, + const T& literal1, const T& literal2) { + if (rand() % frequency == 0) { + switch (rand() % 5) { + case 4: (*value) = (*value) + (*value); // FALLTHROUGH + case 3: (*value) = (*value) + (*value); // FALLTHROUGH + case 2: (*value) = (*value) + (*value); break; + case 1: (*value) += literal1; break; + case 0: (*value) = literal2; break; + } + } +} + +} // Namespace + +// One such fuzzer implementation. +class DefaultFuzzer : public IPC::Fuzzer { + public: + static const int DEFAULT_FREQUENCY = 23; + + DefaultFuzzer() : frequency_(DEFAULT_FREQUENCY) { + const char *env_var; + if ((env_var = getenv("CHROME_IPC_FUZZING_LIST"))) { + std::string str = std::string(env_var); + size_t pos; + while ((pos = str.find_first_of(',')) != std::string::npos) { + message_set_.insert(atoi(str.substr(0, pos).c_str())); + str = str.substr(pos+1); + } + message_set_.insert(atoi(str.c_str())); + } + + if ((env_var = getenv("CHROME_IPC_FUZZING_SEED"))) { + int new_seed = atoi(env_var); + if (new_seed) + srand(new_seed); + } + + if ((env_var = getenv("CHROME_IPC_FUZZING_FREQUENCY"))) + { + unsigned int new_frequency = atoi(env_var); + if (new_frequency) + frequency_ = new_frequency; + } + } + + virtual ~DefaultFuzzer() {} + + virtual bool FuzzThisMessage(const IPC::Message *msg) { + return (message_set_.empty() || + std::find(message_set_.begin(), + message_set_.end(), + msg->type()) != message_set_.end()); + } + + virtual void FuzzBool(bool* value) { + if (rand() % frequency_ == 0) + (*value) = !(*value); + } + + virtual void FuzzInt(int* value) { + FuzzIntegralType<int>(value, frequency_); + } + + virtual void FuzzLong(long* value) { + FuzzIntegralType<long>(value, frequency_); + } + + virtual void FuzzSize(size_t* value) { + FuzzIntegralType<size_t>(value, frequency_); + } + + virtual void FuzzUChar(unsigned char* value) { + FuzzIntegralType<unsigned char>(value, frequency_); + } + + virtual void FuzzUInt16(uint16* value) { + FuzzIntegralType<uint16>(value, frequency_); + } + + virtual void FuzzUInt32(uint32* value) { + FuzzIntegralType<uint32>(value, frequency_); + } + + virtual void FuzzInt64(int64* value) { + FuzzIntegralType<int64>(value, frequency_); + } + + virtual void FuzzUInt64(uint64* value) { + FuzzIntegralType<uint64>(value, frequency_); + } + + virtual void FuzzFloat(float* value) { + if (rand() % frequency_ == 0) + (*value) *= rand() / 1000000.0; + } + + virtual void FuzzDouble(double* value) { + if (rand() % frequency_ == 0) + (*value) *= rand() / 1000000.0; + } + + virtual void FuzzString(std::string* value) { + FuzzStringType<std::string>(value, frequency_, "BORKED", ""); + } + + virtual void FuzzWString(std::wstring* value) { + FuzzStringType<std::wstring>(value, frequency_, L"BORKED", L""); + } + + virtual void FuzzString16(string16* value) { + FuzzStringType<string16>(value, frequency_, + WideToUTF16(L"BORKED"), + WideToUTF16(L"")); + } + + virtual void FuzzData(char* data, int length) { + if (rand() % frequency_ == 0) { + for (int i = 0; i < length; ++i) { + FuzzIntegralType<char>(&data[i], frequency_); + } + } + } + + virtual void FuzzBytes(void* data, int data_len) { + FuzzData(static_cast<char*>(data), data_len); + } + + private: + std::set<int> message_set_; + unsigned int frequency_; +}; + + +// No-op fuzzer. Rewrites each message unchanged to check if the message +// re-assembly is legit. +class NoOpFuzzer : public IPC::Fuzzer { + public: + NoOpFuzzer() {} + virtual ~NoOpFuzzer() {} + + virtual bool FuzzThisMessage(const IPC::Message *msg) { + return true; + } + + virtual void FuzzBool(bool* value) {} + virtual void FuzzInt(int* value) {} + virtual void FuzzLong(long* value) {} + virtual void FuzzSize(size_t* value) {} + virtual void FuzzUChar(unsigned char* value) {} + virtual void FuzzUInt16(uint16* value) {} + virtual void FuzzUInt32(uint32* value) {} + virtual void FuzzInt64(int64* value) {} + virtual void FuzzUInt64(uint64* value) {} + virtual void FuzzFloat(float* value) {} + virtual void FuzzDouble(double* value) {} + virtual void FuzzString(std::string* value) {} + virtual void FuzzWString(std::wstring* value) {} + virtual void FuzzString16(string16* value) {} + virtual void FuzzData(char* data, int length) {} + virtual void FuzzBytes(void* data, int data_len) {} +}; + +class FuzzerFactory { + public: + static IPC::Fuzzer *NewFuzzer(const std::string& name) { + if (name == "no-op") + return new NoOpFuzzer(); + else + return new DefaultFuzzer(); + } +}; + +// Partially-specialized class that knows how to fuzz a given type. +template <class P> +struct FuzzTraits { + static void Fuzz(P* p, IPC::Fuzzer *fuzzer) {} +}; + +// Template function to invoke partially-specialized class method. +template <class P> +static void FuzzParam(P* p, IPC::Fuzzer* fuzzer) { + FuzzTraits<P>::Fuzz(p, fuzzer); +} + +// Specializations to fuzz primitive types. +template <> +struct FuzzTraits<bool> { + static void Fuzz(bool* p, IPC::Fuzzer* fuzzer) { + fuzzer->FuzzBool(p); + } +}; + +template <> +struct FuzzTraits<int> { + static void Fuzz(int* p, IPC::Fuzzer* fuzzer) { + fuzzer->FuzzInt(p); + } +}; + +template <> +struct FuzzTraits<unsigned int> { + static void Fuzz(unsigned int* p, IPC::Fuzzer* fuzzer) { + fuzzer->FuzzInt(reinterpret_cast<int*>(p)); + } +}; + +template <> +struct FuzzTraits<long> { + static void Fuzz(long* p, IPC::Fuzzer* fuzzer) { + fuzzer->FuzzLong(p); + } +}; + +template <> +struct FuzzTraits<unsigned long> { + static void Fuzz(unsigned long* p, IPC::Fuzzer* fuzzer) { + fuzzer->FuzzLong(reinterpret_cast<long*>(p)); + } +}; + +template <> +struct FuzzTraits<long long> { + static void Fuzz(long long* p, IPC::Fuzzer* fuzzer) { + fuzzer->FuzzInt64(reinterpret_cast<int64*>(p)); + } +}; + +template <> +struct FuzzTraits<unsigned long long> { + static void Fuzz(unsigned long long* p, IPC::Fuzzer* fuzzer) { + fuzzer->FuzzInt64(reinterpret_cast<int64*>(p)); + } +}; + +template <> +struct FuzzTraits<short> { + static void Fuzz(short* p, IPC::Fuzzer* fuzzer) { + fuzzer->FuzzUInt16(reinterpret_cast<uint16*>(p)); + } +}; + +template <> +struct FuzzTraits<unsigned short> { + static void Fuzz(unsigned short* p, IPC::Fuzzer* fuzzer) { + fuzzer->FuzzUInt16(reinterpret_cast<uint16*>(p)); + } +}; + +template <> +struct FuzzTraits<char> { + static void Fuzz(char* p, IPC::Fuzzer* fuzzer) { + fuzzer->FuzzUChar(reinterpret_cast<unsigned char*>(p)); + } +}; + +template <> +struct FuzzTraits<unsigned char> { + static void Fuzz(unsigned char* p, IPC::Fuzzer* fuzzer) { + fuzzer->FuzzUChar(p); + } +}; + +template <> +struct FuzzTraits<float> { + static void Fuzz(float* p, IPC::Fuzzer* fuzzer) { + fuzzer->FuzzFloat(p); + } +}; + +template <> +struct FuzzTraits<double> { + static void Fuzz(double* p, IPC::Fuzzer* fuzzer) { + fuzzer->FuzzDouble(p); + } +}; + +template <> +struct FuzzTraits<std::string> { + static void Fuzz(std::string* p, IPC::Fuzzer* fuzzer) { + fuzzer->FuzzString(p); + } +}; + +template <> +struct FuzzTraits<std::wstring> { + static void Fuzz(std::wstring* p, IPC::Fuzzer* fuzzer) { + fuzzer->FuzzWString(p); + } +}; + +template <> +struct FuzzTraits<string16> { + static void Fuzz(string16* p, IPC::Fuzzer* fuzzer) { + fuzzer->FuzzString16(p); + } +}; + +// Specializations to fuzz tuples. +template <class A> +struct FuzzTraits<Tuple1<A> > { + static void Fuzz(Tuple1<A>* p, IPC::Fuzzer* fuzzer) { + FuzzParam(&p->a, fuzzer); + } +}; + +template <class A, class B> +struct FuzzTraits<Tuple2<A, B> > { + static void Fuzz(Tuple2<A, B>* p, IPC::Fuzzer* fuzzer) { + FuzzParam(&p->a, fuzzer); + FuzzParam(&p->b, fuzzer); + } +}; + +template <class A, class B, class C> +struct FuzzTraits<Tuple3<A, B, C> > { + static void Fuzz(Tuple3<A, B, C>* p, IPC::Fuzzer* fuzzer) { + FuzzParam(&p->a, fuzzer); + FuzzParam(&p->b, fuzzer); + FuzzParam(&p->c, fuzzer); + } +}; + +template <class A, class B, class C, class D> +struct FuzzTraits<Tuple4<A, B, C, D> > { + static void Fuzz(Tuple4<A, B, C, D>* p, IPC::Fuzzer* fuzzer) { + FuzzParam(&p->a, fuzzer); + FuzzParam(&p->b, fuzzer); + FuzzParam(&p->c, fuzzer); + FuzzParam(&p->d, fuzzer); + } +}; + +template <class A, class B, class C, class D, class E> +struct FuzzTraits<Tuple5<A, B, C, D, E> > { + static void Fuzz(Tuple5<A, B, C, D, E>* p, IPC::Fuzzer* fuzzer) { + FuzzParam(&p->a, fuzzer); + FuzzParam(&p->b, fuzzer); + FuzzParam(&p->c, fuzzer); + FuzzParam(&p->d, fuzzer); + FuzzParam(&p->e, fuzzer); + } +}; + +// Specializations to fuzz containers. +template <class A> +struct FuzzTraits<std::vector<A> > { + static void Fuzz(std::vector<A>* p, IPC::Fuzzer* fuzzer) { + for (size_t i = 0; i < p->size(); ++i) { + FuzzParam(&p->at(i), fuzzer); + } + } +}; + +template <class A, class B> +struct FuzzTraits<std::map<A, B> > { + static void Fuzz(std::map<A, B>* p, IPC::Fuzzer* fuzzer) { + typename std::map<A, B>::iterator it; + for (it = p->begin(); it != p->end(); ++it) { + FuzzParam(&it->second, fuzzer); + } + } +}; + +template <class A, class B> +struct FuzzTraits<std::pair<A, B> > { + static void Fuzz(std::pair<A, B>* p, IPC::Fuzzer* fuzzer) { + FuzzParam(&p->second, fuzzer); + } +}; + +// Specializations to fuzz hand-coded tyoes +template <> +struct FuzzTraits<base::FileDescriptor> { + static void Fuzz(base::FileDescriptor* p, IPC::Fuzzer* fuzzer) { + FuzzParam(&p->fd, fuzzer); + } +}; + +template <> +struct FuzzTraits<GURL> { + static void Fuzz(GURL *p, IPC::Fuzzer* fuzzer) { + FuzzParam(&p->possibly_invalid_spec(), fuzzer); + } +}; + +template <> +struct FuzzTraits<gfx::Point> { + static void Fuzz(gfx::Point *p, IPC::Fuzzer* fuzzer) { + int x = p->x(); + int y = p->y(); + FuzzParam(&x, fuzzer); + FuzzParam(&y, fuzzer); + p->SetPoint(x, y); + } +}; + +template <> +struct FuzzTraits<gfx::Size> { + static void Fuzz(gfx::Size *p, IPC::Fuzzer* fuzzer) { + int w = p->width(); + int h = p->height(); + FuzzParam(&w, fuzzer); + FuzzParam(&h, fuzzer); + p->SetSize(w, h); + } +}; + +template <> +struct FuzzTraits<gfx::Rect> { + static void Fuzz(gfx::Rect *p, IPC::Fuzzer* fuzzer) { + gfx::Point origin = p->origin(); + gfx::Size size = p->size(); + FuzzParam(&origin, fuzzer); + FuzzParam(&size, fuzzer); + p->set_origin(origin); + p->set_size(size); + } +}; + +// Means for updating message id in pickles. +class PickleCracker : public Pickle { + public: + static void CopyMessageID(PickleCracker *dst, PickleCracker *src) { + memcpy(dst->payload(), src->payload(), sizeof(int)); + } +}; + +// Redefine macros to generate fuzzing from traits declarations. +// Null out all the macros that need nulling. +#include "ipc/ipc_message_null_macros.h" + +// STRUCT declarations cause corresponding STRUCT_TRAITS declarations to occur. +#undef IPC_STRUCT_BEGIN +#undef IPC_STRUCT_MEMBER +#undef IPC_STRUCT_END +#define IPC_STRUCT_BEGIN(struct_name) IPC_STRUCT_TRAITS_BEGIN(struct_name) +#define IPC_STRUCT_MEMBER(type, name) IPC_STRUCT_TRAITS_MEMBER(name) +#define IPC_STRUCT_END() IPC_STRUCT_TRAITS_END() + +// Set up so next include will generate fuzz trait classes. +#undef IPC_STRUCT_TRAITS_BEGIN +#undef IPC_STRUCT_TRAITS_MEMBER +#undef IPC_STRUCT_TRAITS_PARENT +#undef IPC_STRUCT_TRAITS_END +#define IPC_STRUCT_TRAITS_BEGIN(struct_name) \ + template <> \ + struct FuzzTraits<struct_name> { \ + static void Fuzz(struct_name *p, IPC::Fuzzer* fuzzer) { \ + +#define IPC_STRUCT_TRAITS_MEMBER(name) \ + FuzzParam(&p->name, fuzzer); + +#define IPC_STRUCT_TRAITS_PARENT(type) \ + FuzzParam(static_cast<type*>(p), fuzzer); + +#define IPC_STRUCT_TRAITS_END() \ + } \ + }; + +#undef IPC_ENUM_TRAITS +#define IPC_ENUM_TRAITS(enum_name) \ + template <> \ + struct FuzzTraits<enum_name> { \ + static void Fuzz(enum_name* p, IPC::Fuzzer* fuzzer) { \ + FuzzParam(reinterpret_cast<int*>(p), fuzzer); \ + } \ + }; + +// Bring them into existence. +#include "chrome/tools/ipclist/all_messages.h" + +// Redefine macros to generate fuzzing funtions +#include "ipc/ipc_message_null_macros.h" +#undef IPC_MESSAGE_DECL +#define IPC_MESSAGE_DECL(kind, type, name, in, out, ilist, olist) \ + IPC_##kind##_##type##_FUZZ(name, in, out, ilist, olist) + +#define IPC_EMPTY_CONTROL_FUZZ(name, in, out, ilist, olist) \ + IPC::Message* fuzzer_for_##name(IPC::Message *msg, IPC::Fuzzer* fuzzer) { \ + return NULL; \ + } + +#define IPC_EMPTY_ROUTED_FUZZ(name, in, out, ilist, olist) \ + IPC::Message* fuzzer_for_##name(IPC::Message *msg, IPC::Fuzzer* fuzzer) { \ + return NULL; \ + } + +#define IPC_ASYNC_CONTROL_FUZZ(name, in, out, ilist, olist) \ + IPC::Message* fuzzer_for_##name(IPC::Message *msg, IPC::Fuzzer* fuzzer) { \ + name* real_msg = static_cast<name*>(msg); \ + IPC_TUPLE_IN_##in ilist p; \ + name::Read(real_msg, &p); \ + FuzzParam(&p, fuzzer); \ + return new name(IPC_MEMBERS_IN_##in(p)); \ + } + +#define IPC_ASYNC_ROUTED_FUZZ(name, in, out, ilist, olist) \ + IPC::Message* fuzzer_for_##name(IPC::Message *msg, IPC::Fuzzer* fuzzer) { \ + name* real_msg = static_cast<name*>(msg); \ + IPC_TUPLE_IN_##in ilist p; \ + name::Read(real_msg, &p); \ + FuzzParam(&p, fuzzer); \ + return new name(msg->routing_id() \ + IPC_COMMA_##in \ + IPC_MEMBERS_IN_##in(p)); \ + } + +#define IPC_SYNC_CONTROL_FUZZ(name, in, out, ilist, olist) \ + IPC::Message* fuzzer_for_##name(IPC::Message *msg, IPC::Fuzzer* fuzzer) { \ + name* real_msg = static_cast<name*>(msg); \ + IPC_TUPLE_IN_##in ilist p; \ + name::ReadSendParam(real_msg, &p); \ + FuzzParam(&p, fuzzer); \ + name* new_msg = new name(IPC_MEMBERS_IN_##in(p) \ + IPC_COMMA_AND_##out(IPC_COMMA_##in) \ + IPC_MEMBERS_OUT_##out()); \ + PickleCracker::CopyMessageID( \ + reinterpret_cast<PickleCracker *>(new_msg), \ + reinterpret_cast<PickleCracker *>(real_msg)); \ + return new_msg; \ + } + + +#define IPC_SYNC_ROUTED_FUZZ(name, in, out, ilist, olist) \ + IPC::Message* fuzzer_for_##name(IPC::Message *msg, IPC::Fuzzer* fuzzer) { \ + name* real_msg = static_cast<name*>(msg); \ + IPC_TUPLE_IN_##in ilist p; \ + name::ReadSendParam(real_msg, &p); \ + FuzzParam(&p, fuzzer); \ + name* new_msg = new name(msg->routing_id() \ + IPC_COMMA_OR_##out(IPC_COMMA_##in) \ + IPC_MEMBERS_IN_##in(p) \ + IPC_COMMA_AND_##out(IPC_COMMA_##in) \ + IPC_MEMBERS_OUT_##out()); \ + PickleCracker::CopyMessageID( \ + reinterpret_cast<PickleCracker *>(new_msg), \ + reinterpret_cast<PickleCracker *>(real_msg)); \ + return new_msg; \ + } + +#define IPC_MEMBERS_IN_0(p) +#define IPC_MEMBERS_IN_1(p) p.a +#define IPC_MEMBERS_IN_2(p) p.a, p.b +#define IPC_MEMBERS_IN_3(p) p.a, p.b, p.c +#define IPC_MEMBERS_IN_4(p) p.a, p.b, p.c, p.d +#define IPC_MEMBERS_IN_5(p) p.a, p.b, p.c, p.d, p.e + +#define IPC_MEMBERS_OUT_0() +#define IPC_MEMBERS_OUT_1() NULL +#define IPC_MEMBERS_OUT_2() NULL, NULL +#define IPC_MEMBERS_OUT_3() NULL, NULL, NULL +#define IPC_MEMBERS_OUT_4() NULL, NULL, NULL, NULL +#define IPC_MEMBERS_OUT_5() NULL, NULL, NULL, NULL, NULL + +#include "chrome/tools/ipclist/all_messages.h" + +typedef IPC::Message* (*FuzzFunction)(IPC::Message*, IPC::Fuzzer*); +typedef base::hash_map<uint32, FuzzFunction> FuzzFunctionMap; + +// Redefine macros to register fuzzing functions into map. +#include "ipc/ipc_message_null_macros.h" +#undef IPC_MESSAGE_DECL +#define IPC_MESSAGE_DECL(kind, type, name, in, out, ilist, olist) \ + (*map)[static_cast<uint32>(name::ID)] = fuzzer_for_##name; + +void PopulateFuzzFunctionMap(FuzzFunctionMap *map) { +#include "chrome/tools/ipclist/all_messages.h" +} + +class ipcfuzz : public IPC::ChannelProxy::OutgoingMessageFilter { + public: + ipcfuzz() { + const char* env_var = getenv("CHROME_IPC_FUZZING_KIND"); + fuzzer_ = FuzzerFactory::NewFuzzer(env_var ? env_var : ""); + PopulateFuzzFunctionMap(&fuzz_function_map_); + } + + IPC::Message* Rewrite(IPC::Message* message) { + if (fuzzer_ && fuzzer_->FuzzThisMessage(message)) { + FuzzFunctionMap::iterator it = fuzz_function_map_.find(message->type()); + if (it != fuzz_function_map_.end()) { + IPC::Message* fuzzed_message = (*it->second)(message, fuzzer_); + if (fuzzed_message) { + delete message; + message = fuzzed_message; + } + } + } + return message; + } + + private: + IPC::Fuzzer* fuzzer_; + FuzzFunctionMap fuzz_function_map_; +}; + +ipcfuzz g_ipcfuzz; + +// Entry point avoiding mangled names. +extern "C" { + __attribute__((visibility("default"))) + IPC::ChannelProxy::OutgoingMessageFilter* GetFilter(void); +} + +IPC::ChannelProxy::OutgoingMessageFilter* GetFilter(void) { + return &g_ipcfuzz; +} + diff --git a/chrome/tools/ipclist/ipclist.cc b/chrome/tools/ipclist/ipclist.cc index 53af9d7..de9aea7 100644 --- a/chrome/tools/ipclist/ipclist.cc +++ b/chrome/tools/ipclist/ipclist.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Copyright (c) 2011 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. @@ -75,36 +75,57 @@ static bool check_msgtable() { return result; } -static void dump_msgtable(bool show_args, bool show_ids) { +static void dump_msgtable(bool show_args, bool show_ids, + bool show_comma, const char *prefix) { + bool first = true; for (size_t i = 0; i < MSGTABLE_SIZE; ++i) { - if (show_ids) { - std::cout << "{" << IPC_MESSAGE_ID_CLASS(msgtable[i].id) << ", " << - IPC_MESSAGE_ID_LINE(msgtable[i].id) << "}: "; - } - std::cout << msgtable[i].name; - if (show_args) { - std::cout << "(" << msgtable[i].in_count << " IN, " << - msgtable[i].out_count << " OUT)"; + if ((!prefix) || strstr(msgtable[i].name, prefix) == msgtable[i].name) { + if (show_comma) { + if (!first) + std::cout << ","; + first = false; + std::cout << msgtable[i].id; + } else { + if (show_ids) + std::cout << msgtable[i].id << " " << + IPC_MESSAGE_ID_CLASS(msgtable[i].id) << "," << + IPC_MESSAGE_ID_LINE(msgtable[i].id) << " "; + std::cout << msgtable[i].name; + if (show_args) { + std::cout << "(" << msgtable[i].in_count << " IN, " << + msgtable[i].out_count << " OUT)"; + } + std::cout << "\n"; + } } - std::cout << "\n"; } + if (show_comma) + std::cout << "\n"; } int main(int argc, char **argv) { bool show_args = false; bool show_ids = false; bool skip_check = false; + bool show_comma = false; + const char *filter = NULL; while (--argc > 0) { ++argv; if (std::string("--args") == *argv) { show_args = true; + } else if (std::string("--comma") == *argv) { + show_comma = true; + } else if (std::string("--filter") == *argv) { + filter = *(++argv); --argc; } else if (std::string("--ids") == *argv) { show_ids = true; } else if (std::string("--no-check") == *argv) { skip_check = true; } else { - std::cout << "usage: ipclist [--args] [--ids] [--no-check]\n"; + std::cout << + "usage: ipclist [--args] [--ids] [--no-check] [--filter prefix] " + "[--comma]\n"; return 1; } } @@ -114,7 +135,7 @@ int main(int argc, char **argv) { if (!skip_check && check_msgtable() == false) return 1; - dump_msgtable(show_args, show_ids); + dump_msgtable(show_args, show_ids, show_comma, filter); return 0; } diff --git a/content/browser/renderer_host/browser_render_process_host.cc b/content/browser/renderer_host/browser_render_process_host.cc index d422775..5e3354c 100644 --- a/content/browser/renderer_host/browser_render_process_host.cc +++ b/content/browser/renderer_host/browser_render_process_host.cc @@ -552,6 +552,7 @@ void BrowserRenderProcessHost::PropagateBrowserCommandLineToRenderer( switches::kEnableDCHECK, switches::kEnableExperimentalExtensionApis, switches::kEnableInBrowserThumbnailing, + switches::kEnableIPCFuzzing, switches::kEnableLogging, switches::kEnableNaCl, switches::kEnableOpenMax, diff --git a/content/common/child_thread.h b/content/common/child_thread.h index f2ac864..e1d34e8 100644 --- a/content/common/child_thread.h +++ b/content/common/child_thread.h @@ -42,6 +42,8 @@ class ChildThread : public IPC::Channel::Listener, IPC::Channel::Listener* ResolveRoute(int32 routing_id); + IPC::SyncChannel* channel() { return channel_.get(); } + // Creates a ResourceLoaderBridge. // Tests can override this method if they want a custom loading behavior. virtual webkit_glue::ResourceLoaderBridge* CreateBridge( @@ -84,8 +86,6 @@ class ChildThread : public IPC::Channel::Listener, virtual void OnSetIPCLoggingEnabled(bool enable); #endif - IPC::SyncChannel* channel() { return channel_.get(); } - void set_on_channel_error_called(bool on_channel_error_called) { on_channel_error_called_ = on_channel_error_called; } diff --git a/ipc/ipc_channel_proxy.cc b/ipc/ipc_channel_proxy.cc index 8f981f4..cbc18bc 100644 --- a/ipc/ipc_channel_proxy.cc +++ b/ipc/ipc_channel_proxy.cc @@ -283,7 +283,8 @@ ChannelProxy::ChannelProxy(const IPC::ChannelHandle& channel_handle, Channel::Mode mode, Channel::Listener* listener, base::MessageLoopProxy* ipc_thread) - : context_(new Context(listener, ipc_thread)) { + : context_(new Context(listener, ipc_thread)), + outgoing_message_filter_(NULL) { Init(channel_handle, mode, ipc_thread, true); } @@ -292,7 +293,8 @@ ChannelProxy::ChannelProxy(const IPC::ChannelHandle& channel_handle, base::MessageLoopProxy* ipc_thread, Context* context, bool create_pipe_now) - : context_(context) { + : context_(context), + outgoing_message_filter_(NULL) { Init(channel_handle, mode, ipc_thread, create_pipe_now); } @@ -343,6 +345,9 @@ void ChannelProxy::Close() { } bool ChannelProxy::Send(Message* message) { + if (outgoing_message_filter()) + message = outgoing_message_filter()->Rewrite(message); + #ifdef IPC_MESSAGE_LOG_ENABLED Logging::GetInstance()->OnSendMessage(message, context_->channel_id()); #endif diff --git a/ipc/ipc_channel_proxy.h b/ipc/ipc_channel_proxy.h index 2b1dea8..bcdeaac 100644 --- a/ipc/ipc_channel_proxy.h +++ b/ipc/ipc_channel_proxy.h @@ -98,6 +98,15 @@ class ChannelProxy : public Message::Sender { } }; + // Interface for a filter to be imposed on outgoing messages which can + // re-write the message. Used mainly for testing. + class OutgoingMessageFilter { + public: + // Returns a re-written message, freeing the original, or simply the + // original unchanged if no rewrite indicated. + virtual Message *Rewrite(Message *message) = 0; + }; + // Initializes a channel proxy. The channel_handle and mode parameters are // passed directly to the underlying IPC::Channel. The listener is called on // the thread that creates the ChannelProxy. The filter's OnMessageReceived @@ -139,6 +148,10 @@ class ChannelProxy : public Message::Sender { void AddFilter(MessageFilter* filter); void RemoveFilter(MessageFilter* filter); + void set_outgoing_message_filter(OutgoingMessageFilter* filter) { + outgoing_message_filter_ = filter; + } + // Called to clear the pointer to the IPC message loop when it's going away. void ClearIPCMessageLoop(); @@ -236,6 +249,10 @@ class ChannelProxy : public Message::Sender { Context* context() { return context_; } + OutgoingMessageFilter* outgoing_message_filter() { + return outgoing_message_filter_; + } + private: friend class SendTask; @@ -246,6 +263,8 @@ class ChannelProxy : public Message::Sender { // can safely be destroyed while the background thread continues to do stuff // that involves this data. scoped_refptr<Context> context_; + + OutgoingMessageFilter* outgoing_message_filter_; }; } // namespace IPC |