Skip to content

Commit

Permalink
Improve fuzzing coverage (#1227)
Browse files Browse the repository at this point in the history
  • Loading branch information
sashashura authored Nov 10, 2023
1 parent 813f15b commit 6a67cab
Show file tree
Hide file tree
Showing 10 changed files with 751 additions and 61 deletions.
6 changes: 3 additions & 3 deletions Pcap++/src/PcapFileDevice.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -205,13 +205,13 @@ bool SnoopFileReaderDevice::getNextPacket(RawPacket& rawPacket)
if(packetSize > 15000) {
return false;
}
char* packetData = new char[packetSize];
m_snoopFile.read(packetData, packetSize);
std::unique_ptr<char[]> packetData(new char[packetSize]);
m_snoopFile.read(packetData.get(), packetSize);
if(!m_snoopFile) {
return false;
}
timespec ts = { static_cast<time_t>(be32toh(snoop_packet_header.time_sec)), static_cast<long>(be32toh(snoop_packet_header.time_usec)) * 1000 };
if (!rawPacket.setRawData((const uint8_t*)packetData, packetSize, ts, static_cast<LinkLayerType>(m_PcapLinkLayerType)))
if (!rawPacket.setRawData((const uint8_t*)packetData.release(), packetSize, ts, static_cast<LinkLayerType>(m_PcapLinkLayerType)))
{
PCPP_LOG_ERROR("Couldn't set data to raw packet");
return false;
Expand Down
21 changes: 20 additions & 1 deletion Tests/Fuzzers/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,22 @@
add_executable(FuzzTarget FuzzTarget.cpp)

target_link_libraries(FuzzTarget PRIVATE Pcap++ -fsanitize=fuzzer)
target_compile_definitions(FuzzTarget PUBLIC FILE_EXT=".pcap")
target_include_directories(FuzzTarget PRIVATE $<TARGET_PROPERTY:EndianPortable,INTERFACE_INCLUDE_DIRECTORIES>)

add_executable(FuzzTargetNg FuzzTarget.cpp)
target_link_libraries(FuzzTargetNg PRIVATE Pcap++ -fsanitize=fuzzer)
target_compile_definitions(FuzzTargetNg PUBLIC FILE_EXT=".pcapng")
target_include_directories(FuzzTargetNg PRIVATE $<TARGET_PROPERTY:EndianPortable,INTERFACE_INCLUDE_DIRECTORIES>)

add_executable(FuzzTargetSnoop FuzzTarget.cpp)
target_link_libraries(FuzzTargetSnoop PRIVATE Pcap++ -fsanitize=fuzzer)
target_compile_definitions(FuzzTargetSnoop PUBLIC FILE_EXT=".snoop")
target_include_directories(FuzzTargetSnoop PRIVATE $<TARGET_PROPERTY:EndianPortable,INTERFACE_INCLUDE_DIRECTORIES>)

add_executable(FuzzWriter FuzzWriter.cpp)
target_link_libraries(FuzzWriter PRIVATE Pcap++ -fsanitize=fuzzer)
target_compile_definitions(FuzzWriter PUBLIC FILE_EXT=".pcap" NG_WRITER)

add_executable(FuzzWriterNg FuzzWriter.cpp)
target_link_libraries(FuzzWriterNg PRIVATE Pcap++ -fsanitize=fuzzer)
target_compile_definitions(FuzzWriterNg PUBLIC FILE_EXT=".pcapng")
32 changes: 32 additions & 0 deletions Tests/Fuzzers/DumpToFile.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
#ifndef DUMP_TO_FILE_H
#define DUMP_TO_FILE_H

#include <iostream>

// This function is created as PcapPlusPlus doesn't seem to offer a way of
// parsing Pcap files directly from memory
static int dumpDataToPcapFile(const uint8_t *data, size_t size, const char* path)
{
FILE *fd;
int written = 0;

fd = fopen(path, "wb");
if (fd == NULL)
{
std::cerr << "Error opening pcap file for writing\n";
return -1;
}

written = fwrite(data, 1, size, fd);
if (static_cast<size_t>(written) != size)
{
std::cerr << "Error writing pcap file\n";
fclose(fd);
return -1;
}

fclose(fd);
return 0;
}

#endif // DUMP_TO_FILE_H
103 changes: 46 additions & 57 deletions Tests/Fuzzers/FuzzTarget.cpp
Original file line number Diff line number Diff line change
@@ -1,87 +1,76 @@
#include <iostream>

#include <IPv4Layer.h>
#include <Packet.h>
#include <PcapFileDevice.h>
#include <Packet.h>
#include <Logger.h>
#include "DumpToFile.h"
#include "ReadParsedPacket.h"

#include "Logger.h"

#define TMP_FILEPATH "/tmp/fuzz_sample.pcap"
static std::string tmpName;
static std::string tmpFile;

// This function is created as PcapPlusPlus doesn't seem to offer a way of
// parsing Pcap files directly from memory
int dumpDataToPcapFile(const uint8_t *data, size_t size)
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
{
FILE *fd;
int written = 0;
if (tmpName.empty())
tmpName = tmpnam (NULL);

fd = fopen(TMP_FILEPATH, "wb");
if (fd == NULL)
{
std::cerr << "Error opening pcap file for writing\n";
return -1;
}
if (tmpFile.empty())
tmpFile = tmpName + FILE_EXT;

written = fwrite(data, 1, size, fd);
if (static_cast<size_t>(written) != size)
if (dumpDataToPcapFile(data, size, tmpFile.c_str()) != 0)
{
std::cerr << "Error writing pcap file\n";
fclose(fd);
std::cerr << "Can't Dump buffer to the '" << tmpFile << "' file!!!!\n";
return -1;
}

fclose(fd);

return 0;
}
pcpp::Logger::getInstance().suppressLogs();

extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size)
{
if (dumpDataToPcapFile(Data, Size) < 0)
std::unique_ptr<pcpp::IFileReaderDevice> reader(pcpp::IFileReaderDevice::getReader(tmpFile));
if (!reader->open())
{
std::cerr << "Can't Dump buffer to a PCAP file!!!!\n";
return 0;
std::cerr << "Error opening the '" << tmpFile << "' file\n";
return -1;
}

// Disable logs
pcpp::Logger::getInstance().suppressLogs();
pcpp::IPcapDevice::PcapStats stats;
reader->getStatistics(stats);
std::cout << "Read " << stats.packetsRecv << " packets successfully and "
<< stats.packetsDrop << " packets could not be read" << std::endl;

// open a pcap file for reading
pcpp::PcapFileReaderDevice reader(TMP_FILEPATH);
if (!reader.open())
if (auto ngReader = dynamic_cast<pcpp::PcapNgFileReaderDevice*>(reader.get()))
{
std::cerr << "Error opening the pcap file\n";
return 0;
std::cout << "OS is '" << ngReader->getOS() << "'; Hardware is '" << ngReader->getHardware() << "'"
<< "'; CaptureApplication is '" << ngReader->getCaptureApplication()
<< "'; CaptureFileComment is '" << ngReader->getCaptureFileComment()
<< "'" << std::endl;
}

// read the first (and only) packet from the file
pcpp::RawPacket rawPacket;
if (!reader.getNextPacket(rawPacket))
pcpp::RawPacketVector packets;
if (reader->getNextPackets(packets, 1) != 1)
{
std::cerr << "Couldn't read the first packet in the file\n";
return 0;
}

pcpp::RawPacket& rawPacket = *packets.front();
do
{
// parse the raw packet into a parsed packet
pcpp::Packet parsedPacket(&rawPacket);

// verify the packet is IPv4
if (parsedPacket.isPacketOfType(pcpp::IPv4))
// go deeper only for .pcap and .pcapng format
// for .snoop we are only fuzzing the reader
if (0 == strcmp(FILE_EXT, ".pcap") || 0 == strcmp(FILE_EXT, ".pcapng"))
{
// extract source and dest IPs
pcpp::IPv4Address srcIP = parsedPacket.getLayerOfType<pcpp::IPv4Layer>()->getSrcIPv4Address();
pcpp::IPv4Address destIP = parsedPacket.getLayerOfType<pcpp::IPv4Layer>()->getDstIPv4Address();

// print source and dest IPs
std::cout << "Source IP is '" << srcIP.toString() << "'; Dest IP is '" << destIP.toString() << "'"
<< std::endl;
pcpp::Packet parsedPacket(&rawPacket);
parsedPacket.toString();
auto layer = parsedPacket.getFirstLayer();
while (layer != NULL)
{
std::cout << layer->toString() << std::endl;
layer->getHeaderLen();
readParsedPacket(parsedPacket, layer);
layer = layer->getNextLayer();
}
parsedPacket.computeCalculateFields();
}
} while (reader.getNextPacket(rawPacket));

// close the file
reader.close();
} while (reader->getNextPacket(rawPacket));

reader->close();
return 0;
}
79 changes: 79 additions & 0 deletions Tests/Fuzzers/FuzzWriter.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
#include <functional>
#include <Packet.h>
#include <PcapFileDevice.h>

#include "Logger.h"
#include "DumpToFile.h"

static std::string tmpName;
static std::string tmpFile;
static std::string outPcapFile;
static int writes = 0;

extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
{
if (tmpName.empty())
tmpName = tmpnam (NULL);

if (tmpFile.empty())
tmpFile = tmpName + FILE_EXT;

if (dumpDataToPcapFile(data, size, tmpFile.c_str()) != 0)
{
std::cerr << "Can't Dump buffer to the '" << tmpFile << "' file!!!!\n";
return -1;
}

pcpp::Logger::getInstance().suppressLogs();

std::unique_ptr<pcpp::IFileReaderDevice> reader(pcpp::IFileReaderDevice::getReader(tmpFile));
if (!reader->open())
{
std::cerr << "Error opening the '" << tmpFile << "' file\n";
return -1;
}

if (outPcapFile.empty())
#ifdef NG_WRITER
outPcapFile = tmpName + ".pcapng";
#else
outPcapFile = tmpName + ".pcap";
#endif

#ifdef NG_WRITER
pcpp::PcapNgFileWriterDevice pcapWriter(outPcapFile);
#else
pcpp::PcapFileWriterDevice pcapWriter(outPcapFile, pcpp::LINKTYPE_ETHERNET);
#endif
if (writes++ == 10)
{
writes = 1;
remove(outPcapFile.c_str());
}
if (!pcapWriter.open(writes != 1))
{
std::cerr << "Cannot open '" << outPcapFile << "' for writing" << std::endl;
return -1;
}

pcpp::RawPacketVector packets;
if (reader->getNextPackets(packets, 1) != 1)
{
std::cerr << "Couldn't read the first packet in the file\n";
return 0;
}

pcpp::RawPacket& rawPacket = *packets.front();
do
{
pcapWriter.writePacket(rawPacket);
} while (reader->getNextPacket(rawPacket));

pcpp::IPcapDevice::PcapStats stats;
pcapWriter.getStatistics(stats);
std::cout << "Written " << stats.packetsRecv << " packets successfully to pcap writer and "
<< stats.packetsDrop << " packets could not be written" << std::endl;

pcapWriter.close();
return 0;
}
13 changes: 13 additions & 0 deletions Tests/Fuzzers/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
The fuzzers here are used by [oss-fuzz](https://github.com/google/oss-fuzz/tree/master/projects/pcapplusplus).
Oss-fuzz uses its own [tracker for found issues](https://bugs.chromium.org/p/oss-fuzz/issues/list?q=proj%3Apcapplusplus&can=1).

Read more about oss-fuzz and fuzzing [here](https://google.github.io/oss-fuzz/).

Current fuzzers either try to open and parse different pcap, pcapng and snoop files or convert pcap files to pcapng and vice versa.

To analyze the fuzzing coverage (code that is called and that is not by the fuzzers) open https://introspector.oss-fuzz.com/project-profile?project=pcapplusplus and click on the `Code coverage report` link to follow to the latest coverage statistics.
It may also generate it locally using the [instructions](https://google.github.io/oss-fuzz/advanced-topics/code-coverage/).

The fuzzers are built as part of CI, you will be notified you break something.
See [oss-fuzz instructions](https://google.github.io/oss-fuzz/advanced-topics/reproducing/#building-using-docker) how to build locally.
You may also check [how it is built in CI](https://github.com/sashashura/PcapPlusPlus/blob/4d12307aac20d6387956a1eae0b5274a0d3f922b/.cirrus.yml#L71-L83).
Loading

0 comments on commit 6a67cab

Please sign in to comment.