Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-8316278

Fix the indexing method of PtrQueue's buf when a large integer value overflow as setted by `G1SATBBufferSize`

XMLWordPrintable

    • Icon: Backport Backport
    • Resolution: Other
    • Icon: P4 P4
    • None
    • openjdk8u342
    • hotspot
    • gc
    • generic
    • linux

      As reported by issue : https://bugs.openjdk.org/browse/JDK-8308169

      Our analysis process is as follows:

      Run the command: java -XX:G1SATBBufferSize=716m -Xms1024m -Xmx4096m -XX:+UseG1GC Case
      The error stack frame is:
      # V [libjvm.so+0x4170b4] CMSATBBufferClosure::do_buffer(void**, unsigned long)+0x7c

      When adjust G1SATBBufferSize=500m, the error stack frame is:
      # V [libjvm.so+0xb93320] PtrQueue::enqueue_known_active(void*)+0x134

      We found that `byte_index_to_index` is called to determine the subscript of the stored object based on the value of _index when _buf stores an object in function call `PtrQueue::enqueue_known_active`.
      There's an int type casting:
      When G1SATBBufferSize is set to 500m, _index is calculated as 4194303992, and is negative after casted, which is an illegal array subscript;
      When G1SATBBufferSize is set to 712m, _index is calculated as 6006243328, and it is a positive number after type casted, which is a legal array subscript but not correct;
      So the object was stored in the buffer, but at a location that could not be retrieved sequentially. When the function `CMSATBBufferClosure::do_buffer` executes, it reads an incorrect object reference address: 0xf1f1f1f1f1f1f1f1

      We found that this MR(https://bugs.openjdk.org/browse/JDK-6899049) can solve this problem.
      Of course, we also make a cleaner patch as follows.

      Please review, thank you~

      A cleaner patch :
      ```
      diff --git a/hotspot/src/share/vm/gc_implementation/g1/dirtyCardQueue.cpp b/hotspot/src/share/vm/gc_implementation/g1/dirtyCardQueue.cpp
      index 62555c9749..cd11a76958 100644
      --- a/hotspot/src/share/vm/gc_implementation/g1/dirtyCardQueue.cpp
      +++ b/hotspot/src/share/vm/gc_implementation/g1/dirtyCardQueue.cpp
      @@ -52,7 +52,7 @@ bool DirtyCardQueue::apply_closure_to_buffer(CardTableEntryClosure* cl,
                                                    uint worker_i) {
         if (cl == NULL) return true;
         for (size_t i = index; i < sz; i += oopSize) {
      - int ind = byte_index_to_index((int)i);
      + size_t ind = byte_index_to_index(i);
           jbyte* card_ptr = (jbyte*)buf[ind];
           if (card_ptr != NULL) {
             // Set the entry to null, so we don't do it again (via the test
      @@ -294,7 +294,7 @@ void DirtyCardQueueSet::concatenate_logs() {
             void **buf = t->dirty_card_queue().get_buf();
             // We must NULL out the unused entries, then enqueue.
             for (size_t i = 0; i < t->dirty_card_queue().get_index(); i += oopSize) {
      - buf[PtrQueue::byte_index_to_index((int)i)] = NULL;
      + buf[PtrQueue::byte_index_to_index(i)] = NULL;
             }
             enqueue_complete_buffer(dcq.get_buf(), dcq.get_index());
             dcq.reinitialize();
      diff --git a/hotspot/src/share/vm/gc_implementation/g1/ptrQueue.cpp b/hotspot/src/share/vm/gc_implementation/g1/ptrQueue.cpp
      index 2845d51869..157efb0562 100644
      --- a/hotspot/src/share/vm/gc_implementation/g1/ptrQueue.cpp
      +++ b/hotspot/src/share/vm/gc_implementation/g1/ptrQueue.cpp
      @@ -47,7 +47,7 @@ void PtrQueue::flush_impl() {
           } else {
             // We must NULL out the unused entries, then enqueue.
             for (size_t i = 0; i < _index; i += oopSize) {
      - _buf[byte_index_to_index((int)i)] = NULL;
      + _buf[byte_index_to_index(i)] = NULL;
             }
             qset()->enqueue_complete_buffer(_buf);
           }
      @@ -67,7 +67,7 @@ void PtrQueue::enqueue_known_active(void* ptr) {
       
         assert(_index > 0, "postcondition");
         _index -= oopSize;
      - _buf[byte_index_to_index((int)_index)] = ptr;
      + _buf[byte_index_to_index(_index)] = ptr;
         assert(0 <= _index && _index <= _sz, "Invariant.");
       }
       
      diff --git a/hotspot/src/share/vm/gc_implementation/g1/ptrQueue.hpp b/hotspot/src/share/vm/gc_implementation/g1/ptrQueue.hpp
      index 27404f0a9d..606a19b552 100644
      --- a/hotspot/src/share/vm/gc_implementation/g1/ptrQueue.hpp
      +++ b/hotspot/src/share/vm/gc_implementation/g1/ptrQueue.hpp
      @@ -129,9 +129,9 @@ public:
       
         bool is_active() { return _active; }
       
      - static int byte_index_to_index(int ind) {
      - assert((ind % oopSize) == 0, "Invariant.");
      - return ind / oopSize;
      + static size_t byte_index_to_index(size_t ind) {
      + assert((ind % sizeof(void*)) == 0, "Invariant.");
      + return ind / sizeof(void*);
         }
       
         static int index_to_byte_index(int byte_ind) {
      diff --git a/hotspot/src/share/vm/gc_implementation/g1/satbQueue.cpp b/hotspot/src/share/vm/gc_implementation/g1/satbQueue.cpp
      index d423c69ac2..e46cec1638 100644
      --- a/hotspot/src/share/vm/gc_implementation/g1/satbQueue.cpp
      +++ b/hotspot/src/share/vm/gc_implementation/g1/satbQueue.cpp
      @@ -118,7 +118,7 @@ void ObjPtrQueue::filter() {
           assert(i > 0, "we should have at least one more entry to process");
           i -= oopSize;
           debug_only(entries += 1;)
      - void** p = &buf[byte_index_to_index((int) i)];
      + void** p = &buf[byte_index_to_index(i)];
           void* entry = *p;
           // NULL the entry so that unused parts of the buffer contain NULLs
           // at the end. If we are going to retain it we will copy it to its
      @@ -131,7 +131,7 @@ void ObjPtrQueue::filter() {
             new_index -= oopSize;
             assert(new_index >= i,
                    "new_index should never be below i, as we alwaysr compact 'up'");
      - void** new_p = &buf[byte_index_to_index((int) new_index)];
      + void** new_p = &buf[byte_index_to_index(new_index)];
             assert(new_p >= p, "the destination location should never be below "
                    "the source as we always compact 'up'");
             assert(*new_p == NULL,
      @@ -187,8 +187,8 @@ void ObjPtrQueue::apply_closure_and_empty(SATBBufferClosure* cl) {
           assert(_index % sizeof(void*) == 0, "invariant");
           assert(_sz % sizeof(void*) == 0, "invariant");
           assert(_index <= _sz, "invariant");
      - cl->do_buffer(_buf + byte_index_to_index((int)_index),
      - byte_index_to_index((int)(_sz - _index)));
      + cl->do_buffer(_buf + byte_index_to_index(_index),
      + byte_index_to_index(_sz - _index));
           _index = _sz;
         }
       }
      @@ -301,7 +301,7 @@ bool SATBMarkQueueSet::apply_closure_to_completed_buffer(SATBBufferClosure* cl)
           // Filtering can result in non-full completed buffers; see
           // should_enqueue_buffer.
           assert(_sz % sizeof(void*) == 0, "invariant");
      - size_t limit = ObjPtrQueue::byte_index_to_index((int)_sz);
      + size_t limit = ObjPtrQueue::byte_index_to_index(_sz);
           for (size_t i = 0; i < limit; ++i) {
             if (buf[i] != NULL) {
               // Found the end of the block of NULLs; process the remainder.
      ```

            jianyesun Jianye Sun
            jianyesun Jianye Sun
            Votes:
            0 Vote for this issue
            Watchers:
            4 Start watching this issue

              Created:
              Updated:
              Resolved:

                Estimated:
                Original Estimate - 1 week
                1w
                Remaining:
                Remaining Estimate - 1 week
                1w
                Logged:
                Time Spent - Not Specified
                Not Specified