blob: cd5e6768d91b3b7dce2b6a5763ebbeb0fd7770d5 [file] [log] [blame]
// Copyright 2018 The Chromium OS 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 <arpa/inet.h>
#include <net/if.h>
#include <fuzzer/FuzzedDataProvider.h>
#include <set>
#include "base/logging.h"
#include "permission_broker/port_tracker.h"
namespace permission_broker {
class FakePortTracker : public PortTracker {
public:
FakePortTracker() : PortTracker(nullptr), next_fd_{1} {}
FakePortTracker(const FakePortTracker&) = delete;
FakePortTracker& operator=(const FakePortTracker&) = delete;
~FakePortTracker() override = default;
bool ModifyPortRule(patchpanel::ModifyPortRuleRequest::Operation,
const PortRule& rule) override {
return true;
}
int AddLifelineFd(int dbus_fd) override { return next_fd_++; }
bool DeleteLifelineFd(int fd) override { return true; }
private:
int next_fd_;
};
// Helper struct for keeping track of randomly generated request.
struct FuzzRequest {
PortTracker::PortRuleType type;
Protocol proto;
uint16_t port;
std::string ifname;
};
// Implements a total order for FuzzRequest to insert them in std::set.
bool operator<(const FuzzRequest& lhs, const FuzzRequest& rhs) {
if (lhs.type != rhs.type) {
return lhs.type < rhs.type;
}
if (lhs.proto != rhs.proto) {
return lhs.proto < rhs.proto;
}
if (lhs.port != rhs.port) {
return lhs.port < rhs.port;
}
return lhs.ifname < rhs.ifname;
}
struct FuzzRequest MakeRandomRequest(FuzzedDataProvider& provider) {
return {
.type = provider.ConsumeEnum<PortTracker::PortRuleType>(),
.proto = provider.ConsumeBool() ? ModifyPortRuleRequest::TCP
: ModifyPortRuleRequest::UDP,
.port = provider.ConsumeIntegral<uint16_t>(),
.ifname = provider.ConsumeRandomLengthString(IFNAMSIZ - 1),
};
}
bool AddRule(FuzzedDataProvider& provider,
FakePortTracker& port_tracker,
const FuzzRequest& request) {
int dbus_fd = provider.ConsumeIntegral<int>();
switch (request.type) {
case PortTracker::kUnknownRule:
// Ignore random rule generated with this default type value.
return false;
case PortTracker::kAccessRule:
if (request.proto == ModifyPortRuleRequest::TCP) {
return port_tracker.AllowTcpPortAccess(request.port, request.ifname,
dbus_fd);
} else {
return port_tracker.AllowUdpPortAccess(request.port, request.ifname,
dbus_fd);
}
case PortTracker::kLockdownRule:
if (request.proto == ModifyPortRuleRequest::UDP) {
// Invalid lockdown rule request, ignore.
return false;
}
return port_tracker.LockDownLoopbackTcpPort(request.port, dbus_fd);
case PortTracker::kForwardingRule: {
struct in_addr ip_addr = {.s_addr = provider.ConsumeIntegral<uint32_t>()};
char buffer[INET_ADDRSTRLEN];
memset(buffer, 0, INET_ADDRSTRLEN);
inet_ntop(AF_INET, &ip_addr, buffer, INET_ADDRSTRLEN);
std::string dst_ip = buffer;
uint16_t dst_port = provider.ConsumeIntegral<uint16_t>();
if (request.proto == ModifyPortRuleRequest::TCP) {
return port_tracker.StartTcpPortForwarding(request.port, request.ifname,
dst_ip, dst_port, dbus_fd);
} else {
return port_tracker.StartUdpPortForwarding(request.port, request.ifname,
dst_ip, dst_port, dbus_fd);
}
}
default:
NOTREACHED();
return false;
}
}
bool RemoveRule(FakePortTracker& port_tracker, const FuzzRequest& request) {
switch (request.type) {
case PortTracker::kUnknownRule:
// Ignore random rule generated with this default type value.
return false;
case PortTracker::kAccessRule:
if (request.proto == ModifyPortRuleRequest::TCP) {
return port_tracker.RevokeTcpPortAccess(request.port, request.ifname);
} else {
return port_tracker.RevokeUdpPortAccess(request.port, request.ifname);
}
case PortTracker::kLockdownRule:
if (request.proto == ModifyPortRuleRequest::UDP) {
// Invalid lockdown rule request, ignore.
return false;
}
return port_tracker.ReleaseLoopbackTcpPort(request.port);
case PortTracker::kForwardingRule:
if (request.proto == ModifyPortRuleRequest::TCP) {
return port_tracker.StopTcpPortForwarding(request.port, request.ifname);
} else {
return port_tracker.StopUdpPortForwarding(request.port, request.ifname);
}
default:
NOTREACHED();
return false;
}
}
} // namespace permission_broker
struct Environment {
Environment() { logging::SetMinLogLevel(logging::LOGGING_FATAL); }
};
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
static Environment env;
permission_broker::FakePortTracker port_tracker;
std::set<permission_broker::FuzzRequest> existing_rules;
FuzzedDataProvider provider(data, size);
while (provider.remaining_bytes() > 0) {
float p = provider.ConsumeProbability<float>();
if (p < 0.05) {
// Try removing a non-existing rule 5% of the time.
while (provider.remaining_bytes() > 0) {
permission_broker::FuzzRequest r =
permission_broker::MakeRandomRequest(provider);
if (existing_rules.find(r) != existing_rules.end()) {
// Collision with existing rule, retry.
continue;
}
if (RemoveRule(port_tracker, r)) {
// RemoveRule should fail.
return -1;
}
break;
}
} else if (p < 0.10 && !existing_rules.empty()) {
// Try re-adding an existing rule another 5% of the time.
auto it = std::begin(existing_rules);
std::advance(it, provider.ConsumeIntegralInRange<int>(
0, existing_rules.size() - 1));
if (AddRule(provider, port_tracker, *it)) {
// AddRule should fail.
return -1;
}
} else if (p < existing_rules.size() / 100) {
// Otherwise either generate a new rule or delete an existing rule.
// Deletion attempts are more likely the more rules already exist.
auto it = std::begin(existing_rules);
std::advance(it, provider.ConsumeIntegralInRange<int>(
0, existing_rules.size() - 1));
if (!RemoveRule(port_tracker, *it)) {
// RemoveRule should succeed.
return -1;
}
existing_rules.erase(it);
} else {
permission_broker::FuzzRequest r =
permission_broker::MakeRandomRequest(provider);
// Ignore invalid requests.
if (AddRule(provider, port_tracker, r)) {
existing_rules.insert(r);
}
}
}
return 0;
}