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.