<--

Google Nexus 9 Arbitrary Kernel Write

Aleph Research Advisory

Identifier

Severity

High

Product

Nexus 9

Vulnerable Version

All Nexus 9 versions before the September 2016 patches.

Mitigation

Apply the September 2016 Android Security Patches.

Technical Details

The registers debugfs file node is initialized with the following write file operation:

static ssize_t cl_register_write(struct file *file,
    const char __user *userbuf, size_t count, loff_t *ppos)
{
    char buf[80];
    u32 offs;
    u32 val;
    [...]
    struct tegra_cl_dvfs *cld = c->u.dfll.cl_dvfs;
    if (sizeof(buf) <= count)
        return -EINVAL;
    if (copy_from_user(buf, userbuf, count))
        return -EFAULT;
    [...]
    if (sscanf(buf, "[0x%x] = 0x%x", &offs, &val) != 2)
        return -1;
    [...]
    cl_dvfs_writel(cld, val, offs & (~0x3));
    [...]
    return count;
}

That is, on write system call, cl_register_write() securely copies a user space buffer and parses its contents as two numeric values: val, a value to be written, and offs, an offset from a constant address. Cl_dvfs_writel() is then fed with val and offs (cast into a four-byte aligned address).

static inline void cl_dvfs_writel(struct tegra_cl_dvfs *cld,
                      u32 val, u32 offs)
{
    if (offs >= CL_DVFS_I2C_CFG) {
        cl_dvfs_i2c_writel(cld, val, offs);
        return;
    }
    __raw_writel(val, (void *)cld->cl_base + offs);
}
static inline void cl_dvfs_i2c_writel(struct tegra_cl_dvfs *cld,
                      u32 val, u32 offs)
{
    __raw_writel(val, cld->cl_i2c_base + offs);
}

Eventually, __raw_writel() is used to write value val at offs+constant_address, which results in an arbitrary kernel write.

Timeline

Credit

External References