https://bugs.kde.org/show_bug.cgi?id=517549

            Bug ID: 517549
           Summary: Heap-buffer-overflow Konsole::Screen::displayCharacter
                    with Khudawadi Enclosing Mark U+1171E
    Classification: Applications
           Product: konsole
      Version First 25.12.3
       Reported In:
          Platform: Gentoo Packages
                OS: Linux
            Status: REPORTED
          Severity: normal
          Priority: NOR
         Component: general
          Assignee: [email protected]
          Reporter: [email protected]
  Target Milestone: ---

SUMMARY

A heap-buffer-overflow exists in Konsole::Screen::displayCharacter when
processing certain Unicode combining marks (e.g., Khudawadi Enclosing Mark
U+1171E). The logic fails to resize the screen line buffer when a zero-width
character is placed at the current line boundary, leading to an out-of-bounds
write to the heap.

STEPS TO REPRODUCE
1. Build with AddressSanitizer
2. Run this minimal reproducer

python -c 'c=bytes([0xF0,0x91,0x9C,0x9E]);r=[ c.decode()+i for i in "malc"];
print("".join(r))'

Note - you seem to need at least 3 mark+char to trigger. 2 is not enough.

OBSERVED RESULT
Without ASAN - this is SIGSEGV crash.

With ASAN - we can see who wrote what.

==28747==ERROR: AddressSanitizer: heap-buffer-overflow on address
0x7b9e1265c700 at pc 0x7f3e1ce105f1 bp 0x7ffcb6e1a020 sp 0x7ffcb6e1a010
WRITE of size 4 at 0x7b9e1265c700 thread T0
    #0 0x7f3e1ce105f0 in Konsole::Character::setRightHalfOfDoubleWide()
/var/tmp/portage/kde-apps/konsole-25.12.3/work/konsole-25.12.3/src/colorscheme/../characters/Character.h:423
    #1 0x7f3e1ce105f0 in Konsole::Screen::displayCharacter(unsigned int)
/var/tmp/portage/kde-apps/konsole-25.12.3/work/konsole-25.12.3/src/Screen.cpp:1236
    #2 0x7f3e1cefcb17 in Konsole::Vt102Emulation::receiveChars(QList<unsigned
int> const&)
/var/tmp/portage/kde-apps/konsole-25.12.3/work/konsole-25.12.3/src/Vt102Emulation.cpp:705
    #3 0x7f3e1cd3d479 in Konsole::Emulation::receiveData(char const*, int)
/var/tmp/portage/kde-apps/konsole-25.12.3/work/konsole-25.12.3/src/Emulation.cpp:247
    #4 0x7f3e1d1abda5 in Konsole::Session::onReceiveBlock(char const*, int)
/var/tmp/portage/kde-apps/konsole-25.12.3/work/konsole-25.12.3/src/session/Session.cpp:1729
    #5 0x7f3e1d1c2110 in QtPrivate::FunctorCall<std::integer_sequence<unsigned
