Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

JFR: Prevent chunk buffer overflows #20504

Merged
merged 1 commit into from
Nov 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
165 changes: 104 additions & 61 deletions runtime/vm/BufferWriter.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,9 @@ class VM_BufferWriter {
U_8 *_buffer;
U_8 *_cursor;
UDATA _size;

U_8 *_bufferEnd;
U_8 *_maxCursor;
bool _overflow;

#if defined(J9VM_ENV_LITTLE_ENDIAN)
static const bool _isLE = true;
Expand Down Expand Up @@ -94,8 +95,26 @@ class VM_BufferWriter {
: _buffer(buffer)
, _cursor(buffer)
, _size(size)
, _bufferEnd(buffer + size)
, _maxCursor(NULL)
, _overflow(false)
{
}

bool
checkBounds(UDATA size)
{
if ((_cursor + size) >= _bufferEnd) {
_overflow = true;
}

return !_overflow;
}

bool
overflowOccurred()
{
return _overflow;
}

U_64
Expand Down Expand Up @@ -123,50 +142,66 @@ class VM_BufferWriter {
}

void
writeU8(U_8 val)
writeU8NoCheck(U_8 val)
{
*_cursor = val;
_cursor += sizeof(U_8);
}

void
writeU8(U_8 val)
{
if (checkBounds(sizeof(U_8))) {
writeU8NoCheck(val);
}
}

void
writeU16(U_16 val)
{
U_16 newVal = val;
if (_isLE) {
newVal = byteSwap(val);
if (checkBounds(sizeof(U_16))) {
U_16 newVal = val;
if (_isLE) {
newVal = byteSwap(val);
}
*(U_16 *)_cursor = newVal;
_cursor += sizeof(U_16);
}
*(U_16 *)_cursor = newVal;
_cursor += sizeof(U_16);
}

void
writeU32(U_32 val)
{
U_32 newVal = val;
if (_isLE) {
newVal = byteSwap(val);
if (checkBounds(sizeof(U_32))) {
U_32 newVal = val;
if (_isLE) {
newVal = byteSwap(val);
}
*(U_32 *)_cursor = newVal;
_cursor += sizeof(U_32);
}
*(U_32 *)_cursor = newVal;
_cursor += sizeof(U_32);
}

void
writeU64(U_64 val)
{
U_64 newVal = val;
if (_isLE) {
newVal = byteSwap(val);
if (checkBounds(sizeof(U_64))) {
U_64 newVal = val;
if (_isLE) {
newVal = byteSwap(val);
}
*(U_64 *)_cursor = newVal;
_cursor += sizeof(U_64);
}
*(U_64 *)_cursor = newVal;
_cursor += sizeof(U_64);
}

void
writeData(U_8 *data, UDATA size)
{
memcpy(_cursor, data, size);
_cursor += size;
if (checkBounds(size)) {
memcpy(_cursor, data, size);
_cursor += size;
}
}

U_8 *
Expand Down Expand Up @@ -203,19 +238,21 @@ class VM_BufferWriter {
void
writeLEB128(U_64 val)
{
U_64 newVal = val;
if (!_isLE) {
newVal = byteSwap(val);
}
do {
U_8 byte = newVal & 0x7F;
newVal >>= 7;

if (newVal > 0) {
byte |= 0x80;
if (checkBounds(9)) {
U_64 newVal = val;
if (!_isLE) {
newVal = byteSwap(val);
}
Comment on lines +243 to 245
Copy link
Contributor

@keithc-ca keithc-ca Nov 6, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think calling byteSwap() is appropriate for any platform here or in any writeLEB* method.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ill address this

writeU8(byte);
} while (newVal > 0);
do {
U_8 byte = newVal & 0x7F;
newVal >>= 7;

if (newVal > 0) {
byte |= 0x80;
}
writeU8NoCheck(byte);
} while (newVal > 0);
}
}

void
Expand All @@ -230,19 +267,21 @@ class VM_BufferWriter {
void
writeLEB128PaddedU72(U_64 val)
{
U_64 newVal = val;
if (!_isLE) {
newVal = byteSwap(val);
if (checkBounds(9)) {
U_64 newVal = val;
if (!_isLE) {
newVal = byteSwap(val);
}
writeU8NoCheck((newVal & 0x7F) | 0x80);
writeU8NoCheck(((newVal >> 7) & 0x7F) | 0x80);
writeU8NoCheck(((newVal >> 14) & 0x7F) | 0x80);
writeU8NoCheck(((newVal >> 21) & 0x7F) | 0x80);
writeU8NoCheck(((newVal >> 28) & 0x7F) | 0x80);
writeU8NoCheck(((newVal >> 35) & 0x7F) | 0x80);
writeU8NoCheck(((newVal >> 42) & 0x7F) | 0x80);
writeU8NoCheck(((newVal >> 49) & 0x7F) | 0x80);
writeU8NoCheck(((newVal >> 56) & 0x7F));
}
writeU8((newVal & 0x7F) | 0x80);
writeU8(((newVal >> 7) & 0x7F) | 0x80);
writeU8(((newVal >> 14) & 0x7F) | 0x80);
writeU8(((newVal >> 21) & 0x7F) | 0x80);
writeU8(((newVal >> 28) & 0x7F) | 0x80);
writeU8(((newVal >> 35) & 0x7F) | 0x80);
writeU8(((newVal >> 42) & 0x7F) | 0x80);
writeU8(((newVal >> 49) & 0x7F) | 0x80);
writeU8(((newVal >> 56) & 0x7F));
}

void
Expand All @@ -257,18 +296,20 @@ class VM_BufferWriter {
void
writeLEB128PaddedU64(U_64 val)
{
U_64 newVal = val;
if (!_isLE) {
newVal = byteSwap(val);
if (checkBounds(sizeof(U_64))) {
U_64 newVal = val;
if (!_isLE) {
newVal = byteSwap(val);
}
writeU8NoCheck((newVal & 0x7F) | 0x80);
writeU8NoCheck(((newVal >> 7) & 0x7F) | 0x80);
writeU8NoCheck(((newVal >> 14) & 0x7F) | 0x80);
writeU8NoCheck(((newVal >> 21) & 0x7F) | 0x80);
writeU8NoCheck(((newVal >> 28) & 0x7F) | 0x80);
writeU8NoCheck(((newVal >> 35) & 0x7F) | 0x80);
writeU8NoCheck(((newVal >> 42) & 0x7F) | 0x80);
writeU8NoCheck(((newVal >> 49) & 0x7F));
}
writeU8((newVal & 0x7F) | 0x80);
writeU8(((newVal >> 7) & 0x7F) | 0x80);
writeU8(((newVal >> 14) & 0x7F) | 0x80);
writeU8(((newVal >> 21) & 0x7F) | 0x80);
writeU8(((newVal >> 28) & 0x7F) | 0x80);
writeU8(((newVal >> 35) & 0x7F) | 0x80);
writeU8(((newVal >> 42) & 0x7F) | 0x80);
writeU8(((newVal >> 49) & 0x7F));
}

void
Expand All @@ -283,14 +324,16 @@ class VM_BufferWriter {
void
writeLEB128PaddedU32(U_32 val)
{
U_64 newVal = val;
if (!_isLE) {
newVal = byteSwap(val);
if (checkBounds(sizeof(U_32))) {
U_64 newVal = val;
if (!_isLE) {
newVal = byteSwap(val);
}
writeU8NoCheck((newVal & 0x7F) | 0x80);
writeU8NoCheck(((newVal >> 7) & 0x7F) | 0x80);
writeU8NoCheck(((newVal >> 14) & 0x7F) | 0x80);
writeU8NoCheck(((newVal >> 21) & 0x7F));
}
writeU8((newVal & 0x7F) | 0x80);
writeU8(((newVal >> 7) & 0x7F) | 0x80);
writeU8(((newVal >> 14) & 0x7F) | 0x80);
writeU8(((newVal >> 21) & 0x7F));
}

static U_32
Expand Down
60 changes: 35 additions & 25 deletions runtime/vm/JFRChunkWriter.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,7 @@ class VM_JFRChunkWriter {
/* conservative sizing for JFR chunk */
static constexpr int STRING_HEADER_LENGTH = sizeof(U_64);
static constexpr int CHECKPOINT_EVENT_HEADER_AND_FOOTER = 68;
static constexpr int STRING_CONSTANT_SIZE = 128;
static constexpr int THREADSTATE_ENTRY_LENGTH = CHECKPOINT_EVENT_HEADER_AND_FOOTER + sizeof(threadStateNames) + (THREADSTATE_COUNT * STRING_HEADER_LENGTH);
static constexpr int CLASS_ENTRY_ENTRY_SIZE = (5 * sizeof(U_64)) + sizeof(U_8);
static constexpr int CLASSLOADER_ENTRY_SIZE = 3 * sizeof(U_64);
Expand All @@ -150,6 +151,7 @@ class VM_JFRChunkWriter {
static constexpr int THREAD_START_EVENT_SIZE = (6 * sizeof(U_64)) + sizeof(U_32);
static constexpr int THREAD_END_EVENT_SIZE = (4 * sizeof(U_64)) + sizeof(U_32);
static constexpr int THREAD_SLEEP_EVENT_SIZE = (7 * sizeof(U_64)) + sizeof(U_32);
static constexpr int MONITOR_WAIT_EVENT_SIZE = (9 * sizeof(U_64)) + sizeof(U_32);
static constexpr int JVM_INFORMATION_EVENT_SIZE = 3000;
static constexpr int PHYSICAL_MEMORY_EVENT_SIZE = (4 * sizeof(U_64)) + sizeof(U_32);
static constexpr int VIRTUALIZATION_INFORMATION_EVENT_SIZE = 50;
Expand Down Expand Up @@ -343,6 +345,10 @@ class VM_JFRChunkWriter {

writeJFRHeader();

if (_bufferWriter->overflowOccurred()) {
_buildResult = OutOfMemory;
}

if (isResultNotOKay()) {
Trc_VM_jfr_ErrorWritingChunk(_currentThread, _buildResult);
goto freeBuffer;
Expand Down Expand Up @@ -592,53 +598,57 @@ class VM_JFRChunkWriter {
UDATA
calculateRequiredBufferSize()
{
UDATA requireBufferSize = _constantPoolTypes.getRequiredBufferSize();
requireBufferSize += JFR_CHUNK_HEADER_SIZE;
UDATA requiredBufferSize = _constantPoolTypes.getRequiredBufferSize();
requiredBufferSize += JFR_CHUNK_HEADER_SIZE;

requiredBufferSize += METADATA_HEADER_SIZE;

requiredBufferSize += _vm->jfrState.metaDataBlobFileSize;

requireBufferSize += METADATA_HEADER_SIZE;
requiredBufferSize += THREADSTATE_ENTRY_LENGTH;

requireBufferSize += _vm->jfrState.metaDataBlobFileSize;
requiredBufferSize += (CHECKPOINT_EVENT_HEADER_AND_FOOTER + (_constantPoolTypes.getClassCount() * CLASS_ENTRY_ENTRY_SIZE));

requireBufferSize += THREADSTATE_ENTRY_LENGTH;
requiredBufferSize += (CHECKPOINT_EVENT_HEADER_AND_FOOTER + (_constantPoolTypes.getClassloaderCount() * CLASSLOADER_ENTRY_SIZE));

requireBufferSize += CHECKPOINT_EVENT_HEADER_AND_FOOTER + (_constantPoolTypes.getClassCount() * CLASS_ENTRY_ENTRY_SIZE);
requiredBufferSize += (CHECKPOINT_EVENT_HEADER_AND_FOOTER + ((_constantPoolTypes.getPackageCount() + _constantPoolTypes.getStringUTF8Count()) * STRING_CONSTANT_SIZE));

requireBufferSize += CHECKPOINT_EVENT_HEADER_AND_FOOTER + (_constantPoolTypes.getClassloaderCount() * CLASSLOADER_ENTRY_SIZE);
requiredBufferSize += (CHECKPOINT_EVENT_HEADER_AND_FOOTER + (_constantPoolTypes.getPackageCount() * PACKAGE_ENTRY_SIZE));

requireBufferSize += CHECKPOINT_EVENT_HEADER_AND_FOOTER + (_constantPoolTypes.getPackageCount() * PACKAGE_ENTRY_SIZE);
requiredBufferSize += (CHECKPOINT_EVENT_HEADER_AND_FOOTER + (_constantPoolTypes.getMethodCount() * METHOD_ENTRY_SIZE));

requireBufferSize += CHECKPOINT_EVENT_HEADER_AND_FOOTER + (_constantPoolTypes.getMethodCount() * METHOD_ENTRY_SIZE);
requiredBufferSize += (CHECKPOINT_EVENT_HEADER_AND_FOOTER + (_constantPoolTypes.getThreadCount() * THREAD_ENTRY_SIZE));

requireBufferSize += CHECKPOINT_EVENT_HEADER_AND_FOOTER + (_constantPoolTypes.getThreadCount() * THREAD_ENTRY_SIZE);
requiredBufferSize += (CHECKPOINT_EVENT_HEADER_AND_FOOTER + (_constantPoolTypes.getThreadGroupCount() * THREADGROUP_ENTRY_SIZE));

requireBufferSize += CHECKPOINT_EVENT_HEADER_AND_FOOTER + (_constantPoolTypes.getThreadGroupCount() * THREADGROUP_ENTRY_SIZE);
requiredBufferSize += (CHECKPOINT_EVENT_HEADER_AND_FOOTER + (_constantPoolTypes.getModuleCount() * MODULE_ENTRY_SIZE));

requireBufferSize += CHECKPOINT_EVENT_HEADER_AND_FOOTER + (_constantPoolTypes.getModuleCount() * MODULE_ENTRY_SIZE);
requiredBufferSize += (CHECKPOINT_EVENT_HEADER_AND_FOOTER + (_constantPoolTypes.getStackTraceCount() * STACKTRACE_ENTRY_SIZE));

requireBufferSize += CHECKPOINT_EVENT_HEADER_AND_FOOTER + (_constantPoolTypes.getStackTraceCount() * STACKTRACE_ENTRY_SIZE);
requiredBufferSize += (CHECKPOINT_EVENT_HEADER_AND_FOOTER + (_constantPoolTypes.getStackFrameCount() * STACKFRAME_ENTRY_SIZE));

requireBufferSize += CHECKPOINT_EVENT_HEADER_AND_FOOTER + (_constantPoolTypes.getStackFrameCount() * STACKFRAME_ENTRY_SIZE);
requiredBufferSize += (_constantPoolTypes.getExecutionSampleCount() * EXECUTION_SAMPLE_EVENT_SIZE);

requireBufferSize += _constantPoolTypes.getExecutionSampleCount() * EXECUTION_SAMPLE_EVENT_SIZE;
requiredBufferSize += (_constantPoolTypes.getThreadStartCount() * THREAD_START_EVENT_SIZE);

requireBufferSize += _constantPoolTypes.getThreadStartCount() * THREAD_START_EVENT_SIZE;
requiredBufferSize += (_constantPoolTypes.getThreadEndCount() * THREAD_END_EVENT_SIZE);

requireBufferSize += _constantPoolTypes.getThreadEndCount() * THREAD_END_EVENT_SIZE;
requiredBufferSize += (_constantPoolTypes.getThreadSleepCount() * THREAD_SLEEP_EVENT_SIZE);

requireBufferSize += _constantPoolTypes.getThreadSleepCount() * THREAD_SLEEP_EVENT_SIZE;
requiredBufferSize += (_constantPoolTypes.getMonitorWaitCount() * MONITOR_WAIT_EVENT_SIZE);

requireBufferSize += JVM_INFORMATION_EVENT_SIZE;
requiredBufferSize += JVM_INFORMATION_EVENT_SIZE;

requireBufferSize += OS_INFORMATION_EVENT_SIZE;
requiredBufferSize += OS_INFORMATION_EVENT_SIZE;

requireBufferSize += PHYSICAL_MEMORY_EVENT_SIZE;
requiredBufferSize += PHYSICAL_MEMORY_EVENT_SIZE;

requireBufferSize += VIRTUALIZATION_INFORMATION_EVENT_SIZE;
requiredBufferSize += VIRTUALIZATION_INFORMATION_EVENT_SIZE;

requireBufferSize += CPU_INFORMATION_EVENT_SIZE;
requiredBufferSize += CPU_INFORMATION_EVENT_SIZE;

requireBufferSize += INITIAL_SYSTEM_PROPERTY_EVENT_SIZE;
return requireBufferSize;
requiredBufferSize += INITIAL_SYSTEM_PROPERTY_EVENT_SIZE;
return requiredBufferSize;
}

~VM_JFRChunkWriter()
Expand Down
26 changes: 26 additions & 0 deletions runtime/vm/JFRConstantPoolTypes.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1152,5 +1152,31 @@ VM_JFRConstantPoolTypes::freeStackStraceEntries(void *entry, void *userData)
return FALSE;
}

UDATA
VM_JFRConstantPoolTypes::freeThreadNameEntries(void *entry, void *userData)
{
ThreadEntry *tableEntry = (ThreadEntry *) entry;
J9VMThread *currentThread = (J9VMThread *)userData;
PORT_ACCESS_FROM_VMC(currentThread);

j9mem_free_memory(tableEntry->javaThreadName);
tableEntry->javaThreadName = NULL;

return FALSE;
}

UDATA
VM_JFRConstantPoolTypes::freeThreadGroupNameEntries(void *entry, void *userData)
{
ThreadGroupEntry *tableEntry = (ThreadGroupEntry *) entry;
J9VMThread *currentThread = (J9VMThread *)userData;
PORT_ACCESS_FROM_VMC(currentThread);

j9mem_free_memory(tableEntry->name);
tableEntry->name = NULL;

return FALSE;
}


#endif /* defined(J9VM_OPT_JFR) */
Loading