diff options
author | Todd Poynor <toddpoynor@google.com> | 2013-06-04 17:29:38 -0700 |
---|---|---|
committer | Todd Poynor <toddpoynor@google.com> | 2013-06-07 12:29:15 -0700 |
commit | 446c9cf520f5159f16a018b70aa9aed4053a95e1 (patch) | |
tree | e1daaf80162f3a19313bcf46c55ffdb9aed6722a | |
parent | 108bd7e1f3331f69e46bcc5ecd74a28e0b99b39d (diff) | |
download | exynos-android-exynos-manta-3.4-jb-mr2.tar.gz |
ashmem: avoid deadlock between read and mmap callsandroid-exynos-manta-3.4-jb-mr2
Avoid holding ashmem_mutex across code that can page fault. Page faults
grab the mmap_sem for the process, which are also held by mmap calls
prior to calling ashmem_mmap, which locks ashmem_mutex. The reversed
order of locking between the two can deadlock.
The calls that can page fault are read() and the ASHMEM_SET_NAME and
ASHMEM_GET_NAME ioctls. Move the code that accesses userspace pages
outside the ashmem_mutex.
Bug: 9261835
Change-Id: If1322e981d29c889a56cdc9dfcbc6df2729a45e9
Signed-off-by: Todd Poynor <toddpoynor@google.com>
-rw-r--r-- | drivers/staging/android/ashmem.c | 60 |
1 files changed, 33 insertions, 27 deletions
diff --git a/drivers/staging/android/ashmem.c b/drivers/staging/android/ashmem.c index bda20bf15948..c49480ebf85f 100644 --- a/drivers/staging/android/ashmem.c +++ b/drivers/staging/android/ashmem.c @@ -221,21 +221,29 @@ static ssize_t ashmem_read(struct file *file, char __user *buf, /* If size is not set, or set to 0, always return EOF. */ if (asma->size == 0) - goto out; + goto out_unlock; if (!asma->file) { ret = -EBADF; - goto out; + goto out_unlock; } - ret = asma->file->f_op->read(asma->file, buf, len, pos); - if (ret < 0) - goto out; + mutex_unlock(&ashmem_mutex); - /** Update backing file pos, since f_ops->read() doesn't */ - asma->file->f_pos = *pos; + /* + * asma and asma->file are used outside the lock here. We assume + * once asma->file is set it will never be changed, and will not + * be destroyed until all references to the file are dropped and + * ashmem_release is called. + */ + ret = asma->file->f_op->read(asma->file, buf, len, pos); + if (ret >= 0) { + /** Update backing file pos, since f_ops->read() doesn't */ + asma->file->f_pos = *pos; + } + return ret; -out: +out_unlock: mutex_unlock(&ashmem_mutex); return ret; } @@ -402,50 +410,48 @@ out: static int set_name(struct ashmem_area *asma, void __user *name) { + char lname[ASHMEM_NAME_LEN]; + int len; int ret = 0; + len = strncpy_from_user(lname, name, ASHMEM_NAME_LEN); + if (len < 0) + return len; + if (len == ASHMEM_NAME_LEN) + lname[ASHMEM_NAME_LEN - 1] = '\0'; mutex_lock(&ashmem_mutex); /* cannot change an existing mapping's name */ - if (unlikely(asma->file)) { + if (unlikely(asma->file)) ret = -EINVAL; - goto out; - } + else + strcpy(asma->name + ASHMEM_NAME_PREFIX_LEN, lname); - if (unlikely(copy_from_user(asma->name + ASHMEM_NAME_PREFIX_LEN, - name, ASHMEM_NAME_LEN))) - ret = -EFAULT; - asma->name[ASHMEM_FULL_NAME_LEN-1] = '\0'; - -out: mutex_unlock(&ashmem_mutex); - return ret; } static int get_name(struct ashmem_area *asma, void __user *name) { int ret = 0; + char lname[ASHMEM_NAME_LEN]; + size_t len; mutex_lock(&ashmem_mutex); if (asma->name[ASHMEM_NAME_PREFIX_LEN] != '\0') { - size_t len; - /* * Copying only `len', instead of ASHMEM_NAME_LEN, bytes * prevents us from revealing one user's stack to another. */ len = strlen(asma->name + ASHMEM_NAME_PREFIX_LEN) + 1; - if (unlikely(copy_to_user(name, - asma->name + ASHMEM_NAME_PREFIX_LEN, len))) - ret = -EFAULT; + memcpy(lname, asma->name + ASHMEM_NAME_PREFIX_LEN, len); } else { - if (unlikely(copy_to_user(name, ASHMEM_NAME_DEF, - sizeof(ASHMEM_NAME_DEF)))) - ret = -EFAULT; + len = strlen(ASHMEM_NAME_DEF) + 1; + memcpy(lname, ASHMEM_NAME_DEF, len); } mutex_unlock(&ashmem_mutex); - + if (unlikely(copy_to_user(name, lname, len))) + ret = -EFAULT; return ret; } |