Skip to content

Commit 80cd795

Browse files
Todd Kjosgregkh
authored andcommitted
binder: fix use-after-free due to ksys_close() during fdget()
44d8047 ("binder: use standard functions to allocate fds") exposed a pre-existing issue in the binder driver. fdget() is used in ksys_ioctl() as a performance optimization. One of the rules associated with fdget() is that ksys_close() must not be called between the fdget() and the fdput(). There is a case where this requirement is not met in the binder driver which results in the reference count dropping to 0 when the device is still in use. This can result in use-after-free or other issues. If userpace has passed a file-descriptor for the binder driver using a BINDER_TYPE_FDA object, then kys_close() is called on it when handling a binder_ioctl(BC_FREE_BUFFER) command. This violates the assumptions for using fdget(). The problem is fixed by deferring the close using task_work_add(). A new variant of __close_fd() was created that returns a struct file with a reference. The fput() is deferred instead of using ksys_close(). Fixes: 44d8047 ("binder: use standard functions to allocate fds") Suggested-by: Al Viro <[email protected]> Signed-off-by: Todd Kjos <[email protected]> Cc: stable <[email protected]> Signed-off-by: Greg Kroah-Hartman <[email protected]>
1 parent 2701e80 commit 80cd795

File tree

3 files changed

+91
-2
lines changed

3 files changed

+91
-2
lines changed

drivers/android/binder.c

Lines changed: 61 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@
7272
#include <linux/spinlock.h>
7373
#include <linux/ratelimit.h>
7474
#include <linux/syscalls.h>
75+
#include <linux/task_work.h>
7576

7677
#include <uapi/linux/android/binder.h>
7778

@@ -2170,6 +2171,64 @@ static bool binder_validate_fixup(struct binder_buffer *b,
21702171
return (fixup_offset >= last_min_offset);
21712172
}
21722173

2174+
/**
2175+
* struct binder_task_work_cb - for deferred close
2176+
*
2177+
* @twork: callback_head for task work
2178+
* @fd: fd to close
2179+
*
2180+
* Structure to pass task work to be handled after
2181+
* returning from binder_ioctl() via task_work_add().
2182+
*/
2183+
struct binder_task_work_cb {
2184+
struct callback_head twork;
2185+
struct file *file;
2186+
};
2187+
2188+
/**
2189+
* binder_do_fd_close() - close list of file descriptors
2190+
* @twork: callback head for task work
2191+
*
2192+
* It is not safe to call ksys_close() during the binder_ioctl()
2193+
* function if there is a chance that binder's own file descriptor
2194+
* might be closed. This is to meet the requirements for using
2195+
* fdget() (see comments for __fget_light()). Therefore use
2196+
* task_work_add() to schedule the close operation once we have
2197+
* returned from binder_ioctl(). This function is a callback
2198+
* for that mechanism and does the actual ksys_close() on the
2199+
* given file descriptor.
2200+
*/
2201+
static void binder_do_fd_close(struct callback_head *twork)
2202+
{
2203+
struct binder_task_work_cb *twcb = container_of(twork,
2204+
struct binder_task_work_cb, twork);
2205+
2206+
fput(twcb->file);
2207+
kfree(twcb);
2208+
}
2209+
2210+
/**
2211+
* binder_deferred_fd_close() - schedule a close for the given file-descriptor
2212+
* @fd: file-descriptor to close
2213+
*
2214+
* See comments in binder_do_fd_close(). This function is used to schedule
2215+
* a file-descriptor to be closed after returning from binder_ioctl().
2216+
*/
2217+
static void binder_deferred_fd_close(int fd)
2218+
{
2219+
struct binder_task_work_cb *twcb;
2220+
2221+
twcb = kzalloc(sizeof(*twcb), GFP_KERNEL);
2222+
if (!twcb)
2223+
return;
2224+
init_task_work(&twcb->twork, binder_do_fd_close);
2225+
__close_fd_get_file(fd, &twcb->file);
2226+
if (twcb->file)
2227+
task_work_add(current, &twcb->twork, true);
2228+
else
2229+
kfree(twcb);
2230+
}
2231+
21732232
static void binder_transaction_buffer_release(struct binder_proc *proc,
21742233
struct binder_buffer *buffer,
21752234
binder_size_t *failed_at)
@@ -2309,7 +2368,7 @@ static void binder_transaction_buffer_release(struct binder_proc *proc,
23092368
}
23102369
fd_array = (u32 *)(parent_buffer + (uintptr_t)fda->parent_offset);
23112370
for (fd_index = 0; fd_index < fda->num_fds; fd_index++)
2312-
ksys_close(fd_array[fd_index]);
2371+
binder_deferred_fd_close(fd_array[fd_index]);
23132372
} break;
23142373
default:
23152374
pr_err("transaction release %d bad object type %x\n",
@@ -3928,7 +3987,7 @@ static int binder_apply_fd_fixups(struct binder_transaction *t)
39283987
} else if (ret) {
39293988
u32 *fdp = (u32 *)(t->buffer->data + fixup->offset);
39303989

3931-
ksys_close(*fdp);
3990+
binder_deferred_fd_close(*fdp);
39323991
}
39333992
list_del(&fixup->fixup_entry);
39343993
kfree(fixup);

fs/file.c

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -640,6 +640,35 @@ int __close_fd(struct files_struct *files, unsigned fd)
640640
}
641641
EXPORT_SYMBOL(__close_fd); /* for ksys_close() */
642642

643+
/*
644+
* variant of __close_fd that gets a ref on the file for later fput
645+
*/
646+
int __close_fd_get_file(unsigned int fd, struct file **res)
647+
{
648+
struct files_struct *files = current->files;
649+
struct file *file;
650+
struct fdtable *fdt;
651+
652+
spin_lock(&files->file_lock);
653+
fdt = files_fdtable(files);
654+
if (fd >= fdt->max_fds)
655+
goto out_unlock;
656+
file = fdt->fd[fd];
657+
if (!file)
658+
goto out_unlock;
659+
rcu_assign_pointer(fdt->fd[fd], NULL);
660+
__put_unused_fd(files, fd);
661+
spin_unlock(&files->file_lock);
662+
get_file(file);
663+
*res = file;
664+
return filp_close(file, files);
665+
666+
out_unlock:
667+
spin_unlock(&files->file_lock);
668+
*res = NULL;
669+
return -ENOENT;
670+
}
671+
643672
void do_close_on_exec(struct files_struct *files)
644673
{
645674
unsigned i;

include/linux/fdtable.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,7 @@ extern void __fd_install(struct files_struct *files,
121121
unsigned int fd, struct file *file);
122122
extern int __close_fd(struct files_struct *files,
123123
unsigned int fd);
124+
extern int __close_fd_get_file(unsigned int fd, struct file **res);
124125

125126
extern struct kmem_cache *files_cachep;
126127

0 commit comments

Comments
 (0)