long, 0ul, 1ul>, QtPrivate::List<char const*, int>, void, void
(Konsole::Session::*)(char const*, int)>::call(void (Konsole::Session::*)(char
const*, int), Konsole::Session*, void**)::{lambda()#1}::operator()() const
/usr/include/qt6/QtCore/qobjectdefs_impl.h:127
    #6 0x7f3e1d1c2110 in void QtPrivate::FunctorCallBase::call_internal<void,
QtPrivate::FunctorCall<std::integer_sequence<unsigned long, 0ul, 1ul>,
QtPrivate::List<char const*, int>, void, void (Konsole::Session::*)(char
const*, int)>::call(void (Konsole::Session::*)(char const*, int),
Konsole::Session*, void**)::{lambda()#1}>(void**,
QtPrivate::FunctorCall<std::integer_sequence<unsigned long, 0ul, 1ul>,
QtPrivate::List<char const*, int>, void, void (Konsole::Session::*)(char
const*, int)>::call(void (Konsole::Session::*)(char const*, int),
Konsole::Session*, void**)::{lambda()#1}&&)
/usr/include/qt6/QtCore/qobjectdefs_impl.h:65
    #7 0x7f3e1d1c2110 in QtPrivate::FunctorCall<std::integer_sequence<unsigned
long, 0ul, 1ul>, QtPrivate::List<char const*, int>, void, void
(Konsole::Session::*)(char const*, int)>::call(void (Konsole::Session::*)(char
const*, int), Konsole::Session*, void**)
/usr/include/qt6/QtCore/qobjectdefs_impl.h:126
    #8 0x7f3e1d1c2110 in void QtPrivate::FunctionPointer<void
(Konsole::Session::*)(char const*, int)>::call<QtPrivate::List<char const*,
int>, void>(void (Konsole::Session::*)(char const*, int), Konsole::Session*,
void**) /usr/include/qt6/QtCore/qobjectdefs_impl.h:174
    #9 0x7f3e1d1c2110 in QtPrivate::QCallableObject<void
(Konsole::Session::*)(char const*, int), QtPrivate::List<char const*, int>,
void>::impl(int, QtPrivate::QSlotObjectBase*, QObject*, void**, bool*)
/usr/include/qt6/QtCore/qobjectdefs_impl.h:545
    #10 0x7f3e19b5d6c1 in QtPrivate::QSlotObjectBase::call(QObject*, void**)
/var/tmp/portage/dev-qt/qtbase-6.10.2/work/qtbase-everywhere-src-6.10.2/src/corelib/kernel/qobjectdefs_impl.h:461
    #11 0x7f3e19b5d6c1 in void doActivate<false>(QObject*, int, void**)
/var/tmp/portage/dev-qt/qtbase-6.10.2/work/qtbase-everywhere-src-6.10.2/src/corelib/kernel/qobject.cpp:4272
    #12 0x7f3e1cdb644e in void QMetaObject::activate<void, char const*,
int>(QObject*, QMetaObject const*, int, void*, char const* const&, int const&)
/usr/include/qt6/QtCore/qobjectdefs.h:319
    #13 0x7f3e1cdb644e in Konsole::Pty::receivedData(char const*, int)
/var/tmp/portage/kde-apps/konsole-25.12.3/work/konsole-25.12.3_build/src/konsoleprivate_autogen/include/moc_Pty.cpp:139
    #14 0x7f3e1cdb665e in Konsole::Pty::dataReceived()
/var/tmp/portage/kde-apps/konsole-25.12.3/work/konsole-25.12.3/src/Pty.cpp:123
    #15 0x7f3e1cdc1d37 in QtPrivate::FunctorCall<std::integer_sequence<unsigned
long>, QtPrivate::List<>, void, void (Konsole::Pty::*)()>::call(void
(Konsole::Pty::*)(), Konsole::Pty*, void**)::{lambda()#1}::operator()() const
/usr/include/qt6/QtCore/qobjectdefs_impl.h:127
    #16 0x7f3e1cdc1d37 in void QtPrivate::FunctorCallBase::call_internal<void,
QtPrivate::FunctorCall<std::integer_sequence<unsigned long>, QtPrivate::List<>,
void, void (Konsole::Pty::*)()>::call(void (Konsole::Pty::*)(), Konsole::Pty*,
void**)::{lambda()#1}>(void**,
QtPrivate::FunctorCall<std::integer_sequence<unsigned long>, QtPrivate::List<>,
void, void (Konsole::Pty::*)()>::call(void (Konsole::Pty::*)(), Konsole::Pty*,
void**)::{lambda()#1}&&) /usr/include/qt6/QtCore/qobjectdefs_impl.h:65
    #17 0x7f3e1cdc1d37 in QtPrivate::FunctorCall<std::integer_sequence<unsigned
long>, QtPrivate::List<>, void, void (Konsole::Pty::*)()>::call(void
(Konsole::Pty::*)(), Konsole::Pty*, void**)
/usr/include/qt6/QtCore/qobjectdefs_impl.h:126
    #18 0x7f3e1cdc1d37 in void QtPrivate::FunctionPointer<void
(Konsole::Pty::*)()>::call<QtPrivate::List<>, void>(void (Konsole::Pty::*)(),
Konsole::Pty*, void**) /usr/include/qt6/QtCore/qobjectdefs_impl.h:174
    #19 0x7f3e1cdc1d37 in QtPrivate::QCallableObject<void (Konsole::Pty::*)(),
QtPrivate::List<>, void>::impl(int, QtPrivate::QSlotObjectBase*, QObject*,
void**, bool*) /usr/include/qt6/QtCore/qobjectdefs_impl.h:545
    #20 0x7f3e19b5d6c1 in QtPrivate::QSlotObjectBase::call(QObject*, void**)
/var/tmp/portage/dev-qt/qtbase-6.10.2/work/qtbase-everywhere-src-6.10.2/src/corelib/kernel/qobjectdefs_impl.h:461
    #21 0x7f3e19b5d6c1 in void doActivate<false>(QObject*, int, void**)
/var/tmp/portage/dev-qt/qtbase-6.10.2/work/qtbase-everywhere-src-6.10.2/src/corelib/kernel/qobject.cpp:4272
    #22 0x7f3e1dd23c7c in KPtyDevicePrivate::_k_canRead()
/var/tmp/portage/kde-frameworks/kpty-6.23.0/work/kpty-6.23.0/src/kptydevice.cpp:291
    #23 0x7f3e19b5d6c1 in QtPrivate::QSlotObjectBase::call(QObject*, void**)
/var/tmp/portage/dev-qt/qtbase-6.10.2/work/qtbase-everywhere-src-6.10.2/src/corelib/kernel/qobjectdefs_impl.h:461
    #24 0x7f3e19b5d6c1 in void doActivate<false>(QObject*, int, void**)
/var/tmp/portage/dev-qt/qtbase-6.10.2/work/qtbase-everywhere-src-6.10.2/src/corelib/kernel/qobject.cpp:4272
    #25 0x7f3e19c03014 in void QMetaObject::activate<void, QSocketDescriptor,
QSocketNotifier::Type, QSocketNotifier::QPrivateSignal>(QObject*, QMetaObject
const*, int, void*, QSocketDescriptor const&, QSocketNotifier::Type const&,
QSocketNotifier::QPrivateSignal const&)
/var/tmp/portage/dev-qt/qtbase-6.10.2/work/qtbase-everywhere-src-6.10.2/src/corelib/kernel/qobjectdefs.h:319
    #26 0x7f3e19c03014 in QSocketNotifier::activated(QSocketDescriptor,
QSocketNotifier::Type, QSocketNotifier::QPrivateSignal)
/var/tmp/portage/dev-qt/qtbase-6.10.2/work/qtbase-everywhere-src-6.10.2_build/src/corelib/Core_autogen/include/moc_qsocketnotifier.cpp:161
    #27 0x7f3e19c03014 in QSocketNotifier::event(QEvent*)
/var/tmp/portage/dev-qt/qtbase-6.10.2/work/qtbase-everywhere-src-6.10.2/src/corelib/kernel/qsocketnotifier.cpp:324
    #28 0x7f3e1b00e1a1 in QApplicationPrivate::notify_helper(QObject*, QEvent*)
/var/tmp/portage/dev-qt/qtbase-6.10.2/work/qtbase-everywhere-src-6.10.2/src/widgets/kernel/qapplication.cpp:3305
    #29 0x7f3e19b77587 in QCoreApplication::notifyInternal2(QObject*, QEvent*)
/var/tmp/portage/dev-qt/qtbase-6.10.2/work/qtbase-everywhere-src-6.10.2/src/corelib/kernel/qcoreapplication.cpp:1109
    #30 0x7f3e199320ef in socketNotifierSourceDispatch
/var/tmp/portage/dev-qt/qtbase-6.10.2/work/qtbase-everywhere-src-6.10.2/src/corelib/kernel/qeventdispatcher_glib.cpp:77
    #31 0x7f3e14905571 in g_main_dispatch ../glib-2.84.4/glib/gmain.c:3398
    #32 0x7f3e14908ae6 in g_main_context_dispatch_unlocked
../glib-2.84.4/glib/gmain.c:4249
    #33 0x7f3e14908ae6 in g_main_context_iterate_unlocked
../glib-2.84.4/glib/gmain.c:4314
    #34 0x7f3e1490927f in g_main_context_iteration
../glib-2.84.4/glib/gmain.c:4379
    #35 0x7f3e198f67d7 in
QEventDispatcherGlib::processEvents(QFlags<QEventLoop::ProcessEventsFlag>)
/var/tmp/portage/dev-qt/qtbase-6.10.2/work/qtbase-everywhere-src-6.10.2/src/corelib/kernel/qeventdispatcher_glib.cpp:399
    #36 0x7f3e19b98ca1 in
QEventLoop::exec(QFlags<QEventLoop::ProcessEventsFlag>)
/var/tmp/portage/dev-qt/qtbase-6.10.2/work/qtbase-everywhere-src-6.10.2/src/corelib/kernel/qeventloop.cpp:186
    #37 0x7f3e19b98e2f in QCoreApplication::exec()
/var/tmp/portage/dev-qt/qtbase-6.10.2/work/qtbase-everywhere-src-6.10.2/src/corelib/kernel/qcoreapplication.cpp:1452
    #38 0x563393192345 in main
/var/tmp/portage/kde-apps/konsole-25.12.3/work/konsole-25.12.3/src/main.cpp:288
    #39 0x7f3e1924d47d in __libc_start_call_main
../sysdeps/nptl/libc_start_call_main.h:59
    #40 0x7f3e1924d59a in __libc_start_main_impl ../csu/libc-start.c:360
    #41 0x56339317ffe4 in _start (/tmp/konsole_25.12.3_ebuild_asan+0x7fe4)
(BuildId: bfc17dbfd4a41e38fd3d7609e2956ff4b522056c)

0x7b9e1265c700 is located 0 bytes after 64-byte region
[0x7b9e1265c6c0,0x7b9e1265c700)
allocated by thread T0 here:
    #0 0x7f3e1d72544b in malloc
/var/tmp/portage/sys-devel/gcc-15.2.1_p20260214/work/gcc-15-20260214/libsanitizer/asan/asan_malloc_linux.cpp:67
    #1 0x7f3e19a8ac5f in allocateHelper
/var/tmp/portage/dev-qt/qtbase-6.10.2/work/qtbase-everywhere-src-6.10.2/src/corelib/tools/qarraydata.cpp:178
    #2 0x7f3e19a8ac5f in QArrayData::allocate(QArrayData**, long long, long
long, long long, QArrayData::AllocationOption)
/var/tmp/portage/dev-qt/qtbase-6.10.2/work/qtbase-everywhere-src-6.10.2/src/corelib/tools/qarraydata.cpp:197

SUMMARY: AddressSanitizer: heap-buffer-overflow
/var/tmp/portage/kde-apps/konsole-25.12.3/work/konsole-25.12.3/src/colorscheme/../characters/Character.h:423
in Konsole::Character::setRightHalfOfDoubleWide()
Shadow bytes around the buggy address:
  0x7b9e1265c480: fd fd fd fd fd fd fd fd fa fa fa fa fd fd fd fd
  0x7b9e1265c500: fd fd fd fd fa fa fa fa fd fd fd fd fd fd fd fd
  0x7b9e1265c580: fa fa fa fa fd fd fd fd fd fd fd fd fa fa fa fa
  0x7b9e1265c600: 00 00 00 00 00 00 00 00 fa fa fa fa fd fd fd fd
  0x7b9e1265c680: fd fd fd fd fa fa fa fa 00 00 00 00 00 00 00 00
=>0x7b9e1265c700:[fa]fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x7b9e1265c780: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x7b9e1265c800: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x7b9e1265c880: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x7b9e1265c900: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x7b9e1265c980: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
Shadow byte legend (one shadow byte represents 8 application bytes):
  Addressable:           00
  Partially addressable: 01 02 03 04 05 06 07 
  Heap left redzone:       fa
  Freed heap region:       fd
  Stack left redzone:      f1
  Stack mid redzone:       f2
  Stack right redzone:     f3
  Stack after return:      f5
  Stack use after scope:   f8
  Global redzone:          f9
  Global init order:       f6
  Poisoned by user:        f7
  Container overflow:      fc
  Array cookie:            ac
  Intra object redzone:    bb
  ASan internal:           fe
  Left alloca redzone:     ca
  Right alloca redzone:    cb
==28747==ABORTING


EXPECTED RESULT

Characters combined and printed.

SOFTWARE/OS VERSIONS
Operating System: Gentoo Linux 2.18
KDE Plasma Version: 6.6.2
KDE Frameworks Version: 6.23.0
Qt Version: 6.10.2
Kernel Version: 6.19.6-gentoo (64-bit)
Graphics Platform: Wayland
Processors: 32 × AMD Ryzen 9 3950X 16-Core Processor
Memory: 64 GiB of RAM (62.7 GiB usable)
Graphics Processor: NVIDIA GeForce RTX 2060

ADDITIONAL INFORMATION

I spent some time in GDB - and it seems that in Screen.cpp, the logic for
handling combining characters and double-wide "right-half" placeholders relies
on the width w of the character. When w=0 (as observed here), the conditional
check for resizing the QList buffer fails:

// Screen.cpp: Line 1232
if (_screenLines[_cuY].size() < _cuX + w) {
    _screenLines[_cuY].resize(_cuX + w);
}
// If _cuX is at the end of the line (e.g., 3) and w=0, 
// 3 < 3 is false. No resize occurs.

Character &ch = _screenLines[_cuY][_cuX]; // Accesses index 3 (4th element)
ch.setRightHalfOfDoubleWide();            // CRASH: Out-of-bounds write

I was able to prevent the crash  as follows - but I lack enough knowledge of
the vagaries of unicode to understand if that is correct, if it is - I suspect
similar is needed around line 1273

if (_screenLines[_cuY].size() <= _cuX + qMax(w, 0)) {
    _screenLines[_cuY].resize(_cuX + qMax(w, 1));
}

-- 
You are receiving this mail because:
You are watching all bug changes.

Reply via email to