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

rpc: fix known RCE in rpc-server #1103

Merged
merged 1 commit into from
Feb 6, 2025
Merged

rpc: fix known RCE in rpc-server #1103

merged 1 commit into from
Feb 6, 2025

Conversation

retr0reg
Copy link
Contributor

@retr0reg retr0reg commented Feb 5, 2025

This is a issue of the rpc-server that I started working on around December last year, the vulnerability derives from the fact that ggml_backend_cpu_buffer_cpy_tensor in ./ggml/src/ggml-backend.cpp's cpy_tensor action, registered usually as .cpy_tensor:

static bool ggml_backend_cpu_buffer_cpy_tensor(ggml_backend_buffer_t buffer, const struct ggml_tensor * src, struct ggml_tensor * dst) {
    if (ggml_backend_buffer_is_host(src->buffer)) {
        memcpy(dst->data, src->data, ggml_nbytes(src));
        return true;
    }
    return false;

    GGML_UNUSED(buffer);
}

The ggml_backend_cpu_buffer_cpy_tensor operation seemed secure since the implementation of the strict boundary checks (really strick) in deserialize_tensor() after GHSA-wcr5-566p-9cwj, GHSA-5vm9-p64x-gqw9j, GHSA-mqp6-7pv6-fqjf. Which made buf->data pointer manipulation and write-what-where and read-what-where impossible. However, this tiny leftover from the last patch that can cause RCE from sophisticated exploitation that plays with partial-writing and ggml's unique memory management (I wrote a 10k word writeup for it very interesting). The major issue here is how ggml_nbytes is depends on a Tensor's nb[] / ne[]. This allows out-of-bound writing (or copying) when src applied for a larger context where src->data can be in range and passes deserialize_tensor checks, but copies over dst->data when we try to manipulate ggml_nbytes(src) something larger than ggml_backend_buffer_get_size(tensor->buffer); (which is the actual size of the buffer)

To mitigate this, checks are added in rpc_server::copy_tensor, before dst and src being pass into dst_buf->iface.cpy_tensor after deserialize_tensor checks;

    uint64_t src_size   = (uint64_t) ggml_nbytes(src);
    uint64_t dst_data   = (uint64_t) dst->data;
    uint64_t dst_base   = (uint64_t) ggml_backend_buffer_get_base(dst->buffer);
    uint64_t dst_buf_sz = (uint64_t) ggml_backend_buffer_get_size(dst->buffer);

    if (dst_data + src_size > dst_base + dst_buf_sz) {
        GGML_PRINT_DEBUG("[%s] out-of-bounds write in rpc_server::copy_tensor:\n"
                         "    write range : [0x%" PRIx64 ", 0x%" PRIx64 "]\n"
                         "    buffer base: [0x%" PRIx64 ", 0x%" PRIx64 "]\n",
                         __func__,
                         dst_data,
                         dst_data + src_size,
                         dst_base,
                         dst_base + dst_buf_sz);
        ggml_free(ctx);
        return false;
    }

Here dst_data (dst->data) + src_size (ggml_nbytes(src)) is compared todst_base (the dst context, ggml_backend_buffer_get_base(dst->buffer);) + dst_buf_sz (real dst buffer size, ggml_backend_buffer_get_size(dst->buffer);). Prevents the memcpy in ggml_backend_cpu_buffer_cpy_tensor (the actual sink) from copying ggml_nbytes(src) (size) of src->data to dst->data, with size that's larger than the dst->data + dst->size (the actual dst buffer size). Preventing the overflow (out-of-bound write) from happening, and eliminating the chance of any furthermore exploitations

Add bounds checking in `rpc_server::copy_tensor` to prevent out-of-bounds writes
+ Check if  `(uint8_t *)dst->data + ggml_nbytes(src)` remains within the destination buffer’s allocated region.
@slaren slaren merged commit 08b5380 into ggml-org:master Feb 6, 2025
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants