[Git][wxwidgets/wxwidgets][master] 2 commits: Fix out-of-bounds table read in wxMBConvUTF7::ToWChar()

4 views
Skip to first unread message

Vadim Zeitlin (@_VZ_)

unread,
May 27, 2026, 10:26:20 AM (11 days ago) May 27
to wx-commi...@googlegroups.com

Vadim Zeitlin pushed to branch master at wxWidgets / wxWidgets

Commits:

  • f5c81bc5
    by dxbjavid at 2026-05-27T15:56:54+02:00
    Fix out-of-bounds table read in wxMBConvUTF7::ToWChar()
    
    In wxMBConvUTF7::ToWChar() the value of the byte after '+' was cast to
    "unsigned", which meant that on the platforms with signed bytes values
    greater than 0x80 were sign-extended to a ~4GiB index which was (way)
    out of bounds for a 256-entry table.
    
    Fix the code by casting to "unsigned char", like the cc lookup just
    above already does.
    
    Closes #26517.
    
  • eedc16bc
    by ryancog at 2026-05-27T16:17:21+02:00
    Add wxSizer::DetachItem()
    
    Unlike the existing Detach(), this function doesn't delete wxSizerItem
    itself, allowing to add it back to this or another sizer later.
    
    Closes #26512.
    

6 changed files:

Changes:

  • include/wx/sizer.h
    ... ... @@ -617,6 +617,8 @@ public:
    617 617
         virtual bool Detach( wxSizer *sizer );
    
    618 618
         virtual bool Detach( int index );
    
    619 619
     
    
    620
    +    wxNODISCARD virtual wxSizerItem *DetachItem(size_t index);
    
    621
    +
    
    620 622
         virtual bool Replace( wxWindow *oldwin, wxWindow *newwin, bool recursive = false );
    
    621 623
         virtual bool Replace( wxSizer *oldsz, wxSizer *newsz, bool recursive = false );
    
    622 624
         virtual bool Replace( size_t index, wxSizerItem *newitem );
    

  • interface/wx/sizer.h
    ... ... @@ -440,16 +440,39 @@ public:
    440 440
         virtual bool Detach(wxSizer* sizer);
    
    441 441
     
    
    442 442
         /**
    
    443
    -        Detach an item at position @a index from the sizer without destroying it.
    
    443
    +        Detach the child sizer or window in item at position @a index without
    
    444
    +        destroying the child object.
    
    444 445
     
    
    445 446
             This method does not cause any layout or resizing to take place, call Layout()
    
    446 447
             to update the layout "on screen" after detaching a child from the sizer.
    
    448
    +
    
    447 449
             Returns @true if the child item was found and detached, @false otherwise.
    
    448 450
     
    
    451
    +        Note that the sizer item containing the child sizer or window is
    
    452
    +        deleted by this function, see DetachItem() if you want to prevent this
    
    453
    +        from happening.
    
    454
    +
    
    449 455
             @see Remove()
    
    450 456
         */
    
    451 457
         virtual bool Detach(int index);
    
    452 458
     
    
    459
    +    /**
    
    460
    +        Detach the item at position @a index without destroying it.
    
    461
    +
    
    462
    +        This method does not cause any layout or resizing to take place, call Layout()
    
    463
    +        to update the layout "on screen" after detaching a child from the sizer.
    
    464
    +
    
    465
    +        Returns the item if it was found and detached, @nullptr otherwise.
    
    466
    +
    
    467
    +        The caller takes ownership of the returned pointer, i.e. must either
    
    468
    +        delete it or add it back to this or another sizer later.
    
    469
    +
    
    470
    +        @see Remove(), Add()
    
    471
    +
    
    472
    +        @since 3.3.3
    
    473
    +     */
    
    474
    +    virtual wxSizerItem *DetachItem(size_t index);
    
    475
    +
    
    453 476
         /**
    
    454 477
             Tell the sizer to resize the @a window so that its client area matches the
    
    455 478
             sizer's minimal size (ComputeFittingClientSize() is called to determine it).
    

  • src/common/sizer.cpp
    ... ... @@ -1099,6 +1099,23 @@ bool wxSizer::Detach( int index )
    1099 1099
         return true;
    
    1100 1100
     }
    
    1101 1101
     
    
    1102
    +wxSizerItem *wxSizer::DetachItem(size_t index)
    
    1103
    +{
    
    1104
    +    const auto node = GetChildNode(index);
    
    1105
    +    if ( !node )
    
    1106
    +        return nullptr;
    
    1107
    +
    
    1108
    +    wxSizerItem *item = node->GetData();
    
    1109
    +
    
    1110
    +    wxWindow *window = item->GetWindow();
    
    1111
    +    if ( window != nullptr )
    
    1112
    +        window->SetContainingSizer(nullptr);
    
    1113
    +
    
    1114
    +    m_children.Erase( node );
    
    1115
    +
    
    1116
    +    return item;
    
    1117
    +}
    
    1118
    +
    
    1102 1119
     bool wxSizer::Replace( wxWindow *oldwin, wxWindow *newwin, bool recursive )
    
    1103 1120
     {
    
    1104 1121
         wxASSERT_MSG( oldwin, wxT("Replacing null window") );
    

  • src/common/strconv.cpp
    ... ... @@ -702,7 +702,7 @@ size_t wxMBConvUTF7::ToWChar(wchar_t *dst, size_t dstLen,
    702 702
                         len++;
    
    703 703
                         src++;
    
    704 704
                     }
    
    705
    -                else if ( utf7unb64[(unsigned)*src] == 0xff )
    
    705
    +                else if ( utf7unb64[(unsigned char)*src] == 0xff )
    
    706 706
                     {
    
    707 707
                         // empty encoded chunks are not allowed
    
    708 708
                         if ( !len )
    

  • tests/mbconv/mbconvtest.cpp
    ... ... @@ -1495,4 +1495,9 @@ TEST_CASE("wxMBConv::cMB2WC", "[mbconv][mb2wc]")
    1495 1495
         CHECK( wxConvUTF7.cMB2WC("").length() == 0 );
    
    1496 1496
         CHECK( wxConvUTF7.cMB2WC(wxCharBuffer()).length() == 0 );
    
    1497 1497
         CHECK( wxConvUTF7.cMB2WC("+AKM-").length() == 1 );
    
    1498
    +
    
    1499
    +    // A non-ASCII byte right after the shift character used to be read past
    
    1500
    +    // the end of the base-64 decoding table (signed char index), now it's
    
    1501
    +    // just rejected as an invalid encoded chunk.
    
    1502
    +    CHECK( wxConvUTF7.cMB2WC("+\xc3").length() == 0 );
    
    1498 1503
     }

  • tests/sizers/boxsizer.cpp
    ... ... @@ -448,6 +448,40 @@ TEST_CASE_METHOD(BoxSizerTestCase, "BoxSizer::Replace", "[sizer]")
    448 448
         m_sizer->Replace(0, new wxSizerItem(new wxWindow(m_win, wxID_ANY)));
    
    449 449
     }
    
    450 450
     
    
    451
    +TEST_CASE_METHOD(BoxSizerTestCase, "Sizer::DetachItem", "[sizer]")
    
    452
    +{
    
    453
    +    wxSizerItem *item = nullptr;
    
    454
    +
    
    455
    +    SECTION("Spacer")
    
    456
    +    {
    
    457
    +        item = new wxSizerItem(0, 0);
    
    458
    +    }
    
    459
    +
    
    460
    +    SECTION("Sizer")
    
    461
    +    {
    
    462
    +        item = new wxSizerItem(new wxBoxSizer(wxVERTICAL));
    
    463
    +    }
    
    464
    +
    
    465
    +    SECTION("Window")
    
    466
    +    {
    
    467
    +        item = new wxSizerItem(new wxWindow(m_win, wxID_ANY));
    
    468
    +    }
    
    469
    +
    
    470
    +    m_sizer->Add(item);
    
    471
    +
    
    472
    +    // Test re-insertion
    
    473
    +    auto *detached = m_sizer->DetachItem(0);
    
    474
    +    CHECK( item == detached );
    
    475
    +
    
    476
    +    m_sizer->Insert(0, detached);
    
    477
    +
    
    478
    +    // Test deletion after detach.
    
    479
    +    detached = m_sizer->DetachItem(0);
    
    480
    +    CHECK( item == detached );
    
    481
    +
    
    482
    +    delete detached;
    
    483
    +}
    
    484
    +
    
    451 485
     TEST_CASE("Sizer::CombineFlags", "[sizer]")
    
    452 486
     {
    
    453 487
         // This is a compile-time test which simply verifies that we can combine
    

Reply all
Reply to author
Forward
0 new messages