2021-10-04 22:37:24

by Nick Desaulniers

[permalink] [raw]
Subject: [PATCH v6] kallsyms: strip LTO suffixes from static functions

Similar to:
commit 8b8e6b5d3b01 ("kallsyms: strip ThinLTO hashes from static
functions")

It's very common for compilers to modify the symbol name for static
functions as part of optimizing transformations. That makes hooking
static functions (that weren't inlined or DCE'd) with kprobes difficult.

LLVM has yet another name mangling scheme used by thin LTO.

Combine handling of the various schemes by truncating after the first
'.'. Strip off these suffixes so that we can continue to hook such
static functions. Clang releases prior to clang-13 would use '$'
instead of '.'

Link: https://reviews.llvm.org/rGc6e5c4654bd5045fe22a1a52779e48e2038a404c
Reported-by: KE.LI(Lieke) <[email protected]>
Suggested-by: Nathan Chancellor <[email protected]>
Suggested-by: Padmanabha Srinivasaiah <[email protected]>
Suggested-by: Sami Tolvanen <[email protected]>
Reviewed-by: Nathan Chancellor <[email protected]>
Reviewed-by: Fangrui Song <[email protected]>
Reviewed-by: Sami Tolvanen <[email protected]>
Signed-off-by: Nick Desaulniers <[email protected]>
---
Changes v5 -> v6:
* Replace __clang_major__ with CONFIG_CLANG_VERSION. The former is not
defined for CC=gcc while the latter is.
* Reflow comment that wasn't wrapper properly. gq in vim behaves
differently based on whether tabstop=8 was set.
* Pick up Sami's RB tag.

Changes v4 -> v5:
* Absorb Padmanabha Srinivasaiah's patch from
https://lore.kernel.org/lkml/[email protected]/.
* Add Padmanabha's Suggested-by tag.
* Rewrite the patch to truncate after first '.', as per Sami's comment
from
https://lore.kernel.org/lkml/CABCJKue5Ay6_+8sibzh5wRh3gPzV1g72gJ9m2ot4E1ezj8bpHA@mail.gmail.com/.
* Add Sami's Suggested-by tag.
* Verify that the '$' delimiter only appears for
thin LTO + CFI + clang <= 12, use __clang_minor__ to check.
* Update comments as per Nathan + Fangrui, add their Suggested-by tags.
* While Nathan + Fangrui did review v4, v5 is too different IMO to carry
those tags forward.

Changes v3 -> v4:
* Convert this function to use IS_ENABLED rather than provide multiple
definitions based on preprocessor checks.
* Add Nathan's suggested-by.

Changes v2 -> v3:
* Un-nest preprocessor checks, as per Nathan.

Changes v1 -> v2:
* Both mangling schemes can occur for thinLTO + CFI, this new scheme can
also occur for thinLTO without CFI. Split cleanup_symbol_name() into
two function calls.
* Drop KE.LI's tested by tag.
* Do not carry Fangrui's Reviewed by tag.
* Drop the inline keyword; it is meaningless.

kernel/kallsyms.c | 46 +++++++++++++++++++++++++++++++++-------------
1 file changed, 33 insertions(+), 13 deletions(-)

diff --git a/kernel/kallsyms.c b/kernel/kallsyms.c
index 0ba87982d017..3011bc33a5ba 100644
--- a/kernel/kallsyms.c
+++ b/kernel/kallsyms.c
@@ -164,26 +164,46 @@ static unsigned long kallsyms_sym_address(int idx)
return kallsyms_relative_base - 1 - kallsyms_offsets[idx];
}

-#if defined(CONFIG_CFI_CLANG) && defined(CONFIG_LTO_CLANG_THIN)
-/*
- * LLVM appends a hash to static function names when ThinLTO and CFI are
- * both enabled, i.e. foo() becomes foo$707af9a22804d33c81801f27dcfe489b.
- * This causes confusion and potentially breaks user space tools, so we
- * strip the suffix from expanded symbol names.
- */
-static inline bool cleanup_symbol_name(char *s)
+static bool cleanup_symbol_name(char *s)
{
char *res;

+ if (!IS_ENABLED(CONFIG_LTO_CLANG))
+ return false;
+
+ /*
+ * 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 observed:
+ * - foo.llvm.[0-9a-f]+
+ * - foo.[0-9a-f]+
+ * - foo.[0-9a-f]+.cfi_jt
+ */
+ res = strchr(s, '.');
+ if (res) {
+ *res = '\0';
+ return true;
+ }
+
+ if (!IS_ENABLED(CONFIG_CFI_CLANG) ||
+ !IS_ENABLED(CONFIG_LTO_CLANG_THIN) ||
+ CONFIG_CLANG_VERSION >= 130000)
+ return false;
+
+ /*
+ * Prior to LLVM 13, the following suffixes were observed when thinLTO
+ * and CFI are both enabled:
+ * - foo$[0-9]+
+ */
res = strrchr(s, '$');
- if (res)
+ if (res) {
*res = '\0';
+ return true;
+ }

- return res != NULL;
+ return false;
}
-#else
-static inline bool cleanup_symbol_name(char *s) { return false; }
-#endif

