Repository: dotnet/runtime
File: src/mono/mono/component/hot_reload.c (C)

Code before:
static void
dump_methodbody (MonoImage *image)
{
	if (!mono_trace_is_traced (G_LOG_LEVEL_DEBUG, MONO_TRACE_METADATA_UPDATE))
		return;
	MonoTableInfo *t = &image->tables [MONO_TABLE_METHODBODY];
	uint32_t rows = table_info_get_rows (t);
	for (uint32_t i = 0; i < rows; ++i)
	{
		uint32_t cols[MONO_METHODBODY_SIZE];
		mono_metadata_decode_row (t, i, cols, MONO_METHODBODY_SIZE);
		mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_METADATA_UPDATE, " row[%02d] = doc: 0x%08x seq: 0x%08x", i + 1, cols [MONO_METHODBODY_DOCUMENT], cols [MONO_METHODBODY_SEQ_POINTS]);
	}
}

/**
 *
 * LOCKING: Takes the publish_lock
 */
void
hot_reload_apply_changes (int origin, MonoImage *image_base, gconstpointer dmeta_bytes, uint32_t dmeta_length, gconstpointer dil_bytes_orig, uint32_t dil_length, gconstpointer dpdb_bytes_orig, uint32_t dpdb_length, MonoError *error)
{
	if (!assembly_update_supported (image_base, error)) {
		return;
	}

        static int first_origin = -1;

        if (first_origin < 0) {
                first_origin = origin;
        }

        if (first_origin != origin) {
                mono_error_set_not_supported (error, "Applying deltas through the debugger and System.Reflection.Metadata.MetadataUpdater.ApplyUpdate simultaneously is not supported");
                return;
        }

	const char *basename = image_base->filename;

	if (mono_trace_is_traced (G_LOG_LEVEL_DEBUG, MONO_TRACE_METADATA_UPDATE)) {
		g_print ("LOADING basename=%s delta update.\ndelta image=%p & dil=%p\n", basename, dmeta_bytes, dil_bytes_orig);
                mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_METADATA_UPDATE, "delta image size 0x%08x, delta IL size 0x%08x\n", dmeta_length, dil_length);
#if 0
		mono_dump_mem (dmeta_bytes, dmeta_length);
		mono_dump_mem (dil_bytes_orig, dil_length);
#endif
	}

	uint32_t generation = hot_reload_update_prepare ();

	mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_METADATA_UPDATE, "base image string size: 0x%08x", image_base->heap_strings.size);

Review comment (bug):
The empty-update guard checks pointer values (dmeta_bytes/dil_bytes_orig) instead of the lengths. Callers like debugger-agent pass non-NULL pointers even for empty MonoArray payloads, so this condition won’t trigger and we’ll hit image_open_dmeta_from_data() with dmeta_length==0, which asserts (status != MONO_IMAGE_OK / dmeta_image == NULL). Consider guarding on dmeta_length/dil_length (and dpdb_length if relevant) and/or validating that any non-zero length has a non-NULL pointer, rather than comparing pointers to 0.
```suggestion
	/* we may receive empty updates */
	if (dmeta_length == 0 && dil_length == 0 && dpdb_length == 0)
```

Code after:
static void
dump_methodbody (MonoImage *image)
{
	if (!mono_trace_is_traced (G_LOG_LEVEL_DEBUG, MONO_TRACE_METADATA_UPDATE))
		return;
	MonoTableInfo *t = &image->tables [MONO_TABLE_METHODBODY];
	uint32_t rows = table_info_get_rows (t);
	for (uint32_t i = 0; i < rows; ++i)
	{
		uint32_t cols[MONO_METHODBODY_SIZE];
		mono_metadata_decode_row (t, i, cols, MONO_METHODBODY_SIZE);
		mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_METADATA_UPDATE, " row[%02d] = doc: 0x%08x seq: 0x%08x", i + 1, cols [MONO_METHODBODY_DOCUMENT], cols [MONO_METHODBODY_SEQ_POINTS]);
	}
}

/**
 *
 * LOCKING: Takes the publish_lock
 */
void
hot_reload_apply_changes (int origin, MonoImage *image_base, gconstpointer dmeta_bytes, uint32_t dmeta_length, gconstpointer dil_bytes_orig, uint32_t dil_length, gconstpointer dpdb_bytes_orig, uint32_t dpdb_length, MonoError *error)
{
	if (!assembly_update_supported (image_base, error)) {
		return;
	}
	if (dmeta_bytes == 0 && dil_bytes_orig == 0) // we may receive empty updates
	{
		return;
	}
        static int first_origin = -1;
        if (first_origin < 0) {
                first_origin = origin;
        }

        if (first_origin != origin) {
                mono_error_set_not_supported (error, "Applying deltas through the debugger and System.Reflection.Metadata.MetadataUpdater.ApplyUpdate simultaneously is not supported");
                return;
        }

	const char *basename = image_base->filename;

	if (mono_trace_is_traced (G_LOG_LEVEL_DEBUG, MONO_TRACE_METADATA_UPDATE)) {
		g_print ("LOADING basename=%s delta update.\ndelta image=%p & dil=%p\n", basename, dmeta_bytes, dil_bytes_orig);
                mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_METADATA_UPDATE, "delta image size 0x%08x, delta IL size 0x%08x\n", dmeta_length, dil_length);
#if 0
		mono_dump_mem (dmeta_bytes, dmeta_length);
		mono_dump_mem (dil_bytes_orig, dil_length);
#endif
	}

	uint32_t generation = hot_reload_update_prepare ();