mm/kfence/report.c

Source file repositories/reference/linux-study-clean/mm/kfence/report.c

File Facts

System
Linux kernel
Corpus path
mm/kfence/report.c
Extension
.c
Size
11527 bytes
Lines
376
Domain
Core OS
Bucket
Memory Management
Inferred role
Core OS: implementation source
Status
source implementation candidate

Why This File Exists

Core operating-system implementation surface: boot, tasks, memory, VFS, syscall-facing interfaces, synchronization, credentials, and isolation.

Dependency Surface

Detected Declarations

Annotated Snippet

switch (*type) {
		case KFENCE_ERROR_UAF:
		case KFENCE_ERROR_OOB:
		case KFENCE_ERROR_INVALID:
			/*
			 * kfence_handle_page_fault() may be called with pt_regs
			 * set to NULL; in that case we'll simply show the full
			 * stack trace.
			 */
			return 0;
		case KFENCE_ERROR_CORRUPTION:
		case KFENCE_ERROR_INVALID_FREE:
			break;
		}
	}

	for (skipnr = 0; skipnr < num_entries; skipnr++) {
		int len = scnprintf(buf, sizeof(buf), "%ps", (void *)stack_entries[skipnr]);

		if (str_has_prefix(buf, ARCH_FUNC_PREFIX "kfence_") ||
		    str_has_prefix(buf, ARCH_FUNC_PREFIX "__kfence_") ||
		    str_has_prefix(buf, ARCH_FUNC_PREFIX "__kmem_cache_free") ||
		    !strncmp(buf, ARCH_FUNC_PREFIX "__slab_free", len)) {
			/*
			 * In case of tail calls from any of the below to any of
			 * the above, optimized by the compiler such that the
			 * stack trace would omit the initial entry point below.
			 */
			fallback = skipnr + 1;
		}

		/*
		 * The below list should only include the initial entry points
		 * into the slab allocators. Includes the *_bulk() variants by
		 * checking prefixes.
		 */
		if (str_has_prefix(buf, ARCH_FUNC_PREFIX "kfree") ||
		    str_has_prefix(buf, ARCH_FUNC_PREFIX "kmem_cache_free") ||
		    str_has_prefix(buf, ARCH_FUNC_PREFIX "__kmalloc") ||
		    str_has_prefix(buf, ARCH_FUNC_PREFIX "kmem_cache_alloc"))
			goto found;
	}
	if (fallback < num_entries)
		return fallback;
found:
	skipnr++;
	return skipnr < num_entries ? skipnr : 0;
}

static void kfence_print_stack(struct seq_file *seq, const struct kfence_metadata *meta,
			       bool show_alloc)
	__must_hold(&meta->lock)
{
	const struct kfence_track *track = show_alloc ? &meta->alloc_track : &meta->free_track;
	u64 ts_sec = track->ts_nsec;
	unsigned long rem_nsec = do_div(ts_sec, NSEC_PER_SEC);
	u64 interval_nsec = local_clock() - track->ts_nsec;
	unsigned long rem_interval_nsec = do_div(interval_nsec, NSEC_PER_SEC);

	/* Timestamp matches printk timestamp format. */
	seq_con_printf(seq, "%s by task %d on cpu %d at %lu.%06lus (%lu.%06lus ago):\n",
		       show_alloc ? "allocated" : meta->state == KFENCE_OBJECT_RCU_FREEING ?
		       "rcu freeing" : "freed", track->pid,
		       track->cpu, (unsigned long)ts_sec, rem_nsec / 1000,
		       (unsigned long)interval_nsec, rem_interval_nsec / 1000);

	if (track->num_stack_entries) {
		/* Skip allocation/free internals stack. */
		int i = get_stack_skipnr(track->stack_entries, track->num_stack_entries, NULL);

		/* stack_trace_seq_print() does not exist; open code our own. */
		for (; i < track->num_stack_entries; i++)
			seq_con_printf(seq, " %pS\n", (void *)track->stack_entries[i]);
	} else {
		seq_con_printf(seq, " no %s stack\n", show_alloc ? "allocation" : "deallocation");
	}
}

void kfence_print_object(struct seq_file *seq, const struct kfence_metadata *meta)
{
	const int size = abs(meta->size);
	const unsigned long start = meta->addr;
	const struct kmem_cache *const cache = meta->cache;

	lockdep_assert_held(&meta->lock);

	if (meta->state == KFENCE_OBJECT_UNUSED) {
		seq_con_printf(seq, "kfence-#%td unused\n", meta - kfence_metadata);
		return;
	}

Annotation

Implementation Notes