/* Lookup the address for this symbol. Returns 0 if not found. */
unsigned long kallsyms_lookup_name(const char *name)

base-commit: 4de593fb965fc2bd11a0b767e0c65ff43540a6e4
--
2.33.0.800.g4c38ced690-goog


2021-10-04 23:28:44

by Kees Cook

[permalink] [raw]
Subject: Re: [PATCH v6] kallsyms: strip LTO suffixes from static functions

On Mon, 4 Oct 2021 09:29:33 -0700, Nick Desaulniers wrote:
> Similar to:
> commit 8b8e6b5d3b01 ("kallsyms: strip ThinLTO hashes from static
> functions")
>
> It's very common for compilers to modify the symbol name for static
> functions as part of optimizing transformations. That makes hooking
> static functions (that weren't inlined or DCE'd) with kprobes difficult.
>
> [...]

Applied to for-next/hardening, thanks!

[1/1] kallsyms: strip LTO suffixes from static functions
https://git.kernel.org/kees/c/6eb4bd92c1ce

Also,if you get a moment, can you set up patatt (or b4) to sign your
outgoing patches? DKIM is nice, but I'd like to see more folks signing
their emailed patches: https://github.com/mricon/patatt

tl;dr:

add to ~/.gitconfig:
[patatt]
signingkey = openpgp:FINGERPRINT-HERE

# install git sendemail hook (assuming you send with git)
$ patatt install-hook

# or do it manually before sending:
$ patatt sign $file-with-email

Thanks!

--
Kees Cook

2021-10-04 23:45:27

by Konstantin Ryabitsev

[permalink] [raw]
Subject: Re: [PATCH v6] kallsyms: strip LTO suffixes from static functions

On Mon, Oct 04, 2021 at 03:45:21PM -0400, Konstantin Ryabitsev wrote:
> So, this line:
>
> > fatal: command 'git hook run sendemail-validate -- <patch>' died with exit code 1
>
> Suggests to me that your git version is patched to carry the
> new-and-not-yet-accepted "git hook" changes. It seems that it both opens a
> stdin and passes the file as the parameter. When we find a stdin, we always
> grab that instead of the passed parameter, but that's only a problem with this
> change to git.

Patatt version 0.4.9 is out and should fix this problem. Glad we caught it
before the fancy new git-hook patches are in upstream git. :)

-K

2021-10-04 23:58:10

by Nick Desaulniers

[permalink] [raw]
Subject: Re: [PATCH v6] kallsyms: strip LTO suffixes from static functions

On Mon, Oct 4, 2021 at 1:37 PM Konstantin Ryabitsev
<[email protected]> wrote:
>
> On Mon, Oct 04, 2021 at 03:45:21PM -0400, Konstantin Ryabitsev wrote:
> > So, this line:
> >
> > > fatal: command 'git hook run sendemail-validate -- <patch>' died with exit code 1
> >
> > Suggests to me that your git version is patched to carry the
> > new-and-not-yet-accepted "git hook" changes. It seems that it both opens a
> > stdin and passes the file as the parameter. When we find a stdin, we always
> > grab that instead of the passed parameter, but that's only a problem with this
> > change to git.
>
> Patatt version 0.4.9 is out and should fix this problem. Glad we caught it
> before the fancy new git-hook patches are in upstream git. :)

$ pip install --user patatt --upgrade
$ git send-email ...
$ echo $?
0

:) LGTM, thanks Konstantin. Did I mention that b4 has significantly
improved my workflow contributing to the Linux kernel?
--
Thanks,
~Nick Desaulniers

2021-10-05 13:08:11

by Konstantin Ryabitsev

[permalink] [raw]
Subject: Re: [PATCH v6] kallsyms: strip LTO suffixes from static functions

On Mon, Oct 04, 2021 at 04:22:47PM -0700, Nick Desaulniers wrote:
> :) LGTM, thanks Konstantin. Did I mention that b4 has significantly
> improved my workflow contributing to the Linux kernel?

Great to hear that, thanks! :)

-K