mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-11-25 09:00:22 +00:00
Kernel: Add option to build with coverage instrumentation and KCOV
GCC and Clang allow us to inject a call to a function named __sanitizer_cov_trace_pc on every edge. This function has to be defined by us. By noting down the caller in that function we can trace the code we have encountered during execution. Such information is used by coverage guided fuzzers like AFL and LibFuzzer to determine if a new input resulted in a new code path. This makes fuzzing much more effective. Additionally this adds a basic KCOV implementation. KCOV is an API that allows user space to request the kernel to start collecting coverage information for a given user space thread. Furthermore KCOV then exposes the collected program counters to user space via a BlockDevice which can be mmaped from user space. This work is required to add effective support for fuzzing SerenityOS to the Syzkaller syscall fuzzer. :^) :^)
This commit is contained in:
parent
67b3255fe8
commit
83f88df757
Notes:
sideshowbarker
2024-07-18 08:18:45 +09:00
Author: https://github.com/HerrSpace 🔰 Commit: https://github.com/SerenityOS/serenity/commit/83f88df7574 Pull-request: https://github.com/SerenityOS/serenity/pull/8943 Reviewed-by: https://github.com/ADKaster Reviewed-by: https://github.com/IdanHo Reviewed-by: https://github.com/gunnarbeutner
15 changed files with 478 additions and 0 deletions
|
@ -25,6 +25,7 @@ set(SERENITY_ARCH "i686" CACHE STRING "Target architecture for SerenityOS.")
|
|||
# Central location for all custom options used in the Serenity build.
|
||||
option(ENABLE_ADDRESS_SANITIZER "Enable address sanitizer testing in gcc/clang" OFF)
|
||||
option(ENABLE_KERNEL_ADDRESS_SANITIZER "Enable kernel address sanitizer testing in gcc/clang" OFF)
|
||||
option(ENABLE_KERNEL_COVERAGE_COLLECTION "Enable KCOV and kernel coverage instrumentation in gcc/clang" OFF)
|
||||
option(ENABLE_MEMORY_SANITIZER "Enable memory sanitizer testing in gcc/clang" OFF)
|
||||
option(ENABLE_UNDEFINED_SANITIZER "Enable undefined behavior sanitizer testing in gcc/clang" OFF)
|
||||
option(ENABLE_FUZZER_SANITIZER "Enable fuzzer sanitizer testing in clang" OFF)
|
||||
|
@ -115,6 +116,11 @@ if (ENABLE_ALL_DEBUG_FACILITIES)
|
|||
# sure this code continues to build instead of all_debug_macros to avoid
|
||||
# people filing bugs.
|
||||
set(KMALLOC_VERIFY_NO_SPINLOCK_HELD ON)
|
||||
|
||||
# Enables KCOV API and injects kernel coverage instrumentation via
|
||||
# -fsanitize-coverage=trace-pc. Mostly here to ensure that the CI catches
|
||||
# commits breaking this flag.
|
||||
set(ENABLE_KERNEL_COVERAGE_COLLECTION ON)
|
||||
endif()
|
||||
|
||||
if (ENABLE_ALL_THE_DEBUG_MACROS)
|
||||
|
|
|
@ -31,6 +31,7 @@ directory to `Build/i686` and then running `ninja <target>`:
|
|||
|
||||
There are some optional features that can be enabled during compilation that are intended to help with specific types of development work or introduce experimental features. Currently, the following build options are available:
|
||||
- `ENABLE_ADDRESS_SANITIZER` and `ENABLE_KERNEL_ADDRESS_SANITIZER`: builds in runtime checks for memory corruption bugs (like buffer overflows and memory leaks) in Lagom test cases and the kernel, respectively.
|
||||
- `ENABLE_KERNEL_COVERAGE_COLLECTION`: enables the KCOV API and kernel coverage collection instrumentation. Only useful for coverage guided kernel fuzzing.
|
||||
- `ENABLE_MEMORY_SANITIZER`: enables runtime checks for uninitialized memory accesses in Lagom test cases.
|
||||
- `ENABLE_UNDEFINED_SANITIZER`: builds in runtime checks for [undefined behavior](https://en.wikipedia.org/wiki/Undefined_behavior) (like null pointer dereferences and signed integer overflows) in Lagom test cases.
|
||||
- `ENABLE_FUZZER_SANITIZER`: builds [fuzzers](https://en.wikipedia.org/wiki/Fuzzing) for various parts of the system.
|
||||
|
|
|
@ -46,6 +46,8 @@ set(KERNEL_SOURCES
|
|||
Devices/CharacterDevice.cpp
|
||||
Devices/Device.cpp
|
||||
Devices/FullDevice.cpp
|
||||
Devices/KCOVDevice.cpp
|
||||
Devices/KCOVInstance.cpp
|
||||
Devices/MemoryDevice.cpp
|
||||
Devices/NullDevice.cpp
|
||||
Devices/PCISerialDevice.cpp
|
||||
|
@ -76,6 +78,7 @@ set(KERNEL_SOURCES
|
|||
Graphics/VirtIOGPU/GPU.cpp
|
||||
Graphics/VirtIOGPU/GraphicsAdapter.cpp
|
||||
Graphics/VGACompatibleAdapter.cpp
|
||||
SanCov.cpp
|
||||
Storage/Partition/DiskPartition.cpp
|
||||
Storage/Partition/DiskPartitionMetadata.cpp
|
||||
Storage/Partition/EBRPartitionTable.cpp
|
||||
|
@ -364,6 +367,36 @@ endif()
|
|||
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pie -Wl,--no-dynamic-linker")
|
||||
|
||||
# Kernel Coverage (KCOV) is an API to collect and expose program counters of
|
||||
# kernel code that has been run to user space. It's rather slow and likely not
|
||||
# secure to run in production builds. Useful for coverage guided fuzzing.
|
||||
if (ENABLE_KERNEL_COVERAGE_COLLECTION)
|
||||
add_definitions(-DENABLE_KERNEL_COVERAGE_COLLECTION)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize-coverage=trace-pc")
|
||||
set(KCOV_EXCLUDED_SOURCES
|
||||
# Make sure we don't instrument any code called from __sanitizer_cov_trace_pc
|
||||
# otherwise we'll end up with recursive calls to that function.
|
||||
../AK/Format.cpp
|
||||
../AK/StringBuilder.cpp
|
||||
../Kernel/Arch/x86/${KERNEL_ARCH}/Processor.cpp
|
||||
../Kernel/Devices/KCOVDevice.cpp
|
||||
../Kernel/Devices/KCOVInstance.cpp
|
||||
../Kernel/FileSystem/File.cpp
|
||||
../Kernel/FileSystem/FileDescription.cpp
|
||||
../Kernel/Heap/SlabAllocator.cpp
|
||||
../Kernel/init.cpp
|
||||
../Kernel/SanCov.cpp
|
||||
# GCC assumes that the caller saves registers for functions according
|
||||
# to the System V ABI and happily inserts coverage calls into the
|
||||
# function prologue for all functions. This assumption is not true for
|
||||
# interrupt handlers because their calling convention is not compatible
|
||||
# with the System V ABI.
|
||||
../Kernel/Arch/x86/common/Interrupts.cpp
|
||||
../Kernel/Syscall.cpp
|
||||
)
|
||||
set_source_files_properties(${KCOV_EXCLUDED_SOURCES} PROPERTIES COMPILE_FLAGS "-fno-sanitize-coverage=trace-pc")
|
||||
endif()
|
||||
|
||||
# Kernel Undefined Behavior Sanitizer (KUBSAN)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=undefined")
|
||||
|
||||
|
|
152
Kernel/Devices/KCOVDevice.cpp
Normal file
152
Kernel/Devices/KCOVDevice.cpp
Normal file
|
@ -0,0 +1,152 @@
|
|||
/*
|
||||
* 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/Devices/KCOVDevice.h>
|
||||
#include <Kernel/Devices/KCOVInstance.h>
|
||||
#include <Kernel/FileSystem/FileDescription.h>
|
||||
#include <LibC/sys/ioctl_numbers.h>
|
||||
|
||||
#include <Kernel/Panic.h>
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
HashMap<ProcessID, KCOVInstance*>* KCOVDevice::proc_instance;
|
||||
HashMap<ThreadID, KCOVInstance*>* KCOVDevice::thread_instance;
|
||||
|
||||
UNMAP_AFTER_INIT NonnullRefPtr<KCOVDevice> KCOVDevice::must_create()
|
||||
{
|
||||
return adopt_ref(*new KCOVDevice);
|
||||
}
|
||||
|
||||
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->state = KCOVInstance::OPENED;
|
||||
thread_instance->remove(tid);
|
||||
}
|
||||
|
||||
void KCOVDevice::free_process()
|
||||
{
|
||||
auto process = Process::current();
|
||||
auto pid = process->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->state = KCOVInstance::UNUSED;
|
||||
proc_instance->remove(pid);
|
||||
delete kcov_instance;
|
||||
}
|
||||
|
||||
KResultOr<NonnullRefPtr<FileDescription>> KCOVDevice::open(int options)
|
||||
{
|
||||
auto process = Process::current();
|
||||
auto pid = process->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->state = KCOVInstance::OPENED;
|
||||
proc_instance->set(pid, kcov_instance);
|
||||
|
||||
return File::open(options);
|
||||
}
|
||||
|
||||
int KCOVDevice::ioctl(FileDescription&, unsigned request, FlatPtr arg)
|
||||
{
|
||||
int error = 0;
|
||||
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();
|
||||
|
||||
ScopedSpinLock lock(kcov_instance->lock);
|
||||
switch (request) {
|
||||
case KCOV_SETBUFSIZE: {
|
||||
if (kcov_instance->state >= KCOVInstance::TRACING) {
|
||||
error = EBUSY;
|
||||
break;
|
||||
}
|
||||
error = kcov_instance->buffer_allocate(arg);
|
||||
break;
|
||||
}
|
||||
case KCOV_ENABLE: {
|
||||
if (kcov_instance->state >= KCOVInstance::TRACING) {
|
||||
error = EBUSY;
|
||||
break;
|
||||
}
|
||||
if (!kcov_instance->has_buffer()) {
|
||||
error = ENOBUFS;
|
||||
break;
|
||||
}
|
||||
VERIFY(kcov_instance->state == KCOVInstance::OPENED);
|
||||
kcov_instance->state = KCOVInstance::TRACING;
|
||||
thread_instance->set(tid, kcov_instance);
|
||||
break;
|
||||
}
|
||||
case KCOV_DISABLE: {
|
||||
auto maybe_kcov_instance = thread_instance->get(tid);
|
||||
if (!maybe_kcov_instance.has_value()) {
|
||||
error = ENOENT;
|
||||
break;
|
||||
}
|
||||
VERIFY(kcov_instance->state == KCOVInstance::TRACING);
|
||||
kcov_instance->state = KCOVInstance::OPENED;
|
||||
thread_instance->remove(tid);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
error = EINVAL;
|
||||
}
|
||||
};
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
KResultOr<Region*> KCOVDevice::mmap(Process& process, FileDescription&, const Range& range, u64 offset, int prot, bool shared)
|
||||
{
|
||||
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 process.space().allocate_region_with_vmobject(
|
||||
range, *kcov_instance->vmobject, offset, {}, prot, shared);
|
||||
}
|
||||
|
||||
String KCOVDevice::device_name() const
|
||||
{
|
||||
return "kcov"sv;
|
||||
}
|
||||
|
||||
}
|
46
Kernel/Devices/KCOVDevice.h
Normal file
46
Kernel/Devices/KCOVDevice.h
Normal file
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
* Copyright (c) 2021, Patrick Meyer <git@the-space.agency>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Kernel/Devices/BlockDevice.h>
|
||||
#include <Kernel/Devices/KCOVInstance.h>
|
||||
|
||||
namespace Kernel {
|
||||
class KCOVDevice final : public BlockDevice {
|
||||
AK_MAKE_ETERNAL
|
||||
|
||||
public:
|
||||
static HashMap<ProcessID, KCOVInstance*>* proc_instance;
|
||||
static HashMap<ThreadID, KCOVInstance*>* thread_instance;
|
||||
|
||||
static NonnullRefPtr<KCOVDevice> must_create();
|
||||
static void free_thread();
|
||||
static void free_process();
|
||||
|
||||
// ^File
|
||||
KResultOr<Region*> mmap(Process&, FileDescription&, const Range&, u64 offset, int prot, bool shared) override;
|
||||
KResultOr<NonnullRefPtr<FileDescription>> open(int options) override;
|
||||
|
||||
// ^Device
|
||||
virtual mode_t required_mode() const override { return 0660; }
|
||||
virtual String device_name() const override;
|
||||
|
||||
protected:
|
||||
virtual StringView class_name() const override { return "KCOVDevice"; }
|
||||
|
||||
virtual bool can_read(const FileDescription&, size_t) const override final { return true; }
|
||||
virtual bool can_write(const FileDescription&, size_t) const override final { return true; }
|
||||
virtual void start_request(AsyncBlockDeviceRequest& request) override final { request.complete(AsyncDeviceRequest::Failure); }
|
||||
virtual KResultOr<size_t> read(FileDescription&, u64, UserOrKernelBuffer&, size_t) override { return -EINVAL; }
|
||||
virtual KResultOr<size_t> write(FileDescription&, u64, const UserOrKernelBuffer&, size_t) override { return -EINVAL; }
|
||||
virtual int ioctl(FileDescription&, unsigned request, FlatPtr arg) override;
|
||||
|
||||
private:
|
||||
KCOVDevice();
|
||||
};
|
||||
|
||||
}
|
58
Kernel/Devices/KCOVInstance.cpp
Normal file
58
Kernel/Devices/KCOVInstance.cpp
Normal file
|
@ -0,0 +1,58 @@
|
|||
/*
|
||||
* Copyright (c) 2021, Patrick Meyer <git@the-space.agency>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <AK/String.h>
|
||||
#include <Kernel/Devices/KCOVInstance.h>
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
KCOVInstance::KCOVInstance(ProcessID pid)
|
||||
{
|
||||
m_pid = pid;
|
||||
state = UNUSED;
|
||||
}
|
||||
|
||||
int KCOVInstance::buffer_allocate(size_t buffer_size_in_entries)
|
||||
{
|
||||
// first entry contains index of last PC
|
||||
this->m_buffer_size_in_entries = buffer_size_in_entries - 1;
|
||||
this->m_buffer_size_in_bytes = page_round_up(buffer_size_in_entries * KCOV_ENTRY_SIZE);
|
||||
|
||||
// one single vmobject is representing the buffer
|
||||
// - we allocate one kernel region using that vmobject
|
||||
// - when an mmap call comes in, we allocate another userspace region,
|
||||
// backed by the same vmobject
|
||||
this->vmobject = AnonymousVMObject::try_create_with_size(
|
||||
this->m_buffer_size_in_bytes, AllocationStrategy::AllocateNow);
|
||||
if (!this->vmobject)
|
||||
return ENOMEM;
|
||||
|
||||
this->m_kernel_region = MM.allocate_kernel_region_with_vmobject(
|
||||
*this->vmobject, this->m_buffer_size_in_bytes, String::formatted("kcov_{}", this->m_pid),
|
||||
Region::Access::Read | Region::Access::Write);
|
||||
if (!this->m_kernel_region)
|
||||
return ENOMEM;
|
||||
|
||||
this->m_buffer = (u64*)this->m_kernel_region->vaddr().as_ptr();
|
||||
if (!this->has_buffer())
|
||||
return ENOMEM;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void KCOVInstance::buffer_add_pc(u64 pc)
|
||||
{
|
||||
auto idx = (u64)this->m_buffer[0];
|
||||
if (idx >= this->m_buffer_size_in_entries) {
|
||||
// the buffer is already full
|
||||
return;
|
||||
}
|
||||
|
||||
this->m_buffer[idx + 1] = pc;
|
||||
this->m_buffer[0] = idx + 1;
|
||||
}
|
||||
|
||||
}
|
56
Kernel/Devices/KCOVInstance.h
Normal file
56
Kernel/Devices/KCOVInstance.h
Normal file
|
@ -0,0 +1,56 @@
|
|||
/*
|
||||
* Copyright (c) 2021, Patrick Meyer <git@the-space.agency>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Kernel/SpinLock.h>
|
||||
#include <Kernel/VM/AnonymousVMObject.h>
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
// Note: These need to be kept in sync with Userland/Libraries/LibC/sys/kcov.h
|
||||
typedef volatile u64 kcov_pc_t;
|
||||
#define KCOV_ENTRY_SIZE sizeof(kcov_pc_t)
|
||||
|
||||
/*
|
||||
* One KCOVInstance is allocated per process, when the process opens /dev/kcov
|
||||
* for the first time. At this point it is in state OPENED. When a thread in
|
||||
* the same process then uses the KCOV_ENABLE ioctl on the block device, the
|
||||
* instance enters state TRACING.
|
||||
*
|
||||
* A KCOVInstance in state TRACING can return to state OPENED by either the
|
||||
* KCOV_DISABLE ioctl or by killing the thread. A KCOVInstance in state OPENED
|
||||
* can return to state UNUSED only when the process dies. At this point
|
||||
* KCOVDevice::free_process will delete the KCOVInstance.
|
||||
*/
|
||||
class KCOVInstance final {
|
||||
public:
|
||||
explicit KCOVInstance(ProcessID pid);
|
||||
|
||||
int buffer_allocate(size_t buffer_size_in_entries);
|
||||
bool has_buffer() const { return m_buffer != nullptr; }
|
||||
void buffer_add_pc(u64 pc);
|
||||
|
||||
SpinLock<u8> lock;
|
||||
enum {
|
||||
UNUSED = 0,
|
||||
OPENED = 1,
|
||||
TRACING = 2,
|
||||
} state;
|
||||
|
||||
RefPtr<AnonymousVMObject> vmobject;
|
||||
|
||||
private:
|
||||
ProcessID m_pid = { 0 };
|
||||
u64 m_buffer_size_in_entries = { 0 };
|
||||
size_t m_buffer_size_in_bytes = { 0 };
|
||||
kcov_pc_t* m_buffer = { nullptr };
|
||||
|
||||
// Here to ensure it's not garbage collected at the end of open()
|
||||
OwnPtr<Region> m_kernel_region;
|
||||
};
|
||||
|
||||
}
|
|
@ -27,3 +27,4 @@ add_custom_command(
|
|||
)
|
||||
|
||||
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/Prekernel" DESTINATION boot)
|
||||
set_source_files_properties(${SOURCES} PROPERTIES COMPILE_FLAGS "-fno-sanitize-coverage=trace-pc")
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
#include <Kernel/Arch/x86/InterruptDisabler.h>
|
||||
#include <Kernel/CoreDump.h>
|
||||
#include <Kernel/Debug.h>
|
||||
#include <Kernel/Devices/KCOVDevice.h>
|
||||
#include <Kernel/Devices/NullDevice.h>
|
||||
#include <Kernel/FileSystem/Custody.h>
|
||||
#include <Kernel/FileSystem/FileDescription.h>
|
||||
|
@ -685,6 +686,9 @@ void Process::die()
|
|||
}
|
||||
|
||||
kill_all_threads();
|
||||
#ifdef ENABLE_KERNEL_COVERAGE_COLLECTION
|
||||
KCOVDevice::free_process();
|
||||
#endif
|
||||
}
|
||||
|
||||
void Process::terminate_due_to_signal(u8 signal)
|
||||
|
|
38
Kernel/SanCov.cpp
Normal file
38
Kernel/SanCov.cpp
Normal file
|
@ -0,0 +1,38 @@
|
|||
/*
|
||||
* Copyright (c) 2021, Patrick Meyer <git@the-space.agency>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <Kernel/Devices/KCOVDevice.h>
|
||||
#include <Kernel/Process.h>
|
||||
#include <Kernel/Thread.h>
|
||||
|
||||
extern bool g_in_early_boot;
|
||||
|
||||
extern "C" {
|
||||
|
||||
void __sanitizer_cov_trace_pc(void);
|
||||
void __sanitizer_cov_trace_pc(void)
|
||||
{
|
||||
if (g_in_early_boot) [[unlikely]]
|
||||
return;
|
||||
|
||||
if (Processor::current().in_irq()) [[unlikely]] {
|
||||
// Do not trace in interrupts.
|
||||
return;
|
||||
}
|
||||
|
||||
auto thread = Thread::current();
|
||||
auto tid = thread->tid();
|
||||
auto maybe_kcov_instance = KCOVDevice::thread_instance->get(tid);
|
||||
if (!maybe_kcov_instance.has_value()) [[likely]] {
|
||||
// not traced
|
||||
return;
|
||||
}
|
||||
auto kcov_instance = maybe_kcov_instance.value();
|
||||
if (kcov_instance->state < KCOVInstance::TRACING) [[likely]]
|
||||
return;
|
||||
kcov_instance->buffer_add_pc((u64)__builtin_return_address(0));
|
||||
}
|
||||
}
|
|
@ -10,6 +10,7 @@
|
|||
#include <Kernel/Arch/x86/SmapDisabler.h>
|
||||
#include <Kernel/Arch/x86/TrapFrame.h>
|
||||
#include <Kernel/Debug.h>
|
||||
#include <Kernel/Devices/KCOVDevice.h>
|
||||
#include <Kernel/FileSystem/FileDescription.h>
|
||||
#include <Kernel/KSyms.h>
|
||||
#include <Kernel/Panic.h>
|
||||
|
@ -409,6 +410,9 @@ void Thread::exit(void* exit_value)
|
|||
auto* region = process().space().find_region_from_range(m_thread_specific_range.value());
|
||||
process().space().deallocate_region(*region);
|
||||
}
|
||||
#ifdef ENABLE_KERNEL_COVERAGE_COLLECTION
|
||||
KCOVDevice::free_thread();
|
||||
#endif
|
||||
die_if_needed();
|
||||
}
|
||||
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
#include <Kernel/CommandLine.h>
|
||||
#include <Kernel/Devices/FullDevice.h>
|
||||
#include <Kernel/Devices/HID/HIDManagement.h>
|
||||
#include <Kernel/Devices/KCOVDevice.h>
|
||||
#include <Kernel/Devices/MemoryDevice.h>
|
||||
#include <Kernel/Devices/NullDevice.h>
|
||||
#include <Kernel/Devices/PCISerialDevice.h>
|
||||
|
@ -284,6 +285,9 @@ void init_stage2(void*)
|
|||
NetworkingManagement::the().initialize();
|
||||
Syscall::initialize();
|
||||
|
||||
#ifdef ENABLE_KERNEL_COVERAGE_COLLECTION
|
||||
(void)KCOVDevice::must_create().leak_ref();
|
||||
#endif
|
||||
(void)MemoryDevice::must_create().leak_ref();
|
||||
(void)ZeroDevice::must_create().leak_ref();
|
||||
(void)FullDevice::must_create().leak_ref();
|
||||
|
|
|
@ -83,6 +83,9 @@ enum IOCtlNumber {
|
|||
SIOCDARP,
|
||||
FIBMAP,
|
||||
FIONBIO,
|
||||
KCOV_SETBUFSIZE,
|
||||
KCOV_ENABLE,
|
||||
KCOV_DISABLE,
|
||||
};
|
||||
|
||||
#define TIOCGPGRP TIOCGPGRP
|
||||
|
|
13
Userland/Libraries/LibC/sys/kcov.h
Normal file
13
Userland/Libraries/LibC/sys/kcov.h
Normal file
|
@ -0,0 +1,13 @@
|
|||
/*
|
||||
* Copyright (c) 2021, Patrick Meyer <git@the-space.agency>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/Types.h>
|
||||
|
||||
// Note: These need to be kept in sync with Kernel/Devices/KCOVInstance.h
|
||||
typedef volatile u64 kcov_pc_t;
|
||||
#define KCOV_ENTRY_SIZE sizeof(kcov_pc_t)
|
59
Userland/Utilities/kcov-example.cpp
Normal file
59
Userland/Utilities/kcov-example.cpp
Normal file
|
@ -0,0 +1,59 @@
|
|||
/*
|
||||
* Copyright (c) 2021, Patrick Meyer <git@the-space.agency>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/ioctl_numbers.h>
|
||||
#include <sys/kcov.h>
|
||||
#include <sys/mman.h>
|
||||
#include <unistd.h>
|
||||
|
||||
// Note: This program requires serenity to be built with the CMake build option
|
||||
// ENABLE_KERNEL_COVERAGE_COLLECTION
|
||||
int main(void)
|
||||
{
|
||||
constexpr size_t num_entries = 1024 * 100;
|
||||
|
||||
int fd = open("/dev/kcov", O_RDWR);
|
||||
if (fd == -1) {
|
||||
perror("open");
|
||||
return 1;
|
||||
}
|
||||
if (ioctl(fd, KCOV_SETBUFSIZE, num_entries) == -1) {
|
||||
perror("ioctl: KCOV_SETBUFSIZE");
|
||||
return 1;
|
||||
}
|
||||
kcov_pc_t* cover = (kcov_pc_t*)mmap(NULL, num_entries * KCOV_ENTRY_SIZE,
|
||||
PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
|
||||
if (cover == MAP_FAILED) {
|
||||
perror("mmap");
|
||||
return 1;
|
||||
}
|
||||
if (ioctl(fd, KCOV_ENABLE) == -1) {
|
||||
perror("ioctl: KCOV_ENABLE");
|
||||
return 1;
|
||||
}
|
||||
cover[0] = 0;
|
||||
|
||||
// Example syscall so we actually cover some kernel code.
|
||||
getppid();
|
||||
|
||||
if (ioctl(fd, KCOV_DISABLE) == -1) {
|
||||
perror("ioctl: KCOV_DISABLE");
|
||||
return 1;
|
||||
}
|
||||
u64 cov_idx = cover[0];
|
||||
for (size_t idx = 1; idx <= cov_idx; idx++)
|
||||
printf("%p\n", (void*)cover[idx]);
|
||||
if (munmap(const_cast<u64*>(cover), num_entries * KCOV_ENTRY_SIZE) == -1) {
|
||||
perror("munmap");
|
||||
return 1;
|
||||
}
|
||||
close(fd);
|
||||
|
||||
return 0;
|
||||
}
|
Loading…
Reference in a new issue