Kernel: Introduce ProtectedValue

A protected value is a variable with enforced locking semantics. The
value is protected with a Mutex and can only be accessed through a
Locked object that holds a MutexLocker to said Mutex. Therefore, the
value itself cannot be accessed except through the proper locking
mechanism, which enforces correct locking semantics.

The Locked object has knowledge of shared and exclusive lock types and
will only return const-correct references and pointers. This should
help catch incorrect locking usage where a shared lock is acquired but
the user then modifies the locked value.

This is not a perfect solution because dereferencing the Locked object
returns the value, so the caller could defeat the protected value
semantics once it acquires a lock by keeping a pointer or a reference
to the value around. Then again, this is C++ and we can't protect
against malicious users from within the kernel anyways, but we can
raise the threshold above "didn't pay attention".
This commit is contained in:
Jean-Baptiste Boric 2021-07-18 10:03:10 +02:00 committed by Andreas Kling
parent 39ceefa5dd
commit 75260bff92
Notes: sideshowbarker 2024-07-18 07:20:37 +09:00

View File

@ -0,0 +1,64 @@
/*
* Copyright (c) 2021, the SerenityOS developers.
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <Kernel/Locking/ContendedResource.h>
namespace Kernel {
template<typename T>
class ProtectedValue : private T
, public ContendedResource {
AK_MAKE_NONCOPYABLE(ProtectedValue);
AK_MAKE_NONMOVABLE(ProtectedValue);
protected:
using LockedShared = LockedResource<T const, LockMode::Shared>;
using LockedExclusive = LockedResource<T, LockMode::Exclusive>;
LockedShared lock_shared() const { return LockedShared(this, this->ContendedResource::m_mutex); }
LockedExclusive lock_exclusive() { return LockedExclusive(this, this->ContendedResource::m_mutex); }
public:
using T::T;
ProtectedValue() = default;
template<typename Callback>
decltype(auto) with_shared(Callback callback) const
{
auto lock = lock_shared();
return callback(*lock);
}
template<typename Callback>
decltype(auto) with_exclusive(Callback callback)
{
auto lock = lock_exclusive();
return callback(*lock);
}
template<typename Callback>
void for_each_shared(Callback callback) const
{
with_shared([&](const auto& value) {
for (auto& item : value)
callback(item);
});
}
template<typename Callback>
void for_each_exclusive(Callback callback)
{
with_exclusive([&](auto& value) {
for (auto& item : value)
callback(item);
});
}
};
}