mirror of
https://github.com/RGBCube/serenity
synced 2025-05-31 11:28:12 +00:00
Kernel+Userland: Add ioctl to set process ownership of DisplayConnector
Now that the infrastructure of the Graphics subsystem is quite stable, it is time to try to fix a long-standing problem, which is the lack of locking on display connector devices. Reading and writing from multiple processes to a framebuffer controlled by the display connector is not a huge problem - it could be solved with POSIX locking. The real problem is some program that will try to do ioctl operations on a display connector without the WindowServer being aware of that which can lead to very bad situations, for example - assuming a framebuffer is encoded at a known resolution and certain display timings, but another process changed the ModeSetting of the display connector, leading to inconsistency on the properties of the current ModeSetting. To solve this, there's a new "master" ioctl to take "ownership" and another one to release that ownership of a display connector device. To ensure we will not hold a Process object forever just because it has an ownership over a display connector, we hold it with a weak reference, and if the process is gone, someone else can take an ownership.
This commit is contained in:
parent
1968aba69b
commit
977aa81310
5 changed files with 101 additions and 8 deletions
|
@ -231,11 +231,81 @@ ErrorOr<ByteBuffer> DisplayConnector::get_edid() const
|
|||
return ByteBuffer::copy(m_edid_bytes, sizeof(m_edid_bytes));
|
||||
}
|
||||
|
||||
struct GraphicsIOCtlChecker {
|
||||
unsigned ioctl_number;
|
||||
StringView name;
|
||||
bool requires_ownership { false };
|
||||
};
|
||||
|
||||
static constexpr GraphicsIOCtlChecker s_checkers[] = {
|
||||
{ GRAPHICS_IOCTL_GET_PROPERTIES, "GRAPHICS_IOCTL_GET_PROPERTIES"sv, false },
|
||||
{ GRAPHICS_IOCTL_SET_HEAD_VERTICAL_OFFSET_BUFFER, "GRAPHICS_IOCTL_SET_HEAD_VERTICAL_OFFSET_BUFFER"sv, true },
|
||||
{ GRAPHICS_IOCTL_GET_HEAD_VERTICAL_OFFSET_BUFFER, "GRAPHICS_IOCTL_GET_HEAD_VERTICAL_OFFSET_BUFFER"sv, false },
|
||||
{ GRAPHICS_IOCTL_FLUSH_HEAD_BUFFERS, "GRAPHICS_IOCTL_FLUSH_HEAD_BUFFERS"sv, true },
|
||||
{ GRAPHICS_IOCTL_FLUSH_HEAD, "GRAPHICS_IOCTL_FLUSH_HEAD"sv, true },
|
||||
{ GRAPHICS_IOCTL_SET_HEAD_MODE_SETTING, "GRAPHICS_IOCTL_SET_HEAD_MODE_SETTING"sv, true },
|
||||
{ GRAPHICS_IOCTL_GET_HEAD_MODE_SETTING, "GRAPHICS_IOCTL_GET_HEAD_MODE_SETTING"sv, false },
|
||||
{ GRAPHICS_IOCTL_SET_SAFE_HEAD_MODE_SETTING, "GRAPHICS_IOCTL_SET_SAFE_HEAD_MODE_SETTING"sv, true },
|
||||
{ GRAPHICS_IOCTL_SET_RESPONSIBLE, "GRAPHICS_IOCTL_SET_RESPONSIBLE"sv, false },
|
||||
{ GRAPHICS_IOCTL_UNSET_RESPONSIBLE, "GRAPHICS_IOCTL_UNSET_RESPONSIBLE"sv, true },
|
||||
};
|
||||
|
||||
static StringView ioctl_to_stringview(unsigned request)
|
||||
{
|
||||
for (auto& checker : s_checkers) {
|
||||
if (checker.ioctl_number == request)
|
||||
return checker.name;
|
||||
}
|
||||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
|
||||
bool DisplayConnector::ioctl_requires_ownership(unsigned request) const
|
||||
{
|
||||
for (auto& checker : s_checkers) {
|
||||
if (checker.ioctl_number == request)
|
||||
return checker.requires_ownership;
|
||||
}
|
||||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
|
||||
ErrorOr<void> DisplayConnector::ioctl(OpenFileDescription&, unsigned request, Userspace<void*> arg)
|
||||
{
|
||||
TRY(Process::current().require_promise(Pledge::video));
|
||||
|
||||
// Note: We only allow to set responsibility on a DisplayConnector,
|
||||
// get the current ModeSetting or the hardware framebuffer properties without the
|
||||
// need of having an established responsibility on a DisplayConnector.
|
||||
if (ioctl_requires_ownership(request)) {
|
||||
auto process = m_responsible_process.strong_ref();
|
||||
if (!process || process.ptr() != &Process::current()) {
|
||||
dbgln("DisplayConnector::ioctl: {} requires ownership over the device", ioctl_to_stringview(request));
|
||||
return Error::from_errno(EPERM);
|
||||
}
|
||||
}
|
||||
|
||||
switch (request) {
|
||||
case GRAPHICS_IOCTL_SET_RESPONSIBLE: {
|
||||
SpinlockLocker locker(m_responsible_process_lock);
|
||||
auto process = m_responsible_process.strong_ref();
|
||||
// Note: If there's already a process being responsible, just return an error.
|
||||
// We could technically return 0 if the the requesting process is already
|
||||
// was set to be responsible for this DisplayConnector, but it servicing no
|
||||
// no good purpose and should be considered a bug if this happens anyway.
|
||||
if (process)
|
||||
return Error::from_errno(EPERM);
|
||||
m_responsible_process = Process::current();
|
||||
return {};
|
||||
}
|
||||
case GRAPHICS_IOCTL_UNSET_RESPONSIBLE: {
|
||||
SpinlockLocker locker(m_responsible_process_lock);
|
||||
auto process = m_responsible_process.strong_ref();
|
||||
if (!process)
|
||||
return Error::from_errno(ESRCH);
|
||||
if (process.ptr() != &Process::current())
|
||||
return Error::from_errno(EPERM);
|
||||
m_responsible_process.clear();
|
||||
return {};
|
||||
}
|
||||
case GRAPHICS_IOCTL_GET_PROPERTIES: {
|
||||
auto user_properties = static_ptr_cast<GraphicsConnectorProperties*>(arg);
|
||||
GraphicsConnectorProperties properties {};
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue