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

Code before:
    {
        const struct mach_header_64* header = (const struct mach_header_64*)_dyld_get_image_header(i);
        if ((const void*)header != base_address)
            continue;

        const struct load_command* cmd = (const struct load_command*)((const char*)header + sizeof(struct mach_header_64));

        size_t image_size = 0;
        for (uint32_t j = 0; j < header->ncmds; ++j)
        {
            if (cmd->cmd == LC_SEGMENT_64)
            {
                const struct segment_command_64* seg = (const struct segment_command_64*)cmd;
                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");

Review comment (bug):
The `strncpy` at line 130 does not null-terminate the destination buffer if `dir_len` equals or exceeds the buffer size. Since the subsequent `snprintf` at line 131 writes starting at `r2r_path + dir_len`, if the buffer is not properly null-terminated or if `dir_len` is large, this could lead to incorrect path construction. Add explicit null-termination after `strncpy`:
```c
strncpy(r2r_path, context->assembly_path, dir_len);
r2r_path[dir_len] = '\0';
```
```suggestion
    strncpy(r2r_path, context->assembly_path, dir_len);
    r2r_path[dir_len] = '\0';
```

Code after:
        {
            const struct segment_command_64* seg = (const struct segment_command_64*)cmd;
            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;

    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;