Uploaded image for project: 'Qt'
  1. Qt
  2. QTBUG-123999

Heap buffer overflow in JS Set.delete

    XMLWordPrintable

Details

    • All
    • d3e364548 (dev), 3241bb2e5 (6.7), 42b8085b5 (tqtc/lts-6.5), 49ef3d897 (tqtc/lts-6.2), 6568278d7 (tqtc/lts-5.15), f6c03e330 (dev), 5c8b62f93 (6.7), f80d09e01 (tqtc/lts-6.5)

    Description

      Calling delete on a JS Set object can cause asan builds of Qt to trip with:

      ==89802==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x60c000a092c0 at pc 0x000102e76788 bp 0x00016f627a80 sp 0x00016f627230
      READ of size 128 at 0x60c000a092c0 thread T0
      ==89802==WARNING: invalid path to external symbolizer!
      ==89802==WARNING: Failed to use and restart external symbolizer!
          #0 0x102e76784 in __asan_memmove+0xe40 (/<path_to_llvm_build_sources>/llvm-build/Release+Asserts/lib/clang/18/lib/darwin/libclang_rt.asan_osx_dynamic.dylib:arm64+0x4e784)
          #1 0x1044a77b0 in QV4::ESTable::remove(QV4::Value const&)+0x90 (/<path_to_qt_sources>/QtQml.framework/Versions/A/QtQml:arm64+0x10b7b0)
          #2 0x10453f4b0 in QV4::SetPrototype::method_delete(QV4::FunctionObject const*, QV4::Value const*, QV4::Value const*, int)+0x114 (/<path_to_qt_sources>/QtQml.framework/Versions/A/QtQml:arm64+0x1a34b0)
          #3 0x10456c94c in QV4::Moth::VME::interpret(QV4::JSTypesStackFrame*, QV4::ExecutionEngine*, char const*)+0x141c (/<path_to_qt_sources>/QtQml.framework/Versions/A/QtQml:arm64+0x1d094c)
          #4 0x10456b508 in QV4::Moth::VME::exec(QV4::JSTypesStackFrame*, QV4::ExecutionEngine*)+0x12c (/<path_to_qt_sources>/QtQml.framework/Versions/A/QtQml:arm64+0x1cf508)
          #5 0x1044b3a00 in QV4::doCall(QV4::Function*, QV4::Value const*, QV4::Value const*, int, QV4::ExecutionContext*)+0xc4 (/<path_to_qt_sources>/QtQml.framework/Versions/A/QtQml:arm64+0x117a00)
          #6 0x1044b319c in QV4::Function::call(QObject*, void**, QMetaType const*, int, QV4::ExecutionContext*)+0x208 (/<path_to_qt_sources>/QtQml.framework/Versions/A/QtQml:arm64+0x11719c)
          #7 0x104628cec in QQmlJavaScriptExpression::evaluate(void**, QMetaType const*, int)+0x80 (/<path_to_qt_sources>/QtQml.framework/Versions/A/QtQml:arm64+0x28ccec)
          #8 0x1045ce43c in QQmlBoundSignalExpression::evaluate(void**)+0x21c (/<path_to_qt_sources>/QtQml.framework/Versions/A/QtQml:arm64+0x23243c)
          #9 0x1045ce950 in QQmlBoundSignal_callback(QQmlNotifierEndpoint*, void**)+0x48 (/<path_to_qt_sources>/QtQml.framework/Versions/A/QtQml:arm64+0x232950)
          #10 0x104652b9c in QQmlNotifier::emitNotify(QQmlNotifierEndpoint*, void**)+0x200 (/<path_to_qt_sources>/QtQml.framework/Versions/A/QtQml:arm64+0x2b6b9c)
          #11 0x104cbe644 in void doActivate<false>(QObject*, int, void**)+0x9c (/<path_to_qt_sources>/QtCore.framework/Versions/A/QtCore:arm64+0x10e644)
          #12 0x10465c894 in QQmlObjectCreator::finalize(QQmlInstantiationInterrupt&)+0x654 (/<path_to_qt_sources>/QtQml.framework/Versions/A/QtQml:arm64+0x2c0894)
          #13 0x1045df0b4 in QQmlComponentPrivate::complete(QQmlEnginePrivate*, QQmlComponentPrivate::ConstructionState*)+0x40 (/<path_to_qt_sources>/QtQml.framework/Versions/A/QtQml:arm64+0x2430b4)
          #14 0x1045dcb30 in QQmlComponentPrivate::completeCreate()+0x134 (/<path_to_qt_sources>/QtQml.framework/Versions/A/QtQml:arm64+0x240b30)
          #15 0x1045dee08 in QQmlComponentPrivate::createWithProperties(QObject*, QMap<QString, QVariant> const&, QQmlContext*, QQmlComponentPrivate::CreateBehavior)+0x104 (/<path_to_qt_sources>/QtQml.framework/Versions/A/QtQml:arm64+0x242e08) 

      This is due to some off by one errors in ESTable::remove when memmove is called on the key and value arrays.

      memmove(m_keys + idx, m_keys + idx + 1, (m_size - idx)*sizeof(Value));
      memmove(m_values + idx, m_values + idx + 1, (m_size - idx)*sizeof(Value)); 

      The above is calling memmove to shift all elements in m_keys and m_values to the right of idx to the left by one. However the count parameter one too large. Imagine we want to remove the element in a set with an idx of 0, that means  we are moving `(m_size - 0) * sizeof(Value)` bytes, but the src paramater is not at the start of other array. So memmove will overrun the src buffer by an extra sizeof(Value).

      This could cause a crash if these m_keys and m_values arrays are exactly aligned on the boundary of a memory page.

      In practice you would rarely run into this issue unless the arrays were exactly the right size as m_capacity starts at 8 and is then doubled when needed so only when removing elements from a full ESTable would you run into problems.

       

      Attachments

        For Gerrit Dashboard: QTBUG-123999
        # Subject Branch Project Status CR V

        Activity

          People

            haiku Ollie Dawes
            haiku Ollie Dawes
            Votes:
            0 Vote for this issue
            Watchers:
            1 Start watching this issue

            Dates

              Created:
              Updated:
              Resolved: