Commit e724918b authored by Linus Torvalds's avatar Linus Torvalds
Browse files
Pull hardening fixes from Kees Cook:

 - gcc-plugins: randstruct: Remove GCC 4.7 or newer requirement
   (Thorsten Blum)

 - kallsyms: Clean up interaction with LTO suffixes (Song Liu)

 - refcount: Report UAF for refcount_sub_and_test(0) when counter==0
   (Petr Pavlu)

 - kunit/overflow: Avoid misallocation of driver name (Ivan Orlov)

* tag 'hardening-v6.11-rc4' of git://git.kernel.org/pub/scm/linux/kernel/git/kees/linux:
  kallsyms: Match symbols exactly with CONFIG_LTO_CLANG
  kallsyms: Do not cleanup .llvm.<hash> suffix before sorting symbols
  kunit/overflow: Fix UB in overflow_allocation_test
  gcc-plugins: randstruct: Remove GCC 4.7 or newer requirement
  refcount: Report UAF for refcount_sub_and_test(0) when counter==0
parents a4a35f6c fb6a421f
Loading
Loading
Loading
Loading
+16 −0
Original line number Diff line number Diff line
@@ -182,6 +182,21 @@ static void lkdtm_REFCOUNT_SUB_AND_TEST_NEGATIVE(void)
	check_negative(&neg, 3);
}

/*
 * A refcount_sub_and_test() by zero when the counter is at zero should act like
 * refcount_sub_and_test() above when going negative.
 */
static void lkdtm_REFCOUNT_SUB_AND_TEST_ZERO(void)
{
	refcount_t neg = REFCOUNT_INIT(0);

	pr_info("attempting bad refcount_sub_and_test() at zero\n");
	if (refcount_sub_and_test(0, &neg))
		pr_warn("Weird: refcount_sub_and_test() reported zero\n");

	check_negative(&neg, 0);
}

static void check_from_zero(refcount_t *ref)
{
	switch (refcount_read(ref)) {
@@ -400,6 +415,7 @@ static struct crashtype crashtypes[] = {
	CRASHTYPE(REFCOUNT_DEC_NEGATIVE),
	CRASHTYPE(REFCOUNT_DEC_AND_TEST_NEGATIVE),
	CRASHTYPE(REFCOUNT_SUB_AND_TEST_NEGATIVE),
	CRASHTYPE(REFCOUNT_SUB_AND_TEST_ZERO),
	CRASHTYPE(REFCOUNT_INC_ZERO),
	CRASHTYPE(REFCOUNT_ADD_ZERO),
	CRASHTYPE(REFCOUNT_INC_SATURATED),
+2 −2
Original line number Diff line number Diff line
@@ -266,12 +266,12 @@ bool __refcount_sub_and_test(int i, refcount_t *r, int *oldp)
	if (oldp)
		*oldp = old;

	if (old == i) {
	if (old > 0 && old == i) {
		smp_acquire__after_ctrl_dep();
		return true;
	}

	if (unlikely(old < 0 || old - i < 0))
	if (unlikely(old <= 0 || old - i < 0))
		refcount_warn_saturate(r, REFCOUNT_SUB_UAF);

	return false;
+6 −49
Original line number Diff line number Diff line
@@ -160,38 +160,6 @@ unsigned long kallsyms_sym_address(int idx)
	return kallsyms_relative_base - 1 - kallsyms_offsets[idx];
}

static void cleanup_symbol_name(char *s)
{
	char *res;

	if (!IS_ENABLED(CONFIG_LTO_CLANG))
		return;

	/*
	 * LLVM appends various suffixes for local functions and variables that
	 * must be promoted to global scope as part of LTO.  This can break
	 * hooking of static functions with kprobes. '.' is not a valid
	 * character in an identifier in C. Suffixes only in LLVM LTO observed:
	 * - foo.llvm.[0-9a-f]+
	 */
	res = strstr(s, ".llvm.");
	if (res)
		*res = '\0';

	return;
}

static int compare_symbol_name(const char *name, char *namebuf)
{
	/* The kallsyms_seqs_of_names is sorted based on names after
	 * cleanup_symbol_name() (see scripts/kallsyms.c) if clang lto is enabled.
	 * To ensure correct bisection in kallsyms_lookup_names(), do
	 * cleanup_symbol_name(namebuf) before comparing name and namebuf.
	 */
	cleanup_symbol_name(namebuf);
	return strcmp(name, namebuf);
}

static unsigned int get_symbol_seq(int index)
{
	unsigned int i, seq = 0;
@@ -219,7 +187,7 @@ static int kallsyms_lookup_names(const char *name,
		seq = get_symbol_seq(mid);
		off = get_symbol_offset(seq);
		kallsyms_expand_symbol(off, namebuf, ARRAY_SIZE(namebuf));
		ret = compare_symbol_name(name, namebuf);
		ret = strcmp(name, namebuf);
		if (ret > 0)
			low = mid + 1;
		else if (ret < 0)
@@ -236,7 +204,7 @@ static int kallsyms_lookup_names(const char *name,
		seq = get_symbol_seq(low - 1);
		off = get_symbol_offset(seq);
		kallsyms_expand_symbol(off, namebuf, ARRAY_SIZE(namebuf));
		if (compare_symbol_name(name, namebuf))
		if (strcmp(name, namebuf))
			break;
		low--;
	}
@@ -248,7 +216,7 @@ static int kallsyms_lookup_names(const char *name,
			seq = get_symbol_seq(high + 1);
			off = get_symbol_offset(seq);
			kallsyms_expand_symbol(off, namebuf, ARRAY_SIZE(namebuf));
			if (compare_symbol_name(name, namebuf))
			if (strcmp(name, namebuf))
				break;
			high++;
		}
@@ -407,8 +375,7 @@ static int kallsyms_lookup_buildid(unsigned long addr,
		if (modbuildid)
			*modbuildid = NULL;

		ret = strlen(namebuf);
		goto found;
		return strlen(namebuf);
	}

	/* See if it's in a module or a BPF JITed image. */
@@ -422,8 +389,6 @@ static int kallsyms_lookup_buildid(unsigned long addr,
		ret = ftrace_mod_address_lookup(addr, symbolsize,
						offset, modname, namebuf);

found:
	cleanup_symbol_name(namebuf);
	return ret;
}

@@ -450,8 +415,6 @@ const char *kallsyms_lookup(unsigned long addr,

int lookup_symbol_name(unsigned long addr, char *symname)
{
	int res;

	symname[0] = '\0';
	symname[KSYM_NAME_LEN - 1] = '\0';

@@ -462,16 +425,10 @@ int lookup_symbol_name(unsigned long addr, char *symname)
		/* Grab name */
		kallsyms_expand_symbol(get_symbol_offset(pos),
				       symname, KSYM_NAME_LEN);
		goto found;
		return 0;
	}
	/* See if it's in a module. */
	res = lookup_module_symbol_name(addr, symname);
	if (res)
		return res;

found:
	cleanup_symbol_name(symname);
	return 0;
	return lookup_module_symbol_name(addr, symname);
}

/* Look up a kernel symbol and return it in a text buffer. */
+1 −21
Original line number Diff line number Diff line
@@ -187,31 +187,11 @@ static void test_perf_kallsyms_lookup_name(void)
		stat.min, stat.max, div_u64(stat.sum, stat.real_cnt));
}

static bool match_cleanup_name(const char *s, const char *name)
{
	char *p;
	int len;

	if (!IS_ENABLED(CONFIG_LTO_CLANG))
		return false;

	p = strstr(s, ".llvm.");
	if (!p)
		return false;

	len = strlen(name);
	if (p - s != len)
		return false;

	return !strncmp(s, name, len);
}

static int find_symbol(void *data, const char *name, unsigned long addr)
{
	struct test_stat *stat = (struct test_stat *)data;

	if (strcmp(name, stat->name) == 0 ||
	    (!stat->perf && match_cleanup_name(name, stat->name))) {
	if (!strcmp(name, stat->name)) {
		stat->real_cnt++;
		stat->addr = addr;

+1 −2
Original line number Diff line number Diff line
@@ -668,7 +668,6 @@ DEFINE_TEST_ALLOC(devm_kzalloc, devm_kfree, 1, 1, 0);

static void overflow_allocation_test(struct kunit *test)
{
	const char device_name[] = "overflow-test";
	struct device *dev;
	int count = 0;

@@ -678,7 +677,7 @@ static void overflow_allocation_test(struct kunit *test)
} while (0)

	/* Create dummy device for devm_kmalloc()-family tests. */
	dev = kunit_device_register(test, device_name);
	dev = kunit_device_register(test, "overflow-test");
	KUNIT_ASSERT_FALSE_MSG(test, IS_ERR(dev),
			       "Cannot register test device\n");

Loading