From 44dd97bf5e0bc911b5c585fde28bcd589de4b2e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Tue, 30 Jun 2026 13:52:55 +0200 Subject: [PATCH] upektc_img: Prevent image frame overflow The frame length and the offset (which depends on the device-supplied frame type) are derived from device bytes, so validate both the source read (against the response buffer) and the destination write (against the image buffer) before copying. A malicious or malfunctioning device could otherwise drive a negative (huge once unsigned) or out-of-bounds length. Note: the response is reassembled across several USB transfers, so the bound here is the response buffer capacity rather than a single transfer's actual_length. Reported by: Keith Linneman (LinnemanLabs) --- libfprint/drivers/upektc_img.c | 28 ++++++++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/libfprint/drivers/upektc_img.c b/libfprint/drivers/upektc_img.c index f2c6edb8..497d5b44 100644 --- a/libfprint/drivers/upektc_img.c +++ b/libfprint/drivers/upektc_img.c @@ -155,7 +155,8 @@ capture_reqs_cb (FpiUsbTransfer *transfer, FpDevice *device, } static int -upektc_img_process_image_frame (unsigned char *image_buf, unsigned char *cmd_res) +upektc_img_process_image_frame (unsigned char *image_buf, size_t image_buf_len, + unsigned char *cmd_res, size_t cmd_res_len) { int offset = 8; int len = ((cmd_res[5] & 0x0f) << 8) | (cmd_res[6]); @@ -168,6 +169,16 @@ upektc_img_process_image_frame (unsigned char *image_buf, unsigned char *cmd_res } if (cmd_res[7] == 0x20) len -= 4; + + if (len <= 0 || + (size_t) offset + (size_t) len > cmd_res_len || + (size_t) len > image_buf_len) + { + fp_dbg ("Invalid image frame length %d (offset %d, source %zu, dest %zu)", + len, offset, cmd_res_len, image_buf_len); + return 0; + } + memcpy (image_buf, cmd_res + offset, len); return len; @@ -321,7 +332,8 @@ capture_read_data_cb (FpiUsbTransfer *transfer, FpDevice *device, case 0x24: self->image_size += upektc_img_process_image_frame (self->image_bits + self->image_size, - data); + (self->expected_image_size * 2) - self->image_size, + data, MAX_RESPONSE_SIZE); fpi_ssm_jump_to_state (transfer->ssm, CAPTURE_ACK_FRAME); break; @@ -330,8 +342,16 @@ capture_read_data_cb (FpiUsbTransfer *transfer, FpDevice *device, case 0x20: self->image_size += upektc_img_process_image_frame (self->image_bits + self->image_size, - data); - BUG_ON (self->image_size != self->expected_image_size); + (self->expected_image_size * 2) - self->image_size, + data, MAX_RESPONSE_SIZE); + if (self->image_size != self->expected_image_size) + { + fp_err ("Image size mismatch: got %zu, expected %zu", + self->image_size, self->expected_image_size); + fpi_ssm_mark_failed (transfer->ssm, + fpi_device_error_new (FP_DEVICE_ERROR_PROTO)); + return; + } fp_dbg ("Image size is %lu", (gulong) self->image_size); img = fp_image_new (img_class->img_width, img_class->img_height);