Index: compiler-rt/lib/fuzzer/FuzzerLoop.cpp =================================================================== --- compiler-rt/lib/fuzzer/FuzzerLoop.cpp +++ compiler-rt/lib/fuzzer/FuzzerLoop.cpp @@ -151,7 +151,7 @@ TPC.SetUseValueProfileMask(Options.UseValueProfile); if (Options.Verbosity) - TPC.PrintModuleInfo(); + TPC.PrintModuleSummary(); if (!Options.OutputCorpus.empty() && Options.ReloadIntervalSec) EpochOfLastReadOfOutputCorpus = GetEpoch(Options.OutputCorpus); MaxInputLen = MaxMutationLen = Options.MaxLen; Index: compiler-rt/lib/fuzzer/FuzzerModuleRelative.h =================================================================== --- compiler-rt/lib/fuzzer/FuzzerModuleRelative.h +++ compiler-rt/lib/fuzzer/FuzzerModuleRelative.h @@ -14,10 +14,36 @@ #include "FuzzerDefs.h" +#include #include namespace fuzzer { +// This struct contains bookkepping information for a single Module. See also +// the ModuleInfoBy* methods in TracePC. +struct ModuleInfo { + // SimpleHash of the relative PCs offsets and flags. For a PC table like + // `TracePC::PCTableEntry PCs[]`, the relative offset of `PCs[i]` is + // `PCs[i] - PCs[0]`. The hash for a particular module is position- + // independent, but is not collision-resistant. + uint64_t Hash = 0; + + // [FirstFeature, LastFeature) is the range of features as converted from the + // associated module's counters by TracePC. See also TracePC::CollectFeatures. + size_t FirstFeature = 0; + size_t LastFeature = 0; + + // [FirstIdx, LastIdx) is the range of indices into the PC tables for the + // associated module. + uintptr_t FirstIdx = 0; + uintptr_t LastIdx = 0; + + // Index into TracePC's ModulePCTable array. TracePC treats a ModuleInfo + // struct with an offset greater than the length of the array as + // uninitialized. + size_t PCTableOffset = std::numeric_limits::max(); +}; + // This class represents an aggregation of sets of values associated with a // Module. It provides methods for adding/removing additional Modules and/or // values. Index: compiler-rt/lib/fuzzer/FuzzerTracePC.h =================================================================== --- compiler-rt/lib/fuzzer/FuzzerTracePC.h +++ compiler-rt/lib/fuzzer/FuzzerTracePC.h @@ -13,6 +13,7 @@ #include "FuzzerDefs.h" #include "FuzzerDictionary.h" +#include "FuzzerModuleRelative.h" #include "FuzzerValueBitMap.h" #include @@ -68,9 +69,14 @@ }; class TracePC { - public: +public: void HandleInline8bitCountersInit(uint8_t *Start, uint8_t *Stop); + void HandleInline8bitCountersInit(uint8_t *Start, uint8_t *Stop, + size_t ModuleOffset); void HandlePCsInit(const uintptr_t *Start, const uintptr_t *Stop); + void HandlePCsInit(const uintptr_t *Start, const uintptr_t *Stop, + uint64_t Hash); + void HandleCallerCallee(uintptr_t Caller, uintptr_t Callee); template void HandleCmp(uintptr_t PC, T Arg1, T Arg2); size_t GetTotalPCCoverage(); @@ -92,7 +98,7 @@ void UpdateFeatureSet(size_t CurrentElementIdx, size_t CurrentElementSize); void PrintFeatureSet(); - void PrintModuleInfo(); + void PrintModuleSummary(); void PrintCoverage(bool PrintAllCounters); @@ -110,6 +116,10 @@ void RecordInitialStack(); uintptr_t GetMaxStackOffset() const; + // Step function, grows similar to 8 * Log_2(A). + static size_t StackDepthStepFunction(uintptr_t A); + static const size_t kMaxStackDepthFeature = 512; + template void ForEachObservedPC(CallBack CB) { for (auto PC : ObservedPCs) @@ -128,6 +138,13 @@ static uintptr_t GetNextInstructionPc(uintptr_t PC); bool PcIsFuncEntry(const PCTableEntry *TE) { return TE->PCFlags & 1; } + const PCTableEntry *PCTableEntryByModule(uint64_t Hash, uintptr_t Offset); + bool ModuleInfoByFeature(size_t Feature, ModuleInfo *Info); + bool ModuleInfoByIdx(uintptr_t Idx, ModuleInfo *Info); + + // Calculate hash of the relative PC offsets and flags. + static uint64_t HashPCs(const uintptr_t *Start, const uintptr_t *End); + private: bool UseCounters = false; uint32_t UseValueProfileMask = false; @@ -140,14 +157,13 @@ struct Module { struct Region { uint8_t *Start, *Stop; - bool Enabled; - bool OneFullPage; + size_t Size() const { return Stop - Start; } }; Region *Regions; size_t NumRegions; - uint8_t *Start() { return Regions[0].Start; } - uint8_t *Stop() { return Regions[NumRegions - 1].Stop; } - size_t Size() { return Stop() - Start(); } + uint8_t *Start() const { return Regions[0].Start; } + uint8_t *Stop() const { return Regions[NumRegions - 1].Stop; } + size_t Size() const { return Stop() - Start(); } size_t Idx(uint8_t *P) { assert(P >= Start() && P < Stop()); return P - Start(); @@ -165,7 +181,19 @@ CB(Modules[m].Regions[r]); } - struct { const PCTableEntry *Start, *Stop; } ModulePCTable[4096]; + // If |Info| represents a module other than the last one in the array, this + // method will update |Info| to represent the next module in the array and + // return true. Otherwise, if |Info| is the last module or invalid, it will + // update |Info| to represent the first module in the array and return false. + // A series of calls to |NextModuleInfo| can be used to iterate over the + // |Modules| array. + bool NextModuleInfo(ModuleInfo *Info); + + struct { + const PCTableEntry *Start, *Stop; + uint64_t Hash; + size_t Size() const { return Stop - Start; } + } ModulePCTable[4096]; size_t NumPCTables; size_t NumPCsInPCTables; @@ -248,14 +276,11 @@ size_t FirstFeature = 0; - for (size_t i = 0; i < NumModules; i++) { - for (size_t r = 0; r < Modules[i].NumRegions; r++) { - if (!Modules[i].Regions[r].Enabled) continue; + for (size_t i = 0; i < NumModules; i++) + for (size_t r = 0; r < Modules[i].NumRegions; r++) FirstFeature += 8 * ForEachNonZeroByte(Modules[i].Regions[r].Start, Modules[i].Regions[r].Stop, FirstFeature, Handle8bitCounter); - } - } FirstFeature += 8 * ForEachNonZeroByte(ExtraCountersBegin(), ExtraCountersEnd(), @@ -268,24 +293,9 @@ FirstFeature += ValueProfileMap.SizeInBits(); } - // Step function, grows similar to 8 * Log_2(A). - auto StackDepthStepFunction = [](size_t A) -> size_t { - if (!A) - return A; - auto Log2 = Log(A); - if (Log2 < 3) - return A; - Log2 -= 3; - return (Log2 + 1) * 8 + ((A >> Log2) & 7); - }; - assert(StackDepthStepFunction(1024) == 64); - assert(StackDepthStepFunction(1024 * 4) == 80); - assert(StackDepthStepFunction(1024 * 1024) == 144); - if (auto MaxStackOffset = GetMaxStackOffset()) { - HandleFeature(static_cast( - FirstFeature + StackDepthStepFunction(MaxStackOffset / 8))); - FirstFeature += StackDepthStepFunction(std::numeric_limits::max()); + HandleFeature(FirstFeature + StackDepthStepFunction(MaxStackOffset / 8)); + FirstFeature += kMaxStackDepthFeature; } return FirstFeature; Index: compiler-rt/lib/fuzzer/FuzzerTracePC.cpp =================================================================== --- compiler-rt/lib/fuzzer/FuzzerTracePC.cpp +++ compiler-rt/lib/fuzzer/FuzzerTracePC.cpp @@ -29,55 +29,60 @@ namespace fuzzer { +const size_t TracePC::kMaxStackDepthFeature; + TracePC TPC; size_t TracePC::GetTotalPCCoverage() { return ObservedPCs.size(); } - void TracePC::HandleInline8bitCountersInit(uint8_t *Start, uint8_t *Stop) { - if (Start == Stop) return; - if (NumModules && - Modules[NumModules - 1].Start() == Start) + if (Start == Stop) return; - assert(NumModules < - sizeof(Modules) / sizeof(Modules[0])); - auto &M = Modules[NumModules++]; - uint8_t *AlignedStart = RoundUpByPage(Start); - uint8_t *AlignedStop = RoundDownByPage(Stop); - size_t NumFullPages = AlignedStop > AlignedStart ? - (AlignedStop - AlignedStart) / PageSize() : 0; - bool NeedFirst = Start < AlignedStart || !NumFullPages; - bool NeedLast = Stop > AlignedStop && AlignedStop >= AlignedStart; - M.NumRegions = NumFullPages + NeedFirst + NeedLast;; - assert(M.NumRegions > 0); + if (NumModules && Modules[NumModules - 1].Start() == Start) + return; + assert(NumModules < sizeof(Modules) / sizeof(Modules[0])); + HandleInline8bitCountersInit(Start, Stop, NumModules++); + NumInline8bitCounters += Modules[NumModules - 1].Size(); +} + +void TracePC::HandleInline8bitCountersInit(uint8_t *Start, uint8_t *Stop, + size_t ModuleOffset) { + assert(Start < Stop); + assert(ModuleOffset < NumModules); + auto &M = Modules[ModuleOffset]; + assert(ModuleOffset == NumModules - 1 || + (M.Size() == static_cast(Stop - Start))); + uint8_t *FirstPage = RoundDownByPage(Start); + M.NumRegions = (RoundUpByPage(Stop) - FirstPage) / PageSize(); M.Regions = new Module::Region[M.NumRegions]; - assert(M.Regions); - size_t R = 0; - if (NeedFirst) - M.Regions[R++] = {Start, std::min(Stop, AlignedStart), true, false}; - for (uint8_t *P = AlignedStart; P < AlignedStop; P += PageSize()) - M.Regions[R++] = {P, P + PageSize(), true, true}; - if (NeedLast) - M.Regions[R++] = {AlignedStop, Stop, true, false}; - assert(R == M.NumRegions); - assert(M.Size() == (size_t)(Stop - Start)); - assert(M.Stop() == Stop); - assert(M.Start() == Start); - NumInline8bitCounters += M.Size(); + uint8_t *Curr = Start; + uint8_t *Next = FirstPage + PageSize(); + for (size_t R = 0; R < M.NumRegions - 1; ++R) { + M.Regions[R] = {Curr, Next}; + Curr = Next; + Next += PageSize(); + } + M.Regions[M.NumRegions - 1] = {Curr, Stop}; } void TracePC::HandlePCsInit(const uintptr_t *Start, const uintptr_t *Stop) { + uint64_t Hash = TracePC::HashPCs(Start, Stop); + HandlePCsInit(Start, Stop, Hash); +} + +void TracePC::HandlePCsInit(const uintptr_t *Start, const uintptr_t *Stop, + uint64_t Hash) { const PCTableEntry *B = reinterpret_cast(Start); const PCTableEntry *E = reinterpret_cast(Stop); if (NumPCTables && ModulePCTable[NumPCTables - 1].Start == B) return; assert(NumPCTables < sizeof(ModulePCTable) / sizeof(ModulePCTable[0])); - ModulePCTable[NumPCTables++] = {B, E}; + ModulePCTable[NumPCTables++] = {B, E, Hash}; NumPCsInPCTables += E - B; } -void TracePC::PrintModuleInfo() { +void TracePC::PrintModuleSummary() { if (NumModules) { Printf("INFO: Loaded %zd modules (%zd inline 8-bit counters): ", NumModules, NumInline8bitCounters); @@ -89,10 +94,9 @@ if (NumPCTables) { Printf("INFO: Loaded %zd PC tables (%zd PCs): ", NumPCTables, NumPCsInPCTables); - for (size_t i = 0; i < NumPCTables; i++) { - Printf("%zd [%p,%p), ", ModulePCTable[i].Stop - ModulePCTable[i].Start, - ModulePCTable[i].Start, ModulePCTable[i].Stop); - } + for (size_t i = 0; i < NumPCTables; i++) + Printf("%zd [%p,%p), ", ModulePCTable[i].Size(), ModulePCTable[i].Start, + ModulePCTable[i].Stop); Printf("\n"); if (NumInline8bitCounters && NumInline8bitCounters != NumPCsInPCTables) { @@ -104,7 +108,8 @@ _Exit(1); } } - if (size_t NumExtraCounters = ExtraCountersEnd() - ExtraCountersBegin()) + size_t NumExtraCounters = ExtraCountersEnd() - ExtraCountersBegin(); + if (NumExtraCounters) Printf("INFO: %zd Extra Counters\n", NumExtraCounters); size_t MaxFeatures = CollectFeatures([](uint32_t) {}); @@ -177,11 +182,9 @@ if (NumInline8bitCounters == NumPCsInPCTables) { for (size_t i = 0; i < NumModules; i++) { auto &M = Modules[i]; - assert(M.Size() == - (size_t)(ModulePCTable[i].Stop - ModulePCTable[i].Start)); + assert(M.Size() == ModulePCTable[i].Size()); for (size_t r = 0; r < M.NumRegions; r++) { auto &R = M.Regions[r]; - if (!R.Enabled) continue; for (uint8_t *P = R.Start; P < R.Stop; P++) if (*P) Observe(&ModulePCTable[i].Start[M.Idx(P)]); @@ -204,7 +207,7 @@ auto &M = ModulePCTable[i]; if (TE >= M.Start && TE < M.Stop) return TotalTEs + TE - M.Start; - TotalTEs += M.Stop - M.Start; + TotalTEs += M.Size(); } assert(0); return 0; @@ -213,13 +216,79 @@ const TracePC::PCTableEntry *TracePC::PCTableEntryByIdx(uintptr_t Idx) { for (size_t i = 0; i < NumPCTables; i++) { auto &M = ModulePCTable[i]; - size_t Size = M.Stop - M.Start; + size_t Size = M.Size(); if (Idx < Size) return &M.Start[Idx]; Idx -= Size; } return nullptr; } +const TracePC::PCTableEntry *TracePC::PCTableEntryByModule(uint64_t Hash, + uintptr_t Offset) { + for (size_t i = 0; i < NumPCTables; i++) { + auto &M = ModulePCTable[i]; + if (Hash == M.Hash && Offset < M.Size()) + return &M.Start[Offset]; + } + return nullptr; +} + +uint64_t TracePC::HashPCs(const uintptr_t *Start, const uintptr_t *End) { + const PCTableEntry *B = reinterpret_cast(Start); + const PCTableEntry *E = reinterpret_cast(End); + assert(B < E); + PCTableEntry Delta = {0, B->PCFlags}; + uint64_t Hash = SimpleFastHash(&Delta, sizeof(Delta)); + for (const PCTableEntry *TE = B + 1; TE < E; ++TE) { + Delta = {TE->PC - B->PC, TE->PCFlags}; + Hash = SimpleFastHash(&Delta, sizeof(Delta), Hash); + } + return Hash; +} + +bool TracePC::NextModuleInfo(ModuleInfo *Info) { + assert(NumModules == NumPCTables); + // Use an invalid offset as an indicator of the first iteration. + if (Info->PCTableOffset < NumPCTables) { + Info->FirstFeature = Info->LastFeature; + Info->FirstIdx = Info->LastIdx; + Info->PCTableOffset++; + } else { + Info->FirstFeature = 0; + Info->FirstIdx = 0; + Info->PCTableOffset = 0; + } + if (Info->PCTableOffset >= NumPCTables) { + Info->Hash = 0; + Info->LastFeature = std::numeric_limits::max(); + Info->LastIdx = std::numeric_limits::max(); + return false; + } + const auto &PCTable = ModulePCTable[Info->PCTableOffset]; + const auto &Counters = Modules[Info->PCTableOffset]; + Info->Hash = PCTable.Hash; + // See CollectCoverage; there are up to 8 features per counter. + Info->LastFeature = Info->FirstFeature + (Counters.Size() * 8); + Info->LastIdx = Info->FirstIdx + PCTable.Size(); + return true; +} + +bool TracePC::ModuleInfoByFeature(size_t Feature, ModuleInfo *Info) { + Info->PCTableOffset = NumPCTables; + while (NextModuleInfo(Info)) + if (Feature < Info->LastFeature) + return true; + return false; +} + +bool TracePC::ModuleInfoByIdx(uintptr_t Idx, ModuleInfo *Info) { + Info->PCTableOffset = NumPCTables; + while (NextModuleInfo(Info)) + if (Idx < Info->LastIdx) + return true; + return false; +} + static std::string GetModuleName(uintptr_t PC) { char ModulePathRaw[4096] = ""; // What's PATH_MAX in portable C++? void *OffsetRaw = nullptr; @@ -256,7 +325,7 @@ return; for (size_t M = 0; M < NumModules; M++) { auto &PCTE = ModulePCTable[M]; - size_t N = PCTE.Stop - PCTE.Start; + size_t N = PCTE.Size(); for (size_t I = 0; I < N; I++) { if (!(PcIsFuncEntry(&PCTE.Start[I]))) continue; // not a function entry. auto Name = DescribePC("%F", GetNextInstructionPc(PCTE.Start[I].PC)); @@ -406,10 +475,8 @@ } void TracePC::ClearInlineCounters() { - IterateCounterRegions([](const Module::Region &R){ - if (R.Enabled) - memset(R.Start, 0, R.Stop - R.Start); - }); + IterateCounterRegions( + [](const Module::Region &R) { memset(R.Start, 0, R.Size()); }); } ATTRIBUTE_NO_SANITIZE_ALL @@ -422,6 +489,16 @@ return InitialStack - __sancov_lowest_stack; // Stack grows down } +size_t TracePC::StackDepthStepFunction(uintptr_t A) { + if (!A) + return A; + uint32_t Log2 = Log(A); + if (Log2 < 3) + return A; + Log2 -= 3; + return (Log2 + 1) * 8 + ((A >> Log2) & 7); +} + void WarnAboutDeprecatedInstrumentation(const char *flag) { // Use RawPrint because Printf cannot be used on Windows before OutputFile is // initialized. Index: compiler-rt/lib/fuzzer/tests/FuzzerTestUtil.h =================================================================== --- compiler-rt/lib/fuzzer/tests/FuzzerTestUtil.h +++ compiler-rt/lib/fuzzer/tests/FuzzerTestUtil.h @@ -69,6 +69,39 @@ TracePC::PCTableEntry *GetPCTableEntries() { return reinterpret_cast(PCs); } + + uint64_t Hash() { return TracePC::HashPCs(PCs, PCsEnd()); } + + // Adds the Module to the |fuzzer::TracePC| if not already added. Returns the + // the index of the first PC for the module. + uintptr_t AddToTPC() { + auto *B = reinterpret_cast(PCs); + uintptr_t Idx = 0; + for (auto *T = TPC.PCTableEntryByIdx(Idx); T; + T = TPC.PCTableEntryByIdx(++Idx)) + if (T == B) + return Idx; + TPC.HandlePCsInit(PCs, PCsEnd()); + TPC.HandleInline8bitCountersInit(Counters, CountersEnd()); + return Idx; + } + + // Adds the expected features for this module. Compare with + // |fuzzer::TracePC::CollectFeatures|. + void CollectFeatures(bool UseCounters, Vector *Features) { + auto FirstFeature = AddToTPC() * 8; + Features->clear(); + for (size_t i = 0; i < NumPCs; ++i) { + if (Counters[i]) { + if (UseCounters) { + Features->push_back(FirstFeature + i * 8 + + CounterToFeature(Counters[i])); + } else { + Features->push_back(FirstFeature + i); + } + } + } + } }; } // namespace fuzzer Index: compiler-rt/lib/fuzzer/tests/FuzzerUnittest.cpp =================================================================== --- compiler-rt/lib/fuzzer/tests/FuzzerUnittest.cpp +++ compiler-rt/lib/fuzzer/tests/FuzzerUnittest.cpp @@ -617,6 +617,15 @@ } } +TEST(TracePC, StackDepthFunction) { + EXPECT_EQ(TracePC::StackDepthStepFunction(1024), 64U); + EXPECT_EQ(TracePC::StackDepthStepFunction(1024 * 4), 80U); + EXPECT_EQ(TracePC::StackDepthStepFunction(1024 * 1024), 144U); + EXPECT_LT(TracePC::StackDepthStepFunction( + std::numeric_limits::max() / 8), + TracePC::kMaxStackDepthFeature); +} + template void EQ(const Vector &A, const Vector &B) { EXPECT_EQ(A, B); } @@ -866,42 +875,83 @@ EXPECT_TRUE(A.Includes(B)); } -template -void CheckEncodedFeatures(FakeModule &M, bool UseCounters) { - // Ensure the module has been added to the TracePC - auto *B = reinterpret_cast(M.PCs); - uintptr_t Idx = 0; - while (true) { - auto *TE = TPC.PCTableEntryByIdx(Idx); - if (!TE) { - TPC.HandlePCsInit(M.PCs, M.PCsEnd()); - TPC.HandleInline8bitCountersInit(M.Counters, M.CountersEnd()); - break; - } - if (TE == B) - break; - ++Idx; - } +// Helper function to test TracePC's module functions using FakeModules of +// various sizes. +template void VerifyModule(FakeModule &M) { + ModuleInfo Info; - // Generate the sequence of relative encoded features for the given module. - Vector Expected; - auto FirstFeature = Idx * 8; + // The given module's PCTableEntries should to an index, which in turn should + // correspond to ModuleInfo that represents the module. + const auto *TE = M.GetPCTableEntries(); + auto Hash = M.Hash(); for (size_t i = 0; i < M.NumPCs; ++i) { - if (M.Counters[i]) { - if (UseCounters) { - Expected.push_back(FirstFeature + i * 8 + - CounterToFeature(M.Counters[i])); - } else { - Expected.push_back(FirstFeature + i); - } - } + auto Idx = TPC.PCTableEntryIdx(&TE[i]); + EXPECT_TRUE(TPC.ModuleInfoByIdx(Idx, &Info)); + EXPECT_EQ(Hash, Info.Hash); + EXPECT_GE(Idx, Info.FirstIdx); + EXPECT_LT(Idx, Info.LastIdx); + } + + // For the given module's hash, |PCTableEntryByModule| with an offset in + // [0, NumPCs) should yield a valid PCTableEntry... + TE = TPC.PCTableEntryByModule(Hash, 0); + ASSERT_NE(TE, nullptr); + EXPECT_EQ(TPC.PCTableEntryIdx(TE), Info.FirstIdx); + + TE = TPC.PCTableEntryByModule(Hash, M.NumPCs - 1); + ASSERT_NE(TE, nullptr); + EXPECT_EQ(TPC.PCTableEntryIdx(TE), Info.LastIdx - 1); + + // ...but a bad hash or offset that should return null. + EXPECT_EQ(TPC.PCTableEntryByModule(~Hash, 0), nullptr); + EXPECT_EQ(TPC.PCTableEntryByModule(Hash, M.NumPCs), nullptr); + + // Each of the given features should map back to this module... + Vector Features; + M.CollectFeatures(/* UseCounters= */ false, &Features); + for (auto F : Features) { + EXPECT_TRUE(TPC.ModuleInfoByFeature(F, &Info)); + EXPECT_EQ(Hash, Info.Hash); + EXPECT_GE(F, Info.FirstFeature); + EXPECT_LT(F, Info.LastFeature); + } + + // ...with or without using counters. + M.CollectFeatures(/* UseCounters= */ true, &Features); + for (auto F : Features) { + EXPECT_GE(F, Info.FirstFeature); + EXPECT_LT(F, Info.LastFeature); } +} + +TEST(TracePC, ModuleInfo) { + TPC.ResetMaps(); + + // Add modules. They will be added to TPC, so make them static to allow the + // test to be run multiple times. + static FakeModule<64> M1; + M1.AddToTPC(); + memset(M1.Counters, 1, M1.NumPCs * sizeof(uint8_t)); + + static FakeModule<512> M2; + M2.AddToTPC(); + memset(M2.Counters, 8, M2.NumPCs * sizeof(uint8_t)); + + static FakeModule<4096> M3; + M3.AddToTPC(); + memset(M3.Counters, 0xff, M3.NumPCs * sizeof(uint8_t)); - // Now get the actual encoded features, and look for the expected ones. - Vector Features, Actual; + VerifyModule(M1); + VerifyModule(M2); + VerifyModule(M3); +} + +template +void CheckEncodedFeatures(FakeModule &M, bool UseCounters) { + Vector Features, Expected, Actual; + M.CollectFeatures(UseCounters, &Expected); TPC.CollectFeatures( [&](size_t F) { Features.push_back(static_cast(F)); }); - EncodeFeatures(Features, &Actual); auto I = std::search(Actual.begin(), Actual.end(), Expected.begin(), Expected.end());