Repository: ggml-org/llama.cpp
Pull request: GGML: Check for null buffers in get/set/copy tensor RPC endpoints

The RPC operations `RPC_CMD_SET_TENSOR`, `RPC_CMD_SET_TENSOR_HASH`, `RPC_CMD_GET_TENSOR`, and `RPC_CMD_COPY_TENSOR` all require a backend buffer was previously allocated and located during the operation otherwise the code will subsequently crash with various NULL pointer dereferences. This adds checks for NULL pointer buffers after `deserialize_tensor` is called for each operation.

Tested the RPC changes loading various models over the RPC interface:

```
$ build/bin/rpc-server -d metal
...
create_backend: using Metal backend
Starting RPC server v2.0.0
  endpoint       : 127.0.0.1:50052
  local cache    : n/a
  backend memory : 49146 MB
Accepted client connection, free_mem=51533365248, total_mem=51539607552
```

llama-cli on the client side:

```
$ build/bin/llama-cli -m ~/Downloads/Meta-Llama-3.1-8B-Instruct-abliterated-Q6_K.gguf --rpc localhost:50052 -n 64 -ngl 99
...
> This is a test!
I'm ready to help. What's the purpose of this test?
```

and llama-bench

```
$ build/bin/llama-bench -rpc localhost:50052
warning: asserts enabled, performance may be affected
warning: debug build, performance may be affected
register_backend: registered backend Metal (1 devices)
register_device: registered device Metal (Apple M4 Max)
register_backend: registered backend BLAS (1 devices)
register_device: registered device BLAS (Accelerate)
register_backend: registered backend RPC (0 devices)
register_backend: registered backend CPU (1 devices)
register_device: registered device CPU (Apple M4 Max)
| model                          |       size |     params | backend    | threads |            test |                  t/s |
| ------------------------------ | ---------: | ---------: | ---------- | ------: | --------------: | -------------------: |
| llama 1B Q4_0                  | 606.54 MiB |     1.10 B | Metal,BLAS,RPC |      12 |           pp512 |     3115.99 ± 130.79 |
| llama 1B Q4_0                  | 606.54 MiB |     1.10 B | Metal,BLAS,RPC |      12 |           tg128 |         86.28 ± 2.04 |

build: e40e6f7a (5986)
```

Changed files:
filename: ggml/src/ggml-rpc/ggml-rpc.cpp
patch: @@ -1055,7 +1055,7 @@ bool rpc_server::set_tensor(const std::vector<uint8_t> & input) {
     GGML_ASSERT(ctx_ptr != nullptr);
     ggml_context * ctx = ctx_ptr.get();
     ggml_tensor * tensor = deserialize_tensor(ctx, in_tensor);
-    if (tensor == nullptr) {
+    if (tensor == nullptr || tensor->buffer == nullptr) {
         GGML_LOG_ERROR("[%s] error deserializing tensor\n", __func__);
         return false;
     }
@@ -1124,7 +1124,7 @@ bool rpc_server::set_tensor_hash(const rpc_msg_set_tensor_hash_req & request, rp
     GGML_ASSERT(ctx_ptr != nullptr);
     ggml_context * ctx = ctx_ptr.get();
     ggml_tensor * tensor = deserialize_tensor(ctx, &request.tensor);
-    if (tensor == nullptr) {
+    if (tensor == nullptr || tensor->buffer == nullptr) {
         GGML_LOG_ERROR("[%s] error deserializing tensor\n", __func__);
         return false;
     }
@@ -1192,7 +1192,7 @@ bool rpc_server::get_tensor(const rpc_msg_get_tensor_req & request, std::vector<
     GGML_ASSERT(ctx_ptr != nullptr);
     ggml_context * ctx = ctx_ptr.get();
     ggml_tensor * tensor = deserialize_tensor(ctx, &request.tensor);
-    if (tensor == nullptr) {
+    if (tensor == nullptr || tensor->buffer == nullptr) {
         GGML_LOG_ERROR("[%s] error deserializing tensor\n", __func__);
         return false;
     }
@@ -1229,7 +1229,7 @@ bool rpc_server::copy_tensor(const rpc_msg_copy_tensor_req & request, rpc_msg_co
 
     ggml_tensor * src = deserialize_tensor(ctx, &request.src);
     ggml_tensor * dst = deserialize_tensor(ctx, &request.dst);
-    if (src == nullptr || dst == nullptr) {
+    if (src == nullptr || dst == nullptr || src->buffer == nullptr || dst->buffer == nullptr) {
         GGML_LOG_ERROR("[%s] error deserializing tensors\n", __func__);
         return false;
     }