Commit 957f3f8e authored by Linus Torvalds's avatar Linus Torvalds
Browse files
Pull char/misc fixes from Greg KH:
 "Here are some small driver fixes for 6.4-rc4. They are just two
  different types:

   - binder fixes and reverts for reported problems and regressions in
     the binder "driver".

   - coresight driver fixes for reported problems.

  All of these have been in linux-next for over a week with no reported
  problems"

* tag 'char-misc-6.4-rc4' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc:
  binder: fix UAF of alloc->vma in race with munmap()
  binder: add lockless binder_alloc_(set|get)_vma()
  Revert "android: binder: stop saving a pointer to the VMA"
  Revert "binder_alloc: add missing mmap_lock calls when using the VMA"
  binder: fix UAF caused by faulty buffer cleanup
  coresight: perf: Release Coresight path when alloc trace id failed
  coresight: Fix signedness bug in tmc_etr_buf_insert_barrier_packet()
parents 49572d53 d1d8875c
Loading
Loading
Loading
Loading
+21 −7
Original line number Diff line number Diff line
@@ -1934,24 +1934,23 @@ static void binder_deferred_fd_close(int fd)
static void binder_transaction_buffer_release(struct binder_proc *proc,
					      struct binder_thread *thread,
					      struct binder_buffer *buffer,
					      binder_size_t failed_at,
					      binder_size_t off_end_offset,
					      bool is_failure)
{
	int debug_id = buffer->debug_id;
	binder_size_t off_start_offset, buffer_offset, off_end_offset;
	binder_size_t off_start_offset, buffer_offset;

	binder_debug(BINDER_DEBUG_TRANSACTION,
		     "%d buffer release %d, size %zd-%zd, failed at %llx\n",
		     proc->pid, buffer->debug_id,
		     buffer->data_size, buffer->offsets_size,
		     (unsigned long long)failed_at);
		     (unsigned long long)off_end_offset);

	if (buffer->target_node)
		binder_dec_node(buffer->target_node, 1, 0);

	off_start_offset = ALIGN(buffer->data_size, sizeof(void *));
	off_end_offset = is_failure && failed_at ? failed_at :
				off_start_offset + buffer->offsets_size;

	for (buffer_offset = off_start_offset; buffer_offset < off_end_offset;
	     buffer_offset += sizeof(binder_size_t)) {
		struct binder_object_header *hdr;
@@ -2111,6 +2110,21 @@ static void binder_transaction_buffer_release(struct binder_proc *proc,
	}
}

/* Clean up all the objects in the buffer */
static inline void binder_release_entire_buffer(struct binder_proc *proc,
						struct binder_thread *thread,
						struct binder_buffer *buffer,
						bool is_failure)
{
	binder_size_t off_end_offset;

	off_end_offset = ALIGN(buffer->data_size, sizeof(void *));
	off_end_offset += buffer->offsets_size;

	binder_transaction_buffer_release(proc, thread, buffer,
					  off_end_offset, is_failure);
}

static int binder_translate_binder(struct flat_binder_object *fp,
				   struct binder_transaction *t,
				   struct binder_thread *thread)
@@ -2806,7 +2820,7 @@ static int binder_proc_transaction(struct binder_transaction *t,
		t_outdated->buffer = NULL;
		buffer->transaction = NULL;
		trace_binder_transaction_update_buffer_release(buffer);
		binder_transaction_buffer_release(proc, NULL, buffer, 0, 0);
		binder_release_entire_buffer(proc, NULL, buffer, false);
		binder_alloc_free_buf(&proc->alloc, buffer);
		kfree(t_outdated);
		binder_stats_deleted(BINDER_STAT_TRANSACTION);
@@ -3775,7 +3789,7 @@ binder_free_buf(struct binder_proc *proc,
		binder_node_inner_unlock(buf_node);
	}
	trace_binder_transaction_buffer_release(buffer);
	binder_transaction_buffer_release(proc, thread, buffer, 0, is_failure);
	binder_release_entire_buffer(proc, thread, buffer, is_failure);
	binder_alloc_free_buf(&proc->alloc, buffer);
}

