Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

NULL pointer dereference in atomic DRM backend #107

Closed
ziyao233 opened this issue Nov 13, 2024 · 1 comment · Fixed by #108
Closed

NULL pointer dereference in atomic DRM backend #107

ziyao233 opened this issue Nov 13, 2024 · 1 comment · Fixed by #108

Comments

@ziyao233
Copy link
Contributor

Hyprland 0.45.0 together with aquamarine 0.4.4, hyprutils 0.2.4, segfaults on start. The binary is built by clang 19.1.3 and linked against libc++. Hyprland was started with

HYPRLAND_NO_CRASHREPORTER=1 HYPRLAND_TRACE=1 AQ_TRACE=1 Hyprland

and corresponding log is attached1. Some utf8-unclean lines are stripped and they're unrelated to this problem.

Examining the core, it's found that an invalid pointer is dereferenced in Aquamarine::CDRMAtomicRequest::commit()

$ lldb Hyprland --core core
(lldb) target create "Hyprland" --core "core"
Core file '/home/ziyao/packaging/change_aquamarine/core' (x86_64) was loaded.
(lldb) bt
* thread #1, name = 'Hyprland', stop reason = signal SIGSEGV: address not mapped to object
  * frame #0: 0x00007f217f8e426a libaquamarine.so.3`Aquamarine::CDRMAtomicRequest::commit(unsigned int) + 74
    frame #1: 0x00007f217f8e4f34 libaquamarine.so.3`Aquamarine::CDRMAtomicImpl::reset() + 596
    frame #2: 0x00007f217f8c4dd8 libaquamarine.so.3`Aquamarine::CDRMBackend::start() + 24
    frame #3: 0x00007f217f8797b9 libaquamarine.so.3`Aquamarine::CBackend::start() + 217
    frame #4: 0x000055896fce6e1b Hyprland`CCompositor::initServer(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>, int) + 1407
    frame #5: 0x000055896fddfeda Hyprland`main + 1707
(lldb)

More digging shows it is this->conn dereferenced in src/backend/drm/impl/Atomic.cpp2 that contains a NULL address, leading to a segmentation fault,

    if (auto ret = drmModeAtomicCommit(backend->gpu->fd, req, flagssss, &conn->pendingPageFlip); ret) {
        backend->log((flagssss & DRM_MODE_ATOMIC_TEST_ONLY) ? AQ_LOG_DEBUG : AQ_LOG_ERROR,
                     std::format("atomic drm request: failed to commit: {}, flags: {}", strerror(-ret), flagsToStr(flagssss)));
        return false;
    }

It turns out that Aquamarine::CDRMAtomicImpl::reset() doesn't call Aquamarine::CDRMAtomicRequest::commit() in a correct code sequence3,

bool Aquamarine::CDRMAtomicImpl::reset() {
    CDRMAtomicRequest request(backend);


    for (auto const& crtc : backend->crtcs) {
        request.add(crtc->id, crtc->props.mode_id, 0);
        request.add(crtc->id, crtc->props.active, 0);
    }


    for (auto const& conn : backend->connectors) {
        request.add(conn->id, conn->props.crtc_id, 0);
    }


    for (auto const& plane : backend->planes) {
        request.planeProps(plane, nullptr, 0, {});
    }


    return request.commit(DRM_MODE_ATOMIC_ALLOW_MODESET);
}

The member conn of the newly created CDRMAtomicRequest request isn't initialized before commit() is called, which I think should be done through CDRMAtomicRequest::addConnector(). And when problematic CDRMAtomicImpl::reset() is invoked by CDRMBackend::start() during startup4, Hyprland crashes.

I'm not sure why this doesn't cause a problem before, as the piece of code exists since June....

A simple fix (or more like a workaround), is to check whether conn is valid before dereference it.

diff --git a/src/backend/drm/impl/Atomic.cpp b/src/backend/drm/impl/Atomic.cpp
index b031076..254f4f5 100644
--- a/src/backend/drm/impl/Atomic.cpp
+++ b/src/backend/drm/impl/Atomic.cpp
@@ -162,6 +162,10 @@ bool Aquamarine::CDRMAtomicRequest::commit(uint32_t flagssss) {
         return false;
     }
 
