[prev in list] [next in list] [prev in thread] [next in thread]
List: oss-security
Subject: [oss-security] Linux kernel: a heap buffer overflow in firedtv driver
From: "Luo Likang" <luolikang () nsfocus ! com>
Date: 2021-04-20 1:58:05
Message-ID: 000001d73588$9b95a920$d2c0fb60$ () nsfocus ! com
[Download RAW message or body]
I found a buffer overflow vulnerability in function
avc_ca_pmt(drivers/media/fireware/firedtv-avc.c).
The bounds checking in avc_ca_pmt() is not strict enough. It should be
checking "read_pos + 4" because it's reading 5 bytes. If the
"es_info_length" is non-zero then it reads a 6th byte so there needs to be
an additional check for that.
a) static int fdtv_ca_pmt(struct firedtv *fdtv, void *arg)
{
struct ca_msg *msg = arg;
int data_pos;
int data_length;
int i;
data_pos = 4;
if (msg->msg[3] & 0x80) {
data_length = 0;
for (i = 0; i < (msg->msg[3] & 0x7f); i++)
data_length = (data_length << 8) +
msg->msg[data_pos++];
} else {
data_length = msg->msg[3];
}
return avc_ca_pmt(fdtv, &msg->msg[data_pos], data_length); <== setp
in
}
In this function, the content of `arg` is still the original data passed in
by the user. If msg->msg[3] = 0x84, msg->[4] = 0xff, msg->msg[5]=0xff,
msg->msg[6]=0xff, msg->msg[7]=0xff , will enter the for loop four times, and
data_length will be equal to 0xffffffff, and then call avc_ca_pmt(fdtv,
&msg->msg[8], 0xffffffff)
b) int avc_ca_pmt(struct firedtv *fdtv, char *msg, int length)
{
..
int program_info_length;
int pmt_cmd_id;
int read_pos;
int write_pos;
int es_info_length;
int crc32_csum;
int ret;
..
program_info_length = ((msg[4] & 0x0f) << 8) + msg[5]; /////////////
[0]
if (program_info_length > 0)
program_info_length--; /* Remove pmt_cmd_id */
pmt_cmd_id = msg[6]; //////////////[1]
c->operand[0] = SFE_VENDOR_DE_COMPANYID_0;
..
c->operand[23] = (program_info_length & 0xff);
/* CA descriptors at programme level */
read_pos = 6;
write_pos = 24;
if (program_info_length > 0) {
pmt_cmd_id = msg[read_pos++];
if (pmt_cmd_id != 1 && pmt_cmd_id != 4)
////////////[2]
dev_err(fdtv->device,
"invalid pmt_cmd_id %d\n", pmt_cmd_id);
if (program_info_length > sizeof(c->operand) - 4 - write_pos)
{ ///[3]
ret = -EINVAL;
goto out;
}
memcpy(&c->operand[write_pos], &msg[read_pos],
program_info_length);
read_pos += program_info_length;
write_pos += program_info_length; //////[4]
}
while (read_pos < length) { ////////[5]
c->operand[write_pos++] = msg[read_pos++]; ///[6]
c->operand[write_pos++] = msg[read_pos++];
c->operand[write_pos++] = msg[read_pos++];
..
}
}
In [0] and [1] : We can full control the program_info_length and pmt_cmd_id
;
In [2] : for bypass it, pmt_cmd_id must be 1 or 4 ;
In [3] : program_info_length > sizeof(c->operand) - 4 - write_pos)
==> program_info_length <= 509 - 4 - 24 = 0x1e1, so you can control its
value to be 0x1e1 .
In [4] : write_pos will be updated to 24+0x1e1 = 505
In [5]: at this time, length=0xffffffff, read_pos is much smaller
than it, so in[6] will overwrite the c->oprand many bytes.
Patch : https://lore.kernel.org/linux-media/YHaulytonFcW+lyZ@mwanda/
Credit :
LuoLikang @ NSFOCUS SECURITY TEAM
[prev in list] [next in list] [prev in thread] [next in thread]
Configure |
About |
News |
Add a list |
Sponsored by KoreLogic