Add a Fuzzing configuration and a version of conhost that can be fuzzed (#9604)

This commit introduces a new build configuration, "Fuzzing", which
enables the new address sanitizer (shipped in VS 16.9) and code
coverage over the entire solution. Only a small subset of projects
(those comprising original conhost, right now) are selected to build in
this configuration, and even then only in Fuzzing|x64.

It also adds a fuzzing-adapted build of conhost, which makes no server
connections and handles no client applications. To do this, I've
replicated a bit of the console startup routine into fuzzmain.cpp and
made up some fake data. This is the bare minimum required to boot up
Win32 interactivity (or VT interactivity!) and pretend that a process
has connected.

If we don't pretend that a process has connected, "conhost" will exit
immediately. If we don't forge the process list, conhost will exit. If
we can't provide a server handle, we can't provide a "device comm".

Minor changes were necessary to server/host such that they would accept
a preexisting "device comm". We use this new behavior to provide a
"null" one that only hangs up threads and otherwise responds to requests
successfully.

This fuzzing-adapted build links LLVM's libFuzzer, which is an excellent
coverage-based fuzzer that will produce a corpus of inputs that exercise
unique codepaths. Eventually, we can use this to generate known-"good"
inputs for anything.

I've gone ahead and added a fuzz function that yeets bytes directly into
WriteCharsLegacy, which was the original reason I went down this path.

The implementation of LLVMFuzzerTestOneInput should be replaced with
whatever you want to fuzz.
This commit is contained in:
Dustin L. Howett 2021-03-29 07:23:30 -07:00 committed by GitHub
parent ea3e56db81
commit 12275c8599
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 907 additions and 4 deletions

View File

@ -88,6 +88,7 @@ args
argv
ARRAYSIZE
ARROWKEYS
asan
ASBRST
ASBSET
ASDF
@ -832,6 +833,7 @@ FRAMECHANGED
fre
freopen
frontend
fsanitize
Fscreen
FSCTL
FSINFOCLASS
@ -844,6 +846,7 @@ fullwidth
func
FUNCTIONCALL
fuzzer
fuzzmain
fuzzmap
fuzzwrapper
fwdecl
@ -1256,6 +1259,7 @@ LEFTSHIFT
len
lhs
libpopcnt
libsancov
libtickit
LIMITTEXT
LINEDOWN

File diff suppressed because it is too large Load Diff

View File

@ -42,6 +42,10 @@
<Configuration>AuditMode</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Fuzzing|Win32">
<Configuration>Fuzzing</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
@ -50,6 +54,10 @@
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Fuzzing|x64">
<Configuration>Fuzzing</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="AuditMode|ARM64">
<Configuration>AuditMode</Configuration>
<Platform>ARM64</Platform>
@ -62,6 +70,10 @@
<Configuration>Release</Configuration>
<Platform>ARM64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Fuzzing|ARM64">
<Configuration>Fuzzing</Configuration>
<Platform>ARM64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
@ -125,12 +137,12 @@
</ItemDefinitionGroup>
<!-- For Release ONLY -->
<PropertyGroup Condition="'$(Configuration)'=='Release' Or '$(Configuration)'=='AuditMode'" Label="Configuration">
<PropertyGroup Condition="'$(Configuration)'=='Release' Or '$(Configuration)'=='AuditMode' Or '$(Configuration)'=='Fuzzing'" Label="Configuration">
<UseDebugLibraries>false</UseDebugLibraries>
<WholeProgramOptimization>true</WholeProgramOptimization>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)'=='Release' Or '$(Configuration)'=='AuditMode'">
<ItemDefinitionGroup Condition="'$(Configuration)'=='Release' Or '$(Configuration)'=='AuditMode' Or '$(Configuration)'=='Fuzzing'">
<ClCompile>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
@ -166,6 +178,24 @@
</ClCompile>
</ItemDefinitionGroup>
<PropertyGroup Condition="'$(Configuration)'=='Fuzzing'">
<OCClangArchitectureName Condition="'$(Platform)'=='x64'">x86_64</OCClangArchitectureName>
<OCClangArchitectureName Condition="'$(Platform)'=='Win32'">i386</OCClangArchitectureName>
<OCClangArchitectureName Condition="'$(Platform)'=='x86'">i386</OCClangArchitectureName>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)'=='Fuzzing'">
<ClCompile>
<!-- Enable all the ASAN and Coverage flags! -->
<AdditionalOptions>/fsanitize=address /fsanitize-coverage=inline-bool-flag /fsanitize-coverage=edge /fsanitize-coverage=trace-cmp /fsanitize-coverage=trace-div %(AdditionalOptions)</AdditionalOptions>
<!-- The fuzzer requires a static CRT -->
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<PreprocessorDefinitions>FUZZING_BUILD;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Link>
<AdditionalDependencies>libsancov.lib;clang_rt.asan-$(OCClangArchitectureName).lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<!-- Sanity check: Make sure the user followed the README and initialized git submodules. -->
<Target Name="EnsureSubmodulesExist" BeforeTargets="PrepareForBuild">
<PropertyGroup>

