-
Backport
-
Resolution: Other
-
P4
-
None
-
openjdk8u342
-
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.
```
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.
```
- backport of
-
JDK-6899049 G1: Clean up code in ptrQueue.[ch]pp and ptrQueue.inline.hpp
- Resolved
- relates to
-
JDK-8308169 fatal error with `-XX:G1SATBBufferSize=716M ` when using G1GC
- Open
- links to
-
Review openjdk/jdk8u/50