Samsung fimg2d – FIMG2D_BITBLT_BLIT ioctl Concurrency Flaw

  • 作者: Google Security Research
    日期: 2015-10-28
  • 类别:
    平台:
  • 来源:https://www.exploit-db.com/exploits/38557/
  • Source: https://code.google.com/p/google-security-research/issues/detail?id=492
    
    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:
    
    #ifdef BLIT_WORKQUE
    #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)
    #else
    #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)
    #endif
    
    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) {
    case FIMG2D_BITBLT_BLIT:
    
    	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:
    https://gitlab.com/exploit-database/exploitdb-bin-sploits/-/raw/main/bin-sploits/38557.zip