View File

@ -0,0 +1,86 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<ProjectGuid>{05d9052f-d78f-478f-968a-2de38a6db996}</ProjectGuid>
<Keyword>Win32Proj</Keyword>
<RootNamespace>Host.FuzzWrapper</RootNamespace>
<ProjectName>Host.FuzzWrapper</ProjectName>
<TargetName>OpenConsoleFuzzer</TargetName>
<ConfigurationType>Application</ConfigurationType>
</PropertyGroup>
<Import Project="..\..\common.build.pre.props" />
<ItemGroup>
<ClInclude Include="..\precomp.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="..\precomp.cpp">
<PrecompiledHeader>Create</PrecompiledHeader>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
</ClCompile>
<ClCompile Include="fuzzmain.cpp" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\buffer\out\lib\bufferout.vcxproj">
<Project>{0cf235bd-2da0-407e-90ee-c467e8bbc714}</Project>
</ProjectReference>
<ProjectReference Include="..\..\interactivity\base\lib\InteractivityBase.vcxproj">
<Project>{06ec74cb-9a12-429c-b551-8562ec964846}</Project>
</ProjectReference>
<ProjectReference Include="..\..\interactivity\win32\lib\win32.LIB.vcxproj">
<Project>{06ec74cb-9a12-429c-b551-8532ec964726}</Project>
</ProjectReference>
<ProjectReference Include="..\..\internal\internal.vcxproj">
<Project>{ef3e32a7-5ff6-42b4-b6e2-96cd7d033f00}</Project>
</ProjectReference>
<ProjectReference Include="..\..\propslib\propslib.vcxproj">
<Project>{345fd5a4-b32b-4f29-bd1c-b033bd2c35cc}</Project>
</ProjectReference>
<ProjectReference Include="..\..\renderer\base\lib\base.vcxproj">
<Project>{af0a096a-8b3a-4949-81ef-7df8f0fee91f}</Project>
</ProjectReference>
<ProjectReference Include="..\..\renderer\dx\lib\dx.vcxproj">
<Project>{48d21369-3d7b-4431-9967-24e81292cf62}</Project>
</ProjectReference>
<ProjectReference Include="..\..\renderer\gdi\lib\gdi.vcxproj">
<Project>{1c959542-bac2-4e55-9a6d-13251914cbb9}</Project>
</ProjectReference>
<ProjectReference Include="..\..\renderer\vt\lib\vt.vcxproj">
<Project>{990f2657-8580-4828-943f-5dd657d11842}</Project>
</ProjectReference>
<ProjectReference Include="..\..\server\lib\server.vcxproj">
<Project>{18d09a24-8240-42d6-8cb6-236eee820262}</Project>
</ProjectReference>
<ProjectReference Include="..\..\terminal\adapter\lib\adapter.vcxproj">
<Project>{dcf55140-ef6a-4736-a403-957e4f7430bb}</Project>
</ProjectReference>
<ProjectReference Include="..\..\terminal\parser\lib\parser.vcxproj">
<Project>{3ae13314-1939-4dfa-9c14-38ca0834050c}</Project>
</ProjectReference>
<ProjectReference Include="..\..\tsf\tsf.vcxproj">
<Project>{2fd12fbb-1ddb-46d8-b818-1023c624caca}</Project>
</ProjectReference>
<ProjectReference Include="..\..\types\lib\types.vcxproj">
<Project>{18d09a24-8240-42d6-8cb6-236eee820263}</Project>
</ProjectReference>
<ProjectReference Include="..\lib\hostlib.vcxproj">
<Project>{06ec74cb-9a12-429c-b551-8562ec954746}</Project>
</ProjectReference>
</ItemGroup>
<ItemDefinitionGroup>
<ClCompile>
<AdditionalIncludeDirectories>..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)'=='Fuzzing'">
<!-- In theory, we may want to build with a normal main() when Fuzzing is not enabled. -->
<!-- So, let's only add the fuzzer to the link line when we're building for Fuzzing. -->
<Link>
<AdditionalDependencies>clang_rt.fuzzer-$(OCClangArchitectureName).lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<!-- Careful reordering these. Some default props (contained in these files) are order sensitive. -->
<Import Project="..\..\common.build.post.props" />
</Project>

View File

@ -0,0 +1,149 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#include "precomp.h"
#include "../ConsoleArguments.hpp"
#include "../srvinit.h"
#include "../../server/Entrypoints.h"
#include "../../interactivity/inc/ServiceLocator.hpp"
#include "../../server/DeviceHandle.h"
#include "../../server/IoThread.h"
#include "../_stream.h"
#include "../getset.h"
#include <til/u8u16convert.h>
struct NullDeviceComm : public IDeviceComm
{
HRESULT SetServerInformation(CD_IO_SERVER_INFORMATION* const) const override
{
return S_FALSE;
}
HRESULT ReadIo(PCONSOLE_API_MSG const, CONSOLE_API_MSG* const) const override
{
// The easiest way to get the IO thread to stop reading from us us to simply
// suspend it. The fuzzer doesn't need a device IO thread.
SuspendThread(GetCurrentThread());
return S_FALSE;
}
HRESULT CompleteIo(CD_IO_COMPLETE* const) const override
{
return S_FALSE;
}
HRESULT ReadInput(CD_IO_OPERATION* const) const override
{
SuspendThread(GetCurrentThread());
return S_FALSE;
}
HRESULT WriteOutput(CD_IO_OPERATION* const) const override
{
return S_FALSE;
}
HRESULT AllowUIAccess() const override
{
return S_FALSE;
}
ULONG_PTR PutHandle(const void*) override
{
return 0;
}
void* GetHandle(ULONG_PTR) const override
{
return nullptr;
}
HRESULT GetServerHandle(HANDLE*) const override
{
return S_FALSE;
}
};
[[nodiscard]] HRESULT StartNullConsole(const ConsoleArguments* const args)
{
auto& globals = Microsoft::Console::Interactivity::ServiceLocator::LocateGlobals();
globals.pDeviceComm = new NullDeviceComm{}; // quickly, before we "connect". Leak this.
// it is safe to pass INVALID_HANDLE_VALUE here because the null handle would have been detected
// in ConDrvDeviceComm (which has been avoided by setting a global device comm beforehand)
RETURN_IF_NTSTATUS_FAILED(ConsoleCreateIoThreadLegacy(INVALID_HANDLE_VALUE, args));
auto& gci = Microsoft::Console::Interactivity::ServiceLocator::LocateGlobals().getConsoleInformation();
// Process handle list manipulation must be done under lock
gci.LockConsole();
ConsoleProcessHandle* pProcessHandle{ nullptr };
RETURN_IF_FAILED(gci.ProcessHandleList.AllocProcessData(GetCurrentProcessId(),
GetCurrentThreadId(),
0,
nullptr,
&pProcessHandle));
pProcessHandle->fRootProcess = true;
constexpr static std::wstring_view fakeTitle{ L"Fuzzing Harness" };
CONSOLE_API_CONNECTINFO fakeConnectInfo{};
fakeConnectInfo.ConsoleInfo.SetShowWindow(SW_NORMAL);
fakeConnectInfo.ConsoleInfo.SetScreenBufferSize(til::size{ 80, 25 });
fakeConnectInfo.ConsoleInfo.SetWindowSize(til::size{ 80, 25 });
fakeConnectInfo.ConsoleInfo.SetStartupFlags(STARTF_USECOUNTCHARS);
wcscpy_s(fakeConnectInfo.Title, fakeTitle.data());
fakeConnectInfo.TitleLength = gsl::narrow_cast<DWORD>(fakeTitle.size() * sizeof(wchar_t)); // bytes, not wchars
wcscpy_s(fakeConnectInfo.AppName, fakeTitle.data());
fakeConnectInfo.AppNameLength = gsl::narrow_cast<DWORD>(fakeTitle.size() * sizeof(wchar_t)); // bytes, not wchars
fakeConnectInfo.ConsoleApp = TRUE;
fakeConnectInfo.WindowVisible = TRUE;
RETURN_IF_NTSTATUS_FAILED(ConsoleAllocateConsole(&fakeConnectInfo));
CommandHistory::s_Allocate(fakeTitle, (HANDLE)pProcessHandle);
gci.UnlockConsole();
return S_OK;
}
extern "C" __declspec(dllexport) HRESULT RunConhost()
{
Microsoft::Console::Interactivity::ServiceLocator::LocateGlobals().hInstance = wil::GetModuleInstanceHandle();
// passing stdin/stdout lets us drive this like conpty (!!) and test the VT renderer (!!)
// but for now we want to drive it like conhost
ConsoleArguments args({}, nullptr, nullptr);
HRESULT hr = args.ParseCommandline();
if (SUCCEEDED(hr))
{
hr = StartNullConsole(&args);
}
return hr;
}
#ifdef FUZZING_BUILD
extern "C" __declspec(dllexport) int LLVMFuzzerInitialize(int* /*argc*/, char*** /*argv*/)
#else
int main(int /*argc*/, char** /*argv*/)
#endif
{
RETURN_IF_FAILED(RunConhost());
return 0;
}
extern "C" __declspec(dllexport) int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size)
{
auto& gci = Microsoft::Console::Interactivity::ServiceLocator::LocateGlobals().getConsoleInformation();
const auto u16String{ til::u8u16(std::string_view{ reinterpret_cast<const char*>(data), size }) };
SHORT scrollY{};
size_t sizeInBytes{ u16String.size() * 2 };
gci.LockConsole();
auto u = wil::scope_exit([&]() { gci.UnlockConsole(); });
(void)WriteCharsLegacy(gci.GetActiveOutputBuffer(),
u16String.data(),
u16String.data(),
u16String.data(),
&sizeInBytes,
nullptr,
0,
WC_PRINTABLE_CONTROL_CHARS | WC_DESTRUCTIVE_BACKSPACE | WC_KEEP_CURSOR_VISIBLE,
&scrollY);
return 0;
}

View File

@ -46,7 +46,7 @@ public:
CONSOLE_INFORMATION& getConsoleInformation();
IDeviceComm* pDeviceComm;
IDeviceComm* pDeviceComm{ nullptr };
wil::unique_event_nothrow hInputEvent;

View File

@ -40,7 +40,11 @@ try
{
Globals& Globals = ServiceLocator::LocateGlobals();
Globals.pDeviceComm = new ConDrvDeviceComm(Server);
if (!Globals.pDeviceComm)
{
// in rare circumstances (such as in the fuzzing harness), there will already be a device comm
Globals.pDeviceComm = new ConDrvDeviceComm(Server);
}
Globals.launchArgs = *args;