Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit a46393ab authored by Jacob von Chorus's avatar Jacob von Chorus Committed by Greg Kroah-Hartman
Browse files

staging: gs_fpgaboot: add buffer overflow checks



Four fields in struct fpgaimage are char arrays of length MAX_STR (256).
The amount of data read into these buffers is controlled by a length
field in the bitstream file read from userspace. If a corrupt or
malicious firmware file was supplied, kernel data beyond these buffers
can be overwritten arbitrarily.

This patch adds a check of the bitstream's length value to ensure it
fits within the bounds of the allocated buffers. An error condition is
returned from gs_read_bitstream if any of the reads fail.

Signed-off-by: default avatarJacob von Chorus <jacobvonchorus@cwphoto.ca>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent ee714b80
Loading
Loading
Loading
Loading
+40 −14
Original line number Diff line number Diff line
@@ -23,6 +23,7 @@
#include <linux/delay.h>
#include <linux/io.h>
#include <linux/firmware.h>
#include <asm/unaligned.h>

#include "gs_fpgaboot.h"
#include "io.h"
@@ -47,7 +48,7 @@ static void read_bitstream(char *bitdata, char *buf, int *offset, int rdsize)
	*offset += rdsize;
}

static void readinfo_bitstream(char *bitdata, char *buf, int *offset)
static int readinfo_bitstream(char *bitdata, char *buf, int size, int *offset)
{
	char tbuf[64];
	s32 len;
@@ -58,10 +59,16 @@ static void readinfo_bitstream(char *bitdata, char *buf, int *offset)
	/* read length */
	read_bitstream(bitdata, tbuf, offset, 2);

	len = tbuf[0] << 8 | tbuf[1];
	len = get_unaligned_be16(tbuf);
	if (len >= size) {
		pr_err("error: readinfo buffer too small\n");
		return -EINVAL;
	}

	read_bitstream(bitdata, buf, offset, len);
	buf[len] = '\0';

	return 0;
}

/*
@@ -83,8 +90,7 @@ static int readlength_bitstream(char *bitdata, int *lendata, int *offset)
	/* read 4bytes length */
	read_bitstream(bitdata, tbuf, offset, 4);

	*lendata = tbuf[0] << 24 | tbuf[1] << 16 |
		tbuf[2] << 8 | tbuf[3];
	*lendata = get_unaligned_be32(tbuf);

	return 0;
}
@@ -113,7 +119,7 @@ static int readmagic_bitstream(char *bitdata, int *offset)
/*
 * NOTE: supports only bitstream format
 */
static enum fmt_image get_imageformat(struct fpgaimage *fimage)
static enum fmt_image get_imageformat(void)
{
	return f_bit;
}
@@ -127,34 +133,54 @@ static void gs_print_header(struct fpgaimage *fimage)
	pr_info("lendata: %d\n", fimage->lendata);
}

static void gs_read_bitstream(struct fpgaimage *fimage)
static int gs_read_bitstream(struct fpgaimage *fimage)
{
	char *bitdata;
	int offset;
	int err;

	offset = 0;
	bitdata = (char *)fimage->fw_entry->data;

	readmagic_bitstream(bitdata, &offset);
	readinfo_bitstream(bitdata, fimage->filename, &offset);
	readinfo_bitstream(bitdata, fimage->part, &offset);
	readinfo_bitstream(bitdata, fimage->date, &offset);
	readinfo_bitstream(bitdata, fimage->time, &offset);
	readlength_bitstream(bitdata, &fimage->lendata, &offset);
	err = readmagic_bitstream(bitdata, &offset);
	if (err)
		return err;

	err = readinfo_bitstream(bitdata, fimage->filename, MAX_STR, &offset);
	if (err)
		return err;
	err = readinfo_bitstream(bitdata, fimage->part, MAX_STR, &offset);
	if (err)
		return err;
	err = readinfo_bitstream(bitdata, fimage->date, MAX_STR, &offset);
	if (err)
		return err;
	err = readinfo_bitstream(bitdata, fimage->time, MAX_STR, &offset);
	if (err)
		return err;

	err = readlength_bitstream(bitdata, &fimage->lendata, &offset);
	if (err)
		return err;

	fimage->fpgadata = bitdata + offset;

	return 0;
}

static int gs_read_image(struct fpgaimage *fimage)
{
	int img_fmt;
	int err;

	img_fmt = get_imageformat(fimage);
	img_fmt = get_imageformat();

	switch (img_fmt) {
	case f_bit:
		pr_info("image is bitstream format\n");
		gs_read_bitstream(fimage);
		err = gs_read_bitstream(fimage);
		if (err)
			return err;
		break;
	default:
		pr_err("unsupported fpga image format\n");