+29 −35
Original line number Diff line number Diff line
@@ -212,8 +212,8 @@ static int binder_update_page_range(struct binder_alloc *alloc, int allocate,
		mm = alloc->mm;

	if (mm) {
		mmap_read_lock(mm);
		vma = vma_lookup(mm, alloc->vma_addr);
		mmap_write_lock(mm);
		vma = alloc->vma;
	}

	if (!vma && need_mm) {
@@ -270,7 +270,7 @@ static int binder_update_page_range(struct binder_alloc *alloc, int allocate,
		trace_binder_alloc_page_end(alloc, index);
	}
	if (mm) {
		mmap_read_unlock(mm);
		mmap_write_unlock(mm);
		mmput(mm);
	}
	return 0;
@@ -303,21 +303,24 @@ static int binder_update_page_range(struct binder_alloc *alloc, int allocate,
	}
err_no_vma:
	if (mm) {
		mmap_read_unlock(mm);
		mmap_write_unlock(mm);
		mmput(mm);
	}
	return vma ? -ENOMEM : -ESRCH;
}

static inline void binder_alloc_set_vma(struct binder_alloc *alloc,
		struct vm_area_struct *vma)
{
	/* pairs with smp_load_acquire in binder_alloc_get_vma() */
	smp_store_release(&alloc->vma, vma);
}

static inline struct vm_area_struct *binder_alloc_get_vma(
		struct binder_alloc *alloc)
{
	struct vm_area_struct *vma = NULL;

	if (alloc->vma_addr)
		vma = vma_lookup(alloc->mm, alloc->vma_addr);

	return vma;
	/* pairs with smp_store_release in binder_alloc_set_vma() */
	return smp_load_acquire(&alloc->vma);
}

static bool debug_low_async_space_locked(struct binder_alloc *alloc, int pid)
@@ -380,15 +383,13 @@ static struct binder_buffer *binder_alloc_new_buf_locked(
	size_t size, data_offsets_size;
	int ret;

	mmap_read_lock(alloc->mm);
	/* Check binder_alloc is fully initialized */
	if (!binder_alloc_get_vma(alloc)) {
		mmap_read_unlock(alloc->mm);
		binder_alloc_debug(BINDER_DEBUG_USER_ERROR,
				   "%d: binder_alloc_buf, no vma\n",
				   alloc->pid);
		return ERR_PTR(-ESRCH);
	}
	mmap_read_unlock(alloc->mm);

	data_offsets_size = ALIGN(data_size, sizeof(void *)) +
		ALIGN(offsets_size, sizeof(void *));
@@ -778,7 +779,9 @@ int binder_alloc_mmap_handler(struct binder_alloc *alloc,
	buffer->free = 1;
	binder_insert_free_buffer(alloc, buffer);
	alloc->free_async_space = alloc->buffer_size / 2;
	alloc->vma_addr = vma->vm_start;

	/* Signal binder_alloc is fully initialized */
	binder_alloc_set_vma(alloc, vma);

	return 0;

@@ -808,8 +811,7 @@ void binder_alloc_deferred_release(struct binder_alloc *alloc)

	buffers = 0;
	mutex_lock(&alloc->mutex);
	BUG_ON(alloc->vma_addr &&
	       vma_lookup(alloc->mm, alloc->vma_addr));
	BUG_ON(alloc->vma);

	while ((n = rb_first(&alloc->allocated_buffers))) {
		buffer = rb_entry(n, struct binder_buffer, rb_node);
@@ -916,14 +918,7 @@ void binder_alloc_print_pages(struct seq_file *m,
	 * Make sure the binder_alloc is fully initialized, otherwise we might
	 * read inconsistent state.
	 */

	mmap_read_lock(alloc->mm);
	if (binder_alloc_get_vma(alloc) == NULL) {
		mmap_read_unlock(alloc->mm);
		goto uninitialized;
	}

	mmap_read_unlock(alloc->mm);
	if (binder_alloc_get_vma(alloc) != NULL) {
		for (i = 0; i < alloc->buffer_size / PAGE_SIZE; i++) {
			page = &alloc->pages[i];
			if (!page->page_ptr)
@@ -933,8 +928,7 @@ void binder_alloc_print_pages(struct seq_file *m,
			else
				lru++;
		}

uninitialized:
	}
	mutex_unlock(&alloc->mutex);
	seq_printf(m, "  pages: %d:%d:%d\n", active, lru, free);
	seq_printf(m, "  pages high watermark: %zu\n", alloc->pages_high);
@@ -969,7 +963,7 @@ int binder_alloc_get_allocated_count(struct binder_alloc *alloc)
 */
void binder_alloc_vma_close(struct binder_alloc *alloc)
{
	alloc->vma_addr = 0;
	binder_alloc_set_vma(alloc, NULL);
}

/**
+2 −2
Original line number Diff line number Diff line
@@ -75,7 +75,7 @@ struct binder_lru_page {
/**
 * struct binder_alloc - per-binder proc state for binder allocator
 * @mutex:              protects binder_alloc fields
 * @vma_addr:           vm_area_struct->vm_start passed to mmap_handler
 * @vma:                vm_area_struct passed to mmap_handler
 *                      (invariant after mmap)
 * @mm:                 copy of task->mm (invariant after open)
 * @buffer:             base of per-proc address space mapped via mmap
@@ -99,7 +99,7 @@ struct binder_lru_page {
 */
struct binder_alloc {
	struct mutex mutex;
	unsigned long vma_addr;
	struct vm_area_struct *vma;
	struct mm_struct *mm;
	void __user *buffer;
	struct list_head buffers;
+1 −1
Original line number Diff line number Diff line
@@ -287,7 +287,7 @@ void binder_selftest_alloc(struct binder_alloc *alloc)
	if (!binder_selftest_run)
		return;
	mutex_lock(&binder_selftest_lock);
	if (!binder_selftest_run || !alloc->vma_addr)
	if (!binder_selftest_run || !alloc->vma)
		goto done;
	pr_info("STARTED\n");
	binder_selftest_alloc_offset(alloc, end_offset, 0);
+1 −0
Original line number Diff line number Diff line
@@ -402,6 +402,7 @@ static void *etm_setup_aux(struct perf_event *event, void **pages,
		trace_id = coresight_trace_id_get_cpu_id(cpu);
		if (!IS_VALID_CS_TRACE_ID(trace_id)) {
			cpumask_clear_cpu(cpu, mask);
			coresight_release_path(path);
			continue;
		}

Loading