Skip to content

CFI failure in blake2s_update() #1567

Closed
@nathanchance

Description

@nathanchance

Initially reported by CI on android-mainline: https://github.com/ClangBuiltLinux/continuous-integration2/runs/4824153007?check_suite_focus=true

$ make -skj"$(nproc)" ARCH=arm64 LLVM=1 distclean defconfig

$ scripts/config -d LTO_NONE -e LTO_CLANG_FULL -e CFI_CLANG

$ make -skj"$(nproc)" ARCH=arm64 LLVM=1 olddefconfig Image.gz

$ boot-qemu.sh -a arm64 -k . -t 30s
...
[    0.000000] Linux version 5.16.0-rc8-00056-g9f9eff85a008 (nathan@archlinux-ax161) (ClangBuiltLinux clang version 14.0.0 (https://github.com/llvm/llvm-project 1441ffe6a6da90e9c4800914eb0005b1087aa5d2), LLD 14.0.0) #1 SMP PREEMPT Sat Jan
 15 19:17:16 UTC 2022
...
[    0.000000] Kernel panic - not syncing: CFI failure (target: blake2s_compress+0x0/0x144c)
[    0.000000] CPU: 0 PID: 0 Comm: swapper/0 Not tainted 5.16.0-rc8-00056-g9f9eff85a008 #1
[    0.000000] Hardware name: linux,dummy-virt (DT)
[    0.000000] Call trace:
[    0.000000]  dump_backtrace+0x0/0x8
[    0.000000]  dump_stack_lvl+0x7c/0xb0
[    0.000000]  panic+0x174/0x444
[    0.000000]  __cfi_check_fail+0x50/0x54
[    0.000000]  __cfi_slowpath_diag+0x1a0/0x240
[    0.000000]  blake2s_update+0x13c/0x168
[    0.000000]  _extract_entropy+0x158/0x400
[    0.000000]  crng_initialize_primary+0x4c/0xc0
[    0.000000]  rand_initialize+0x14/0x50
[    0.000000]  start_kernel+0x2fc/0x6ac
[    0.000000]  __primary_switched+0xc0/0x7f3c
[    0.000000] ---[ end Kernel panic - not syncing: CFI failure (target: blake2s_compress+0x0/0x144c) ]---

This happens after commit 9f9eff85a008 ("random: use BLAKE2s instead of SHA1 in extraction") (and I assume its prerequisite, commit 6048fdcc5f26 ("lib/crypto: blake2s: include as built-in"), has something to do with this as well).

It does not happen with ThinLTO, which is peculiar (and explains how our CI did not see it in the regular mainline builds, as we test CFI with ThinLTO).

blake2s_update() is a wrapper for __blake2s_update(), which calls blake2s_compress() as the fourth parameter, which should be of type blake2s_compress_t.

lib/crypto/blake2s.c:

void blake2s_update(struct blake2s_state *state, const u8 *in, size_t inlen)
{
    __blake2s_update(state, in, inlen, blake2s_compress);
}
EXPORT_SYMBOL(blake2s_update);

lib/crypto/blake2s-generic.c

void blake2s_compress(struct blake2s_state *state, const u8 *block,
              size_t nblocks, const u32 inc)
              __weak __alias(blake2s_compress_generic);

void blake2s_compress_generic(struct blake2s_state *state, const u8 *block,
                  size_t nblocks, const u32 inc)
{
...

include/crypto/internal/blake2s.h:

void blake2s_compress_generic(struct blake2s_state *state, const u8 *block,
                  size_t nblocks, const u32 inc);

void blake2s_compress(struct blake2s_state *state, const u8 *block,
              size_t nblocks, const u32 inc);

...

typedef void (*blake2s_compress_t)(struct blake2s_state *state,
                   const u8 *block, size_t nblocks, u32 inc);

...

static inline void __blake2s_update(struct blake2s_state *state,
                    const u8 *in, size_t inlen,
                    blake2s_compress_t compress)
{
    const size_t fill = BLAKE2S_BLOCK_SIZE - state->buflen;

    if (unlikely(!inlen))
        return;
    if (inlen > fill) {
        memcpy(state->buf + state->buflen, in, fill);
        (*compress)(state, state->buf, 1, BLAKE2S_BLOCK_SIZE);
        state->buflen = 0;
        in += fill;
        inlen -= fill;
    }
    if (inlen > BLAKE2S_BLOCK_SIZE) {
        const size_t nblocks = DIV_ROUND_UP(inlen, BLAKE2S_BLOCK_SIZE);
        /* Hash one less (full) block than strictly possible */
        (*compress)(state, in, nblocks - 1, BLAKE2S_BLOCK_SIZE);
        in += BLAKE2S_BLOCK_SIZE * (nblocks - 1);
        inlen -= BLAKE2S_BLOCK_SIZE * (nblocks - 1);
    }
    memcpy(state->buf + state->buflen, in, inlen);
    state->buflen += inlen;
}

I tried changing the fourth parameter of blake2s_compress_t from u32 inc to const u32 inc but that did not make a difference:

diff --git a/include/crypto/internal/blake2s.h b/include/crypto/internal/blake2s.h
index d39cfa0d333e..bb7438b9cc10 100644
--- a/include/crypto/internal/blake2s.h
+++ b/include/crypto/internal/blake2s.h
@@ -25,7 +25,7 @@ static inline void blake2s_set_lastblock(struct blake2s_state *state)
 }

 typedef void (*blake2s_compress_t)(struct blake2s_state *state,
-                                  const u8 *block, size_t nblocks, u32 inc);
+                                  const u8 *block, size_t nblocks, const u32 inc);

 /* Helper functions for BLAKE2s shared by the library and shash APIs */

Could it be something going wrong with the __weak __alias(blake2s_compress_generic) part of blake2s_compress()?

cc @samitolvanen

Metadata

Metadata

Assignees

No one assigned

    Labels

    [ARCH] arm64This bug impacts ARCH=arm64[BUG] llvmA bug that should be fixed in upstream LLVM[FEATURE] CFIRelated to building the kernel with Clang Control Flow Integrity[WORKAROUND] AppliedThis bug has an applied workaround

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions