ladybird/Kernel/Devices/KCOVDevice.cpp
Space Meyer 106d4636a4 Revert "Kernel+SystemServer: Make KCOVDevice a character device"
This reverts commit 9dbec601b0.

For KCOV to be performant (or at least not even slower) we need to
mmap the PC buffer from both user and kernel space at the same time.
You can't mmap a character device, so this change didn't make sense.

Plus even if we did invent a new method to exfiltrate the coverage
information out of the kernel, it would be incompatible with existing
kernel fuzzers. That would be kind of annoying. 🙃
2024-04-15 21:16:22 -06:00

132 lines
4.2 KiB
C++

/*
* Copyright (c) 2021, Patrick Meyer <git@the-space.agency>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <AK/Assertions.h>
#include <AK/NonnullOwnPtr.h>
#include <Kernel/API/Ioctl.h>
#include <Kernel/Devices/DeviceManagement.h>
#include <Kernel/Devices/KCOVDevice.h>
#include <Kernel/Devices/KCOVInstance.h>
#include <Kernel/FileSystem/OpenFileDescription.h>
#include <Kernel/Library/Panic.h>
namespace Kernel {
HashMap<ProcessID, KCOVInstance*>* KCOVDevice::proc_instance;
HashMap<ThreadID, KCOVInstance*>* KCOVDevice::thread_instance;
UNMAP_AFTER_INIT NonnullLockRefPtr<KCOVDevice> KCOVDevice::must_create()
{
auto kcov_device_or_error = DeviceManagement::try_create_device<KCOVDevice>();
// FIXME: Find a way to propagate errors
VERIFY(!kcov_device_or_error.is_error());
return kcov_device_or_error.release_value();
}
UNMAP_AFTER_INIT KCOVDevice::KCOVDevice()
: BlockDevice(30, 0)
{
proc_instance = new HashMap<ProcessID, KCOVInstance*>();
thread_instance = new HashMap<ThreadID, KCOVInstance*>();
dbgln("KCOVDevice created");
}
void KCOVDevice::free_thread()
{
auto thread = Thread::current();
auto tid = thread->tid();
auto maybe_kcov_instance = thread_instance->get(tid);
if (!maybe_kcov_instance.has_value())
return;
auto kcov_instance = maybe_kcov_instance.value();
VERIFY(kcov_instance->state() == KCOVInstance::TRACING);
kcov_instance->set_state(KCOVInstance::OPENED);
thread_instance->remove(tid);
}
void KCOVDevice::free_process()
{
auto pid = Process::current().pid();
auto maybe_kcov_instance = proc_instance->get(pid);
if (!maybe_kcov_instance.has_value())
return;
auto kcov_instance = maybe_kcov_instance.value();
VERIFY(kcov_instance->state() == KCOVInstance::OPENED);
kcov_instance->set_state(KCOVInstance::UNUSED);
proc_instance->remove(pid);
delete kcov_instance;
}
ErrorOr<NonnullRefPtr<OpenFileDescription>> KCOVDevice::open(int options)
{
auto pid = Process::current().pid();
if (proc_instance->get(pid).has_value())
return EBUSY; // This process already open()ed the kcov device
auto kcov_instance = new KCOVInstance(pid);
kcov_instance->set_state(KCOVInstance::OPENED);
proc_instance->set(pid, kcov_instance);
return Device::open(options);
}
ErrorOr<void> KCOVDevice::ioctl(OpenFileDescription&, unsigned request, Userspace<void*> arg)
{
auto thread = Thread::current();
auto tid = thread->tid();
auto pid = thread->pid();
auto maybe_kcov_instance = proc_instance->get(pid);
if (!maybe_kcov_instance.has_value())
return ENXIO; // This proc hasn't opened the kcov dev yet
auto kcov_instance = maybe_kcov_instance.value();
SpinlockLocker locker(kcov_instance->spinlock());
switch (request) {
case KCOV_SETBUFSIZE:
if (kcov_instance->state() >= KCOVInstance::TRACING)
return EBUSY;
return kcov_instance->buffer_allocate((FlatPtr)arg.unsafe_userspace_ptr());
case KCOV_ENABLE:
if (kcov_instance->state() >= KCOVInstance::TRACING)
return EBUSY;
if (!kcov_instance->has_buffer())
return ENOBUFS;
VERIFY(kcov_instance->state() == KCOVInstance::OPENED);
kcov_instance->set_state(KCOVInstance::TRACING);
thread_instance->set(tid, kcov_instance);
return {};
case KCOV_DISABLE: {
auto maybe_kcov_instance = thread_instance->get(tid);
if (!maybe_kcov_instance.has_value())
return ENOENT;
VERIFY(kcov_instance->state() == KCOVInstance::TRACING);
kcov_instance->set_state(KCOVInstance::OPENED);
thread_instance->remove(tid);
return {};
}
default:
return EINVAL;
}
}
ErrorOr<NonnullLockRefPtr<Memory::VMObject>> KCOVDevice::vmobject_for_mmap(Process& process, Memory::VirtualRange const&, u64&, bool)
{
auto pid = process.pid();
auto maybe_kcov_instance = proc_instance->get(pid);
VERIFY(maybe_kcov_instance.has_value()); // Should happen on fd open()
auto kcov_instance = maybe_kcov_instance.value();
if (!kcov_instance->vmobject())
return ENOBUFS; // mmaped, before KCOV_SETBUFSIZE
return *kcov_instance->vmobject();
}
}