Пожалуйста, прочтите новое правило об ограничении использования AI-инструментов. ×

Блог пользователя Wael_Zaiback

Автор Wael_Zaiback, история, 3 месяца назад, По-английски

After 6 hours of debugging, this was the bug :

AC code

RTE code

  • Проголосовать: нравится
  • +134
  • Проголосовать: не нравится

»
3 месяца назад, # |
  Проголосовать: нравится +19 Проголосовать: не нравится

I can give you lessons in implementation to avoid these mistakes later, you can take my number from eyad

»
3 месяца назад, # |
Rev. 2   Проголосовать: нравится +9 Проголосовать: не нравится

RIP

»
3 месяца назад, # |
  Проголосовать: нравится +15 Проголосовать: не нравится

I don't get it, max(a, b) = max(b, a) right ?

»
3 месяца назад, # |
  Проголосовать: нравится 0 Проголосовать: не нравится

brutal

»
3 месяца назад, # |
  Проголосовать: нравится -9 Проголосовать: не нравится

After 6 hours of debugging, the compiler was debugged.

»
3 месяца назад, # |
  Проголосовать: нравится 0 Проголосовать: не нравится

Try it by writing a max function and see if it works.

Or try to change your compiler. You are using C++17 (GCC 7-32), use C++20 (GCC 13-64) might be that this will work.

»
3 месяца назад, # |
Rev. 3   Проголосовать: нравится +48 Проголосовать: не нравится

Here's how to debug the code in 10 minutes

Step 1: Generate a random test case using g++ gen.cpp && ./a.out > input.txt

gen.cpp

Step 2: Compile with sanitizer and run against the test case

> g++ 1983F.cpp -O2 -fsanitize=undefined,address -g && ./a.out < input.txt
=================================================================
==109285==ERROR: AddressSanitizer: heap-use-after-free on address 0x150d908e77f8 at pc 0x56238fbb8a6b bp 0x7ffe5cba52d0 sp 0x7ffe5cba52c0
READ of size 4 at 0x150d908e77f8 thread T0
    #0 0x56238fbb8a6a in int const& std::max<int>(int const&, int const&) /usr/include/c++/14.1.1/bits/stl_algobase.h:262
    #1 0x56238fbb8a6a in Trie::get(int, int, int) /home/qmk/code/cpp/1983F.cpp:57

Step 3: Add bound checking for line 57

res = max(get(x, mid, st[node].child[0]), st[node].child[1] < st.size() ? st[st[node].child[1]].mx : 0);

Step 4: Profit 269512762

»
3 месяца назад, # |
  Проголосовать: нравится +50 Проголосовать: не нравится

Same behavior replicated here. Switching order fixes it. Interestingly, adding optimization level O0 or O2 both give random outputs.

My best guess for why this happens is that std::max evaluates the second half of the max function, at some point stores a reference to a value in the vector of nodes, but then reallocates the vector while resizing in the recursive step, messing with that reference.

This behavior could not be replicated on Godbolt (but I didn't try very hard). I'm also not familiar enough with low-level programming to use an actual debugger, so I can't give a better answer.

  • »
    »
    3 месяца назад, # ^ |
      Проголосовать: нравится 0 Проголосовать: не нравится

    Is this a bug? It seems like a dangerous and easy to forget/not knowing how to fix it detail.

    • »
      »
      »
      3 месяца назад, # ^ |
      Rev. 2   Проголосовать: нравится +16 Проголосовать: не нравится

      I suppose that this is a bug in the sense that it is not what you would expect happening. Also very frustrating if you don't know about it, get destroyed by it, and never understand why.

      But think about it another way, assuming my understanding of the bug is correct. Let's say we want to implement a dynamically sized array. To make it dynamically sized, we will have to reallocate the memory for it every so often. If we want to allow usage of pointers to values inside this array, whenever we reallocate the array, we also have to redirect all the old pointers to the new array, which is a massive pain and may cause unwanted performance issues. Hence, this bug. (I actually discovered this the hard way during my country's TST last year...)

      Combining a slightly aggressive compiler that uses references over values for performance along with the vector reference bug will likely generate this new bug.

»
3 месяца назад, # |
  Проголосовать: нравится 0 Проголосовать: не нравится

UB should be more suspected in this case, since the order of evaluation of parameters should not matter.

See the comment from qmk above.