+    if (!conn.get()) {
+        return false;
+    }
+
     if (auto ret = drmModeAtomicCommit(backend->gpu->fd, req, flagssss, &conn->pendingPageFlip); ret) {
         backend->log((flagssss & DRM_MODE_ATOMIC_TEST_ONLY) ? AQ_LOG_DEBUG : AQ_LOG_ERROR,
                      std::format("atomic drm request: failed to commit: {}, flags: {}", strerror(-ret), flagsToStr(flagssss)));
@@ -337,6 +341,7 @@ bool Aquamarine::CDRMAtomicImpl::commit(Hyprutils::Memory::CSharedPointer<SDRMCo
 }
 
 bool Aquamarine::CDRMAtomicImpl::reset() {
+
     CDRMAtomicRequest request(backend);
 
     for (auto const& crtc : backend->crtcs) {
@@ -365,4 +370,4 @@ bool Aquamarine::CDRMAtomicImpl::moveCursor(SP<SDRMConnector> connector, bool sk
     }
 
     return true;
-}
\ No newline at end of file
+}

which has been applied on eweOS5 and fixed the crash. It's easy to find that CDRMAtomicImpl::reset() is used only few times in the codebase, so there may be a cleaner solution.

@vaxerski
Copy link
Member

MRs welcome

ziyao233 added a commit to ziyao233/aquamarine that referenced this issue Nov 18, 2024
During startup, CDRMAtomicImpl::reset() may emit a call to method
commit of a CDRMAtomicRequest instance with member "conn" uninitialized,
leading to a segfault. Validate the the pointer before dereference it as
a workaround.

Fixes: 55ac962 ("DRM: preliminary atomic support")
Closes: hyprwm#107
Signed-off-by: Yao Zi <[email protected]>
vaxerski pushed a commit that referenced this issue Nov 18, 2024
…108)

During startup, CDRMAtomicImpl::reset() may emit a call to method
commit of a CDRMAtomicRequest instance with member "conn" uninitialized,
leading to a segfault. Validate the the pointer before dereference it as
a workaround.

Fixes: 55ac962 ("DRM: preliminary atomic support")
Closes: #107

Signed-off-by: Yao Zi <[email protected]>
UjinT34 pushed a commit to UjinT34/aquamarine that referenced this issue Jan 9, 2025
…yprwm#108)

During startup, CDRMAtomicImpl::reset() may emit a call to method
commit of a CDRMAtomicRequest instance with member "conn" uninitialized,
leading to a segfault. Validate the the pointer before dereference it as
a workaround.

Fixes: 55ac962 ("DRM: preliminary atomic support")
Closes: hyprwm#107

Signed-off-by: Yao Zi <[email protected]>
vaxerski added a commit that referenced this issue Jan 10, 2025
* do not set cursor planeProps unless smth changed

* do not skip cursor state flag setting

* drm: scan only cards and not outputs, safeguard against null renderer (#106)

* drm: dont scan card outputs

no need to scan for card[0-9]* and probe card0-eDP etc if they are kms,
bootvga and rendernodes etc. skip the wildcard and remove a unused
size_t variable.

* drm: dont commit state if renderer is missing

setting certain env vars to force egl implentations makes the render
creation fail on the second gpu. instead of causing a coredump,
safeguard commitState and let the monitor turn blank instead.

* props: bump version to 0.5.0

* drm: Validate conn before dereference in CDRMAtomicRequest::commit() (#108)

During startup, CDRMAtomicImpl::reset() may emit a call to method
commit of a CDRMAtomicRequest instance with member "conn" uninitialized,
leading to a segfault. Validate the the pointer before dereference it as
a workaround.

Fixes: 55ac962 ("DRM: preliminary atomic support")
Closes: #107

Signed-off-by: Yao Zi <[email protected]>

* buffer: remove useless forward def

* drm: clearer flow when rescanning connectors (#113)

* consolidates into checkOutput for clearer flow when rescanning connectors

* add error log

* drm: allow multigpu blit from explicit to implicit (#114)

* version: bump to 0.5.1

* flake.lock: update

* flake.nix: gcc13 -> gcc14 (#118)

* drm: udev scan only drm_minor, not connectors (#119)

* drm: log errno set by drmModeAtomicCommit (#120)

* drm: moved null check for renderer to shouldBlit() (#109) (#121)

* drm: only fail INVALID format when enabled (#122)

* flake.lock: update

* drm: only clear buffers when fullReconfigure succeeds (#124)

* core/drm: Add HDR Support (#112)

* version: bump to 0.6.0

* drm: limit udev drm_minor to Linux after a132fa4 (#129)

Not implemented by libudev-devd yet:

[ERR] [AQ] drm: No gpus in scanGPUs.
[ERR] [AQ] drm: Found no gpus to use, cannot continue
[ERR] [AQ] DRM Backend failed

* do not set cursor planeProps unless smth changed

* test separate cursor commits

* do not change hdr blob unless asked to

* rebase

* split atomic commit processing and move hdr & colorspace into modeset

* fix wide color gamut flag & cleanup

* remove unused debug var

---------

Signed-off-by: Yao Zi <[email protected]>
Co-authored-by: Tom Englund <[email protected]>
Co-authored-by: Vaxry <[email protected]>
Co-authored-by: Ziyao <[email protected]>
Co-authored-by: Ikalco <[email protected]>
Co-authored-by: Mihai Fufezan <[email protected]>
Co-authored-by: Austin Horstman <[email protected]>
Co-authored-by: Richard Henninger <[email protected]>
Co-authored-by: Jan Beich <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants