Repository: dotnet/runtime
File: src/tasks/AppleAppBuilder/Templates/runtime-coreclr.m (Objective-C)

Code before:
                size_t end_addr = (size_t)(seg->vmaddr + seg->vmsize);
                if (end_addr > image_size)
                    image_size = end_addr;
            }

            cmd = (const struct load_command*)((const char*)cmd + cmd->cmdsize);
        }

        return image_size;
    }
}

bool get_native_code_data(const struct host_runtime_contract_native_code_context* context, struct host_runtime_contract_native_code_data* data)
{
    if (!context || !data || !context->assembly_path || !context->owner_composite_name)
        return false;

    // Look for the owner composite R2R image in the same directory as the assembly
    char r2r_path[PATH_MAX];
    const char *last_slash = strrchr(context->assembly_path, '/');
    size_t dir_len = last_slash ? (size_t)(last_slash - context->assembly_path) : 0;
    if (dir_len >= sizeof(r2r_path) - 1)
        return false;

    strncpy(r2r_path, context->assembly_path, dir_len);
    int written = snprintf(r2r_path + dir_len, sizeof(r2r_path) - dir_len, "/%s", context->owner_composite_name);
    if (written <= 0 || (size_t)written >= sizeof(r2r_path) - dir_len)
        return false;

    assert(loaded_native_code_handle_count < MAX_LOADED_NATIVE_CODE_COUNT);
    void* handle = dlopen(r2r_path, RTLD_LAZY | RTLD_LOCAL);
    if (handle == NULL)
        return false;

    loaded_native_code_handles[loaded_native_code_handle_count] = handle;
    loaded_native_code_handle_count++;

    void* r2r_header = dlsym(handle, "RTR_HEADER");
    if (r2r_header == NULL)
    {
        dlclose(handle);
        return false;
    }

    Dl_info info;
    if (dladdr(r2r_header, &info) == 0)
    {
        dlclose(handle);
        return false;
    }


Review comment (bug):
When `dlsym` fails to find the RTR_HEADER symbol, the code calls `dlclose(handle)`, but the handle was already added to `loaded_native_code_handles` array at line 140. This creates a dangling pointer in the array that will be closed again at cleanup (lines 252-257). Either remove the handle from the array before closing, or set it to NULL after closing, or don't add it to the array until after validation succeeds.

Code after:
bool get_native_code_data(const struct host_runtime_contract_native_code_context* context, struct host_runtime_contract_native_code_data* data)
{
    if (!context || !data || !context->assembly_path || !context->owner_composite_name)
        return false;

    // Look for the owner composite R2R image in the same directory as the assembly
    char r2r_path[PATH_MAX];
    const char *last_slash = strrchr(context->assembly_path, '/');
    size_t dir_len = last_slash ? (size_t)(last_slash - context->assembly_path) : 0;
    if (dir_len >= sizeof(r2r_path) - 1)
        return false;

    strncpy(r2r_path, context->assembly_path, dir_len);
    int written = snprintf(r2r_path + dir_len, sizeof(r2r_path) - dir_len, "/%s", context->owner_composite_name);
    if (written <= 0 || (size_t)written >= sizeof(r2r_path) - dir_len)
        return false;

    void* handle = dlopen(r2r_path, RTLD_LAZY | RTLD_LOCAL);
    if (handle == NULL)
        return false;

    void* r2r_header = dlsym(handle, "RTR_HEADER");
    if (r2r_header == NULL)
    {
        dlclose(handle);
        return false;
    }

    Dl_info info;
    if (dladdr(r2r_header, &info) == 0)
    {
        dlclose(handle);
        return false;
    }

    // The base address points to the Mach header
    void* base_address = info.dli_fbase;
    const struct mach_header_64* header = (const struct mach_header_64*)base_address;

    data->size = sizeof(struct host_runtime_contract_native_code_data);
    data->r2r_header_ptr = r2r_header;
    data->image_size = get_image_size(header);
    data->image_base = base_address;
    return true;
}

void
mono_ios_runtime_init (void)
{
#if INVARIANT_GLOBALIZATION
    setenv ("DOTNET_SYSTEM_GLOBALIZATION_INVARIANT", "1", TRUE);