std::nth_element bug引起的crash問題

(1)     源碼:

auto less_compare = [] (const MirroringGroup& mg1, const MirroringGroup& mg2) -> bool {
        return (mg1.usage() < mg2.usage());
};
std::nth_element(mgs->begin(), mgs->begin() + (copy_count - 1), mgs->end(), less_compare);

(2)     問題:

經常發生crashstack如下:

#0 0x00000000004b3807 in MirroringGroup::CopyFrom (this=0x15edf20, from=...) at miuifs/miuistorage-dev/idl/proto/InternalData.pb.cc:6487
#1 0x000000000052bc71 in MirroringGroup::operator= (this=0x15edf20, from=...) at miuifs/miuistorage-dev/idl/proto/InternalData.pb.h:1797
#2 0x000000000052f7cb in std::swap<MirroringGroup> (__a=..., __b=...) at /usr/local/include/c++/4.8.2/bits/move.h:177
#3 0x000000000052e0b0 in std::iter_swap<__gnu_cxx::__normal_iterator<MirroringGroup*, std::vector<MirroringGroup, std::allocator<MirroringGroup> > >, __gnu_cxx::__normal_iterator<MirroringGroup*, std::vector<MirroringGroup, std::allocator<MirroringGroup> > > > (__a=..., __b=...)
    at /usr/local/include/c++/4.8.2/bits/stl_algobase.h:147
#4 0x0000000000604b11 in std::__unguarded_partition<__gnu_cxx::__normal_iterator<MirroringGroup*, std::vector<MirroringGroup> >, MirroringGroup, miuifs::BlockManager::ChooseWritableMirroringGroups(std::vector<MirroringGroup>*, int)::__lambda101>(__gnu_cxx::__normal_iterator<MirroringGroup*, std::vector<MirroringGroup, std::allocator<MirroringGroup> > >, __gnu_cxx::__normal_iterator<MirroringGroup*, std::vector<MirroringGroup, std::allocator<MirroringGroup> > >, const MirroringGroup &, miuifs::BlockManager::__lambda101) (__first=..., __last=..., __pivot=..., __comp=...) at /usr/local/include/c++/4.8.2/bits/stl_algo.h:2270
#5 0x0000000000603c1b in std::__unguarded_partition_pivot<__gnu_cxx::__normal_iterator<MirroringGroup*, std::vector<MirroringGroup> >, miuifs::BlockManager::ChooseWritableMirroringGroups(std::vector<MirroringGroup>*, int)::__lambda101>(__gnu_cxx::__normal_iterator<MirroringGroup*, std::vector<MirroringGroup, std::allocator<MirroringGroup> > >, __gnu_cxx::__normal_iterator<MirroringGroup*, std::vector<MirroringGroup, std::allocator<MirroringGroup> > >, miuifs::BlockManager::__lambda101) (
    __first=..., __last=..., __comp=...) at /usr/local/include/c++/4.8.2/bits/stl_algo.h:2296
#6 0x0000000000603408 in std::__introselect<__gnu_cxx::__normal_iterator<MirroringGroup*, std::vector<MirroringGroup> >, long int, miuifs::BlockManager::ChooseWritableMirroringGroups(std::vector<MirroringGroup>*, int)::__lambda101>(__gnu_cxx::__normal_iterator<MirroringGroup*, std::vector<MirroringGroup, std::allocator<MirroringGroup> > >, __gnu_cxx::__normal_iterator<MirroringGroup*, std::vector<MirroringGroup, std::allocator<MirroringGroup> > >, __gnu_cxx::__normal_iterator<MirroringGroup*, std::vector<MirroringGroup, std::allocator<MirroringGroup> > >, long, miuifs::BlockManager::__lambda101) (__first=..., __nth=..., __last=..., __depth_limit=2, 
    __comp=...) at /usr/local/include/c++/4.8.2/bits/stl_algo.h:2394
#7 0x0000000000602c95 in std::nth_element<__gnu_cxx::__normal_iterator<MirroringGroup*, std::vector<MirroringGroup> >, miuifs::BlockManager::ChooseWritableMirroringGroups(std::vector<MirroringGroup>*, int)::__lambda101>(__gnu_cxx::__normal_iterator<MirroringGroup*, std::vector<MirroringGroup, std::allocator<MirroringGroup> > >, __gnu_cxx::__normal_iterator<MirroringGroup*, std::vector<MirroringGroup, std::allocator<MirroringGroup> > >, __gnu_cxx::__normal_iterator<MirroringGroup*, std::vector<MirroringGroup, std::allocator<MirroringGroup> > >, miuifs::BlockManager::__lambda101) (__first=..., __nth=..., __last=..., __comp=...)
    at /usr/local/include/c++/4.8.2/bits/stl_algo.h:5417
#8 0x000000000060039c in miuifs::BlockManager::ChooseWritableMirroringGroups (this=0x118abe0 <miuifs::BlockManager::Instance()::instance>, mgs=0x7fffeb9f4140, 
    copy_count=2) at miuifs/miuistorage-dev/BlockManager.cc:391
#9 0x00000000005ff9cf in miuifs::BlockManager::NewBlock (this=0x118abe0 <miuifs::BlockManager::Instance()::instance>) at miuifs/miuistorage-dev/BlockManager.cc:331
#10 0x00000000005fed63 in miuifs::BlockManager::AcquireBlock (this=0x118abe0 <miuifs::BlockManager::Instance()::instance>, attribute=...)
    at miuifs/miuistorage-dev/BlockManager.cc:243

(3)     查找問題:

問題一直出現在std::nth_element中,開始沒有想到是STL的問題,一直沒有很好的解決辦法,後來通過閱讀STL源碼找到原因在/usr/local/include/c++/4.8.2/bits/stl_algo.h中:

template<typename _RandomAccessIterator, typename _Compare>
    inline _RandomAccessIterator
    __unguarded_partition_pivot(_RandomAccessIterator __first,
                _RandomAccessIterator __last, _Compare __comp)
    {
      _RandomAccessIterator __mid = __first + (__last - __first) / 2;
      std::__move_median_to_first(__first, __first + 1, __mid, (__last - 2),
                  __comp);
      return std::__unguarded_partition(__first + 1, __last, *__first, __comp);
    }

__move_median_to_first函數的作用是將 __first +1 , __mid, (__last - 2)中中間大小的值和 __first交換。但是卻忽略了__mid,(__last - 2)指向相同迭代器的情況,如果輸入時情況如下:


經過__move_median_to_first之後的結果如下:


此時__first指向了最大的值。然後看std::__unguarded_partition的實現,在2263行__comp(*__first, __pivot))永遠返回true,導致++__first一直執行而訪問了非法內存。

template<typename _RandomAccessIterator, typename _Tp, typename _Compare>
    _RandomAccessIterator
    __unguarded_partition(_RandomAccessIterator __first,
              _RandomAccessIterator __last,
              const _Tp& __pivot, _Compare __comp)
    {
      while (true)
    {
      while (__comp(*__first, __pivot))
        ++__first;
      --__last;
      while (__comp(__pivot, *__last))
        --__last;
      if (!(__first < __last))
        return __first;
      std::iter_swap(__first, __last);
      ++__first;
    }
    }

(4)     解決方法:

通過google找到下面這個鏈接,發現確實是一個STLbug,只能通過升級C++解決了。

https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=732042





發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章