oem-meta: http-boot: fix heap OOB write on read with offset past EOF

HttpBootHandler::read() for READ_DATA revised the response length with

```
    if (offset + data->length >= sb.st_size)
        data->length = sb.st_size - offset;
```

Both offset (uint16_t) and length (uint8_t) are host-supplied. When the
host passes an offset beyond the end of the bios-rootcert file, lseek()
past EOF succeeds and sb.st_size - offset is negative; the implicit
conversion to uint8_t wraps (e.g. 2000 - 2001 -> 255). The memcpy()
into the respbuf data[] region sized from the original request length
then writes out of bounds, corrupting the heap, and the over-written
bytes are encoded back into the response.

Reject offsets at or beyond EOF so the subtraction cannot underflow,
and clamp the revised length to min(requested, remaining) so it can
never exceed the caller's response buffer. Also fail short reads
instead of returning uninitialized bytes, and clamp any over-long
handler length in FileIOHandler::readFileIO as defense in depth.

Signed-off-by: Patrick Williams <patrick@stwcx.xyz>
Reported-by: Yu Chen <paloaltoalex@gmail.com>
Assisted-by: opencode:meta/muse-spark-1.3
Change-Id: Ic9c26aeeb933aeed2ec1f267be661329cc9507e2
diff --git a/oem/meta/libpldmresponder/file_io.cpp b/oem/meta/libpldmresponder/file_io.cpp
index 02d85ef..8c400c8 100644
--- a/oem/meta/libpldmresponder/file_io.cpp
+++ b/oem/meta/libpldmresponder/file_io.cpp
@@ -127,6 +127,15 @@
         return ccOnlyResponse(request, rc);
     }
 
+    // Clamp handler-revised length to the request-sized response buffer.
+    if (response_msg->length > request_msg.length)
+    {
+        error(
+            "OEM Meta read handler returned length {RETURNED} exceeding request length {REQUESTED}",
+            "RETURNED", response_msg->length, "REQUESTED", request_msg.length);
+        response_msg->length = request_msg.length;
+    }
+
     response_msg->completion_code = PLDM_SUCCESS;
     encoderesplen += response_msg->length;
 
diff --git a/oem/meta/libpldmresponder/file_io_type_http_boot.cpp b/oem/meta/libpldmresponder/file_io_type_http_boot.cpp
index 24bc0de..beccb98 100644
--- a/oem/meta/libpldmresponder/file_io_type_http_boot.cpp
+++ b/oem/meta/libpldmresponder/file_io_type_http_boot.cpp
@@ -111,6 +111,15 @@
             uint8_t transferFlag = data->info.data.transferFlag;
             uint16_t offset = data->info.data.offset;
 
+            if (offset > sb.st_size)
+            {
+                error(
+                    "Invalid offset={OFFSET} beyond file size={SIZE} on Http boot certification file",
+                    "OFFSET", offset, "SIZE", sb.st_size);
+                close(fd);
+                return PLDM_ERROR;
+            }
+
             int ret = lseek(fd, offset, SEEK_SET);
             if (ret < 0)
             {
@@ -121,16 +130,26 @@
                 return PLDM_ERROR;
             }
 
-            if (offset + data->length >= sb.st_size)
+            // remaining >= 0 here; keep revised length <= requested.
+            off_t remaining = sb.st_size - offset;
+            if (remaining <= data->length)
             {
                 transferFlag = PLDM_END;
-                data->length = sb.st_size - offset; // Revise length
+                data->length = static_cast<uint8_t>(remaining);
             }
             else
             {
                 transferFlag = PLDM_MIDDLE;
             }
 
+            if (data->length == 0)
+            {
+                data->info.data.transferFlag = transferFlag;
+                data->info.data.offset = offset;
+                close(fd);
+                return PLDM_SUCCESS;
+            }
+
             uint8_t* buffer = (uint8_t*)malloc(data->length);
             if (buffer == nullptr)
             {
@@ -141,8 +160,8 @@
                 return PLDM_ERROR;
             }
 
-            ret = ::read(fd, buffer, data->length);
-            if (ret < 0)
+            ssize_t bytesRead = ::read(fd, buffer, data->length);
+            if (bytesRead < 0)
             {
                 error(
                     "Failed to read file content at offset={OFFSET} of length={LENGTH} on Http boot certification file",
@@ -152,6 +171,15 @@
                 return PLDM_ERROR;
             }
 
+            if (bytesRead != data->length)
+            {
+                // Short read: file shrank under us. Expose only the bytes
+                // actually read so we never copy uninitialized memory or
+                // over-report, and terminate the transfer.
+                transferFlag = PLDM_END;
+                data->length = static_cast<uint8_t>(bytesRead);
+            }
+
             memcpy(pldm_oem_meta_file_io_read_resp_data(data), buffer,
                    data->length);
             free(buffer);