Samsung fimg2d - FIMG2D_BITBLT_BLIT ioctl Concurrency Flaw







Become a Certified Penetration Tester

Enroll in Penetration Testing with Kali Linux and pass the exam to become an Offensive Security Certified Professional (OSCP). All new content for 2020.



The Samsung Graphics 2D driver (/dev/fimg2d) is accessible by unprivileged users/applications. It was found that the ioctl implementation for this driver contains a locking error which can lead to memory errors (such as use-after-free) due to a race condition.

The key observation is in the locking routine definitions in fimg2d.h:

#define g2d_lock(x)             do {} while (0)
#define g2d_unlock(x)           do {} while (0)
#define g2d_spin_lock(x, f)     spin_lock_irqsave(x, f)
#define g2d_spin_unlock(x, f)   spin_unlock_irqrestore(x, f)
#define g2d_lock(x)             mutex_lock(x)
#define g2d_unlock(x)           mutex_unlock(x)
#define g2d_spin_lock(x, f)     do { f = 0; } while (0)
#define g2d_spin_unlock(x, f)   do { f = 0; } while (0)

This means that the g2d_lock/g2d_unlock routines are no-ops when BLIT_WORKQUE is defined, which appears to be the default configuration. Unfortunately the alternative spin lock routines are not used consistently with this configuration. For example, the FIMG2D_BITBLT_BLIT ioctl command (with notes annotated as "PZ"):

ctx = file->private_data; /* PZ: ctx allocated at open(), lives on the heap. */

switch (cmd) {

	mm = get_task_mm(current);
	if (!mm) {
		fimg2d_err("no mm for ctx\n");
		return -ENXIO;

	g2d_lock(&ctrl->drvlock); /* PZ: This is a no-op. */

	ctx->mm = mm;

	ret = fimg2d_add_command(ctrl, ctx, (struct fimg2d_blit __user *)arg);
	if (ret) {

	ret = fimg2d_request_bitblt(ctrl, ctx); /* PZ: Does stuff with the ctx. */
	if (ret) {

	g2d_unlock(&ctrl->drvlock); /* PZ: Another no-op */

As the lock macros are no-ops, a second process can change ctx->mm when the original process is still using the same ctx->mm (as long as it has access to the same file descriptor).

Reproduction steps:
Open /dev/fimg2d
Fork to get two processes with different mm’s with the access to the fd
Concurrently call the FIMG2D_BITBLT_BLIT ioctl from both processes.
One ioctl should have valid data, the other should fail

At this point ctx->mm will now have invalid or free data (free if the forked process dies). Proof-of-concept code to trigger this condition is attached (fimg2d-lock.c)

Proof of Concept: