Commit message: media: vidtv: Check for null return of vzalloc

As the possible failure of the vzalloc(), e->encoder_buf might be NULL.
Therefore, it should be better to check it in order
to guarantee the success of the initialization.
If fails, we need to free not only 'e' but also 'e->name'.
Also, if the allocation for ctx fails, we need to free 'e->encoder_buf'
else.

Fixes: f90cf6079bf6 ("media: vidtv: add a bridge driver")
Signed-off-by: Jiasheng Jiang <jiasheng@iscas.ac.cn>
Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
Signed-off-by: Mauro Carvalho Chehab <mchehab@kernel.org>
CVE: CVE-2022-3078
CWE: CWE-476
Vulnerability description: An issue was discovered in the Linux kernel through 5.16-rc6. There is a lack of check after calling vzalloc() and lack of free after allocation in drivers/media/test-drivers/vidtv/vidtv_s302m.c.

Project: linux
File: vidtv_s302m.c

static void vidtv_s302m_alloc_au(struct vidtv_encoder *e)
{
	struct vidtv_access_unit *sync_au = NULL;
	struct vidtv_access_unit *temp = NULL;

	if (e->sync && e->sync->is_video_encoder) {
		sync_au = e->sync->access_units;

		while (sync_au) {
			temp = vidtv_s302m_access_unit_init(e->access_units);
			if (!e->access_units)
				e->access_units = temp;

			sync_au = sync_au->next;
		}

		return;
	}

	e->access_units = vidtv_s302m_access_unit_init(NULL);
}

Project: linux
File: vidtv_s302m.c

static struct vidtv_access_unit *vidtv_s302m_access_unit_init(struct vidtv_access_unit *head)
{
	struct vidtv_access_unit *au;

	au = kzalloc(sizeof(*au), GFP_KERNEL);
	if (!au)
		return NULL;

	if (head) {
		while (head->next)
			head = head->next;

		head->next = au;
	}

	return au;
}

Project: linux
File: vidtv_s302m.c

static void *vidtv_s302m_encode(struct vidtv_encoder *e)
{
	struct vidtv_s302m_ctx *ctx = e->ctx;

	/*
	 * According to SMPTE 302M, an audio access unit is specified as those
	 * AES3 words that are associated with a corresponding video frame.
	 * Therefore, there is one audio access unit for every video access unit
	 * in the corresponding video encoder ('sync'), using the same values
	 * for PTS as used by the video encoder.
	 *
	 * Assuming that it is also possible to send audio without any
	 * associated video, as in a radio-like service, a single audio access unit
	 * is created with values for 'num_samples' and 'pts' taken empirically from
	 * ffmpeg
	 */

	vidtv_s302m_access_unit_destroy(e);
	vidtv_s302m_alloc_au(e);

	if (e->sync && e->sync->is_video_encoder) {
		vidtv_s302m_compute_sample_count_from_video(e);
		vidtv_s302m_compute_pts_from_video(e);
	} else {
		e->access_units->num_samples = FF_S302M_DEFAULT_NUM_FRAMES;
		e->access_units->pts = (ctx->au_count * FF_S302M_DEFAULT_PTS_INCREMENT) +
				       FF_S302M_DEFAULT_PTS_OFFSET;
	}

	vidtv_s302m_write_frames(e);

	return e->encoder_buf;
}

Project: linux
File: vidtv_s302m.c

*vidtv_s302m_encoder_init(struct vidtv_s302m_encoder_init_args args)
{
	u32 priv_sz = sizeof(struct vidtv_s302m_ctx);
	struct vidtv_s302m_ctx *ctx;
	struct vidtv_encoder *e;

	e = kzalloc(sizeof(*e), GFP_KERNEL);
	if (!e)
		return NULL;

	e->id = S302M;

	if (args.name)
		e->name = kstrdup(args.name, GFP_KERNEL);

	e->encoder_buf = vzalloc(VIDTV_S302M_BUF_SZ);
	if (!e->encoder_buf)
		goto out_kfree_e;

	e->encoder_buf_sz = VIDTV_S302M_BUF_SZ;
	e->encoder_buf_offset = 0;

	e->sample_count = 0;

	e->src_buf = (args.src_buf) ? args.src_buf : NULL;
	e->src_buf_sz = (args.src_buf) ? args.src_buf_sz : 0;
	e->src_buf_offset = 0;

	e->is_video_encoder = false;

	ctx = kzalloc(priv_sz, GFP_KERNEL);
	if (!ctx)
		goto out_kfree_buf;

	e->ctx = ctx;
	ctx->last_duration = 0;

	e->encode = vidtv_s302m_encode;
	e->clear = vidtv_s302m_clear;

	e->es_pid = cpu_to_be16(args.es_pid);
	e->stream_id = cpu_to_be16(PES_PRIVATE_STREAM_1);

	e->sync = args.sync;
	e->sampling_rate_hz = S302M_SAMPLING_RATE_HZ;

	e->last_sample_cb = args.last_sample_cb;

	e->destroy = vidtv_s302m_encoder_destroy;

	if (args.head) {
		while (args.head->next)
			args.head = args.head->next;

		args.head->next = e;
	}

	e->next = NULL;

	return e;

out_kfree_buf:
	kfree(e->encoder_buf);

out_kfree_e:
	kfree(e->name);
	kfree(e);
	return NULL;
}

Project: linux
File: vidtv_s302m.c

static void vidtv_s302m_write_frames(struct vidtv_encoder *e)
{
	struct vidtv_access_unit *au = e->access_units;
	struct vidtv_s302m_ctx *ctx = e->ctx;
	u32 nbytes_per_unit = 0;
	u32 nbytes = 0;
	u32 au_sz = 0;
	u16 sample;
	u32 j;

	while (au) {
		au_sz = au->num_samples *
			sizeof(struct vidtv_s302m_frame_16);

		nbytes_per_unit = vidtv_s302m_write_h(e, au_sz);

		for (j = 0; j < au->num_samples; ++j) {
			sample = vidtv_s302m_get_sample(e);
			nbytes_per_unit += vidtv_s302m_write_frame(e, sample);

			if (e->src_buf)
				e->src_buf_offset += sizeof(u16);

			e->sample_count++;
		}

		au->nbytes = nbytes_per_unit;

		if (au_sz + sizeof(struct vidtv_smpte_s302m_es) != nbytes_per_unit) {
			pr_warn_ratelimited("write size was %u, expected %zu\n",
					    nbytes_per_unit,
					    au_sz + sizeof(struct vidtv_smpte_s302m_es));
		}

		nbytes += nbytes_per_unit;
		au->offset = nbytes - nbytes_per_unit;

		nbytes_per_unit = 0;
		ctx->au_count++;

		au = au->next;
	}
}

Project: linux
File: vidtv_s302m.c

static u16 vidtv_s302m_get_sample(struct vidtv_encoder *e)
{
	u16 sample;
	int pos;
	struct vidtv_s302m_ctx *ctx = e->ctx;

	if (!e->src_buf) {
		/*
		 * Simple tone generator: play the tones at the
		 * beethoven_fur_elise array.
		 */
		if (ctx->last_duration <= 0) {
			if (e->src_buf_offset >= ARRAY_SIZE(beethoven_fur_elise))
				e->src_buf_offset = 0;

			ctx->last_tone = beethoven_fur_elise[e->src_buf_offset].note;
			ctx->last_duration = beethoven_fur_elise[e->src_buf_offset].duration *
					     S302M_SAMPLING_RATE_HZ / COMPASS / 5;
			e->src_buf_offset++;
			ctx->note_offset = 0;
		} else {
			ctx->last_duration--;
		}

		/* Handle pause notes */
		if (!ctx->last_tone)
			return 0x8000;

		pos = (2 * PI * ctx->note_offset * ctx->last_tone) / S302M_SAMPLING_RATE_HZ;
		ctx->note_offset++;

		return (fixp_sin32(pos % (2 * PI)) >> 16) + 0x8000;
	}

	/* bug somewhere */
	if (e->src_buf_offset > e->src_buf_sz) {
		pr_err_ratelimited("overflow detected: %d > %d, wrapping.\n",
				   e->src_buf_offset,
				   e->src_buf_sz);

		e->src_buf_offset = 0;
	}

	if (e->src_buf_offset >= e->src_buf_sz) {
		/* let the source know we are out of data */
		if (e->last_sample_cb)
			e->last_sample_cb(e->sample_count);

		e->src_buf_offset = 0;
	}

	sample = *(u16 *)(e->src_buf + e->src_buf_offset);

	return sample;
}

Project: linux
File: vidtv_s302m.c

static u32 vidtv_s302m_write_h(struct vidtv_encoder *e, u32 p_sz)
{
	struct vidtv_smpte_s302m_es h = {};
	u32 nbytes = 0;

	/* 2 channels, ident: 0, 16 bits per sample */
	h.bitfield = cpu_to_be32((p_sz << 16));

	nbytes += vidtv_memcpy(e->encoder_buf,
			       e->encoder_buf_offset,
			       e->encoder_buf_sz,
			       &h,
			       sizeof(h));

	e->encoder_buf_offset += nbytes;
	return nbytes;
}

Project: linux
File: vidtv_s302m.c

static void vidtv_s302m_access_unit_destroy(struct vidtv_encoder *e)
{
	struct vidtv_access_unit *head = e->access_units;
	struct vidtv_access_unit *tmp = NULL;

	while (head) {
		tmp = head;
		head = head->next;
		kfree(tmp);
	}

	e->access_units = NULL;
}

Project: linux
File: vidtv_s302m.c

static u32 vidtv_s302m_clear(struct vidtv_encoder *e)
{
	struct vidtv_access_unit *au = e->access_units;
	u32 count = 0;

	while (au) {
		count++;
		au = au->next;
	}

	vidtv_s302m_access_unit_destroy(e);
	memset(e->encoder_buf, 0, VIDTV_S302M_BUF_SZ);
	e->encoder_buf_offset = 0;

	return count;
}

Project: linux
File: vidtv_s302m.c

void vidtv_s302m_encoder_destroy(struct vidtv_encoder *e)
{
	if (e->id != S302M) {
		pr_err_ratelimited("Encoder type mismatch, skipping.\n");
		return;
	}

	vidtv_s302m_access_unit_destroy(e);
	kfree(e->name);
	vfree(e->encoder_buf);
	kfree(e->ctx);
	kfree(e);
}

Project: linux
File: vidtv_s302m.c

static u32 vidtv_s302m_write_frame(struct vidtv_encoder *e,
				   u16 sample)
{
	struct vidtv_s302m_ctx *ctx = e->ctx;
	struct vidtv_s302m_frame_16 f = {};
	u32 nbytes = 0;

	/* from ffmpeg: see s302enc.c */

	u8 vucf = ctx->frame_index == 0 ? 0x10 : 0;

	f.data[0] = sample & 0xFF;
	f.data[1] = (sample & 0xFF00) >>  8;
	f.data[2] = ((sample & 0x0F)  <<  4) | vucf;
	f.data[3] = (sample & 0x0FF0) >>  4;
	f.data[4] = (sample & 0xF000) >> 12;

	f.data[0] = reverse[f.data[0]];
	f.data[1] = reverse[f.data[1]];
	f.data[2] = reverse[f.data[2]];
	f.data[3] = reverse[f.data[3]];
	f.data[4] = reverse[f.data[4]];

	nbytes += vidtv_memcpy(e->encoder_buf,
			       e->encoder_buf_offset,
			       VIDTV_S302M_BUF_SZ,
			       &f,
			       sizeof(f));

	e->encoder_buf_offset += nbytes;

	ctx->frame_index++;
	if (ctx->frame_index >= S302M_BLOCK_SZ)
		ctx->frame_index = 0;

	return nbytes;
}

Project: linux
File: vidtv_s302m.c

vidtv_s302m_compute_sample_count_from_video(struct vidtv_encoder *e)
{
	struct vidtv_access_unit *sync_au = e->sync->access_units;
	struct vidtv_access_unit *au = e->access_units;
	u32 sample_duration_usecs;
	u32 vau_duration_usecs;
	u32 s;

	vau_duration_usecs    = USEC_PER_SEC / e->sync->sampling_rate_hz;
	sample_duration_usecs = USEC_PER_SEC / e->sampling_rate_hz;

	while (au && sync_au) {
		s = DIV_ROUND_UP(vau_duration_usecs, sample_duration_usecs);
		au->num_samples = s;
		au = au->next;
		sync_au = sync_au->next;
	}
}

Project: linux
File: vidtv_s302m.c

static void vidtv_s302m_compute_pts_from_video(struct vidtv_encoder *e)
{
	struct vidtv_access_unit *au = e->access_units;
	struct vidtv_access_unit *sync_au = e->sync->access_units;

	/* use the same pts from the video access unit*/
	while (au && sync_au) {
		au->pts = sync_au->pts;
		au = au->next;
		sync_au = sync_au->next;
	}
}