perf genelf: BLAKE2s build ID generation
Subsystems (2)
v2 cover perf genelf: BLAKE2s build ID generation
This series upgrades perf's build ID generation to a more modern hash algorithm and switches to an incremental hashing API.
It also fixes an issue where different (code, symtab, strsym) tuples didn't necessarily result in different hashes.
Note that the size of the build ID field stays the same.
This applies to the perf-tools-next branch of
https://git.kernel.org/pub/scm/linux/kernel/git/perf/perf-tools-next.git
Changed in v2:
- Split into three patches
- Improved a couple comments
Eric Biggers (3):
perf util: Add BLAKE2s support
perf genelf: Switch from SHA-1 to BLAKE2s for build ID generation perf util: Remove SHA-1 code
tools/perf/tests/util.c | 85 +++++++++++++-------- tools/perf/util/Build | 2 +- tools/perf/util/blake2s.c | 151 ++++++++++++++++++++++++++++++++++++++ tools/perf/util/blake2s.h | 73 ++++++++++++++++++ tools/perf/util/genelf.c | 58 +++++++-------- tools/perf/util/sha1.c | 97 ------------------------ tools/perf/util/sha1.h | 6 -- 7 files changed, 309 insertions(+), 163 deletions(-) create mode 100644 tools/perf/util/blake2s.c create mode 100644 tools/perf/util/blake2s.h delete mode 100644 tools/perf/util/sha1.c delete mode 100644 tools/perf/util/sha1.h
base-commit: 2eeb09fe1c5173b659929f92fee4461796ca8c14
Signature · 1 line
2.52.0
PATCH v2 1/3 perf util: Add BLAKE2s support
Add BLAKE2s support to the perf utility library. The code is borrowed from the kernel. This will replace the use of SHA-1 in genelf.c.
--- tools/perf/tests/util.c | 78 +++++++++++++++++++- tools/perf/util/Build | 1 + tools/perf/util/blake2s.c | 151 ++++++++++++++++++++++++++++++++++++++ tools/perf/util/blake2s.h | 73 ++++++++++++++++++ 4 files changed, 302 insertions(+), 1 deletion(-) create mode 100644 tools/perf/util/blake2s.c create mode 100644 tools/perf/util/blake2s.h
tools/perf/tests/util.c +77 -1
@@ -1,7 +1,8 @@// SPDX-License-Identifier: GPL-2.0 #include "tests.h" +#include "util/blake2s.h" #include "util/debug.h" #include "util/sha1.h" #include <linux/compiler.h> #include <stdlib.h>
@@ -57,18 +58,93 @@ static int test_sha1(void)memcmp (digest_of_digests, expected_digest_of_digests, SHA1_DIGEST_SIZE) == 0);return 0 ; } +/* Maximum data length tested by test_blake2s() */ +#define MAX_DATA_LEN 512 + +/* +* Hash length tested by test_blake2s(). BLAKE2s supports variable-length +* hashes. However, the only user of BLAKE2s in 'perf' uses 20-byte hashes, +* matching the length of the ELF build ID field. So that's the length we test. +*/ +#define HASH_LEN 20 + +/* Test the implementation of the BLAKE2s hash algorithm. */ +static int test_blake2s (void ) +{ +u8 data[MAX_DATA_LEN ]; +u8 hash[HASH_LEN ]; +u8 hash2[HASH_LEN ]; +struct blake2s_ctx main_ctx; +/* +* This value was generated by the following Python code: +* +* import hashlib +* +* data = bytes(i % 256 for i in range(513)) +* h = hashlib.blake2s(digest_size=20) +* for i in range(513): +* h.update(hashlib.blake2s(data=data[:i], digest_size=20).digest()) +* print(h.hexdigest()) +*/ +static const u8 expected_hash_of_hashes[20 ] = { +0xef ,0x9b ,0x13 ,0x98 ,0x78 ,0x8e ,0x74 ,0x59 ,0x9c ,0xd5 , +0x0c ,0xf0 ,0x33 ,0x97 ,0x79 ,0x3d ,0x3e ,0xd0 ,0x95 ,0xa6 + }; +size_t i; + +/* Generate MAX_DATA_LEN bytes of data. */ +for (i =0 ; i <MAX_DATA_LEN ; i++) + data[i] = i; + +blake2s_init (&main_ctx,sizeof (hash)); +for (i =0 ; i <=MAX_DATA_LEN ; i++) { +struct blake2s_ctx ctx; + +/* Compute the BLAKE2s hash of 'i' data bytes. */ +blake2s_init (&ctx,HASH_LEN ); +blake2s_update (&ctx, data, i); +blake2s_final (&ctx, hash); + +/* Verify that multiple updates produce the same result. */ +blake2s_init (&ctx,HASH_LEN ); +blake2s_update (&ctx, data, i /2 ); +blake2s_update (&ctx, &data[i /2 ], i - (i /2 )); +blake2s_final (&ctx, hash2); +TEST_ASSERT_VAL ("inconsistent BLAKE2s hashes" , +memcmp (hash, hash2,HASH_LEN ) ==0 ); + +/* +* Pass the hash to another BLAKE2s context, so that we +* incrementally compute the hash of all the hashes. +*/ +blake2s_update (&main_ctx, hash,HASH_LEN ); + } + +/* Verify the hash of all the hashes. */ +blake2s_final (&main_ctx, hash); +TEST_ASSERT_VAL ("wrong BLAKE2s hashes" , +memcmp (hash, expected_hash_of_hashes,HASH_LEN ) ==0 ); +return 0 ; +} +static int test__util (struct test_suite *t__maybe_unused ,int subtest__maybe_unused ) { +int ret; +TEST_ASSERT_VAL ("empty string" ,test_strreplace (' ' ,"" ,"123" ,"" ));TEST_ASSERT_VAL ("no match" ,test_strreplace ('5' ,"123" ,"4" ,"123" ));TEST_ASSERT_VAL ("replace 1" ,test_strreplace ('3' ,"123" ,"4" ,"124" ));TEST_ASSERT_VAL ("replace 2" ,test_strreplace ('a' ,"abcabc" ,"ef" ,"efbcefbc" ));TEST_ASSERT_VAL ("replace long" ,test_strreplace ('a' ,"abcabc" ,"longlong" ,"longlongbclonglongbc" )); -return test_sha1 (); + ret =test_sha1 (); +if (ret !=TEST_OK ) +return ret; + +return test_blake2s (); }DEFINE_SUITE ("util" , util);
tools/perf/util/Build +1 -0
@@ -3,10 +3,11 @@ include $(srctree)/tools/scripts/utilities.mak perf-util-y += arm64-frame-pointer-unwind-support.o perf-util-y += addr2line.o perf-util-y += addr_location.o perf-util-y += annotate.o +perf-util-y += blake2s.o perf-util-y += block-info.o perf-util-y += block-range.o perf-util-y += build-id.o perf-util-y += cacheline.o perf-util-y += capstone.o
tools/perf/util/blake2s.c new file +151 -0
151 lines not on this page · raw message
tools/perf/util/blake2s.h new file +73 -0
73 lines not on this page · raw message
Signature · 1 line
2.52.0
Arnaldo Carvalho de Melo
maintainerRe: [PATCH v2 1/3] perf util: Add BLAKE2s support
Quoted 2 lines · Eric Biggers
Add BLAKE2s support to the perf utility library. The code is borrowed from the kernel.
We have tools/perf/check-headers.sh to check for drifts between things we borrow from the kernel, so that when the kernel fixes or improves things we borrowed, we notice and check if it makes sense for us to update the copy, see tools/include/uapi/README.
In this case the copy isn't verbatim, as not all is needed, perhaps it could be just to facilitate? I haven't checked, just mentioning it while processing this series, I'll check that later, maybe.
- Arnaldo
Quoted 375 lines
This will replace the use of SHA-1 in
genelf.c.Tested-by Ian Rogers <[email protected]>Signed-off-by Eric Biggers <[email protected]>--- tools/perf/tests/util.c | 78 +++++++++++++++++++- tools/perf/util/Build | 1 + tools/perf/util/blake2s.c | 151 ++++++++++++++++++++++++++++++++++++++ tools/perf/util/blake2s.h | 73 ++++++++++++++++++ 4 files changed, 302 insertions(+), 1 deletion(-) create mode 100644 tools/perf/util/blake2s.c create mode 100644 tools/perf/util/blake2s.htools/perf/tests/util.c +77
-1@@ -1,7 +1,8 @@// SPDX-License-Identifier: GPL-2.0 #include "tests.h" +#include "util/blake2s.h" #include "util/debug.h" #include "util/sha1.h" #include <linux/compiler.h> #include <stdlib.h> @@ -57,18 +58,93 @@ static int test_sha1(void)memcmp (digest_of_digests, expected_digest_of_digests, SHA1_DIGEST_SIZE) == 0);return 0 ; } +/* Maximum data length tested by test_blake2s() */ +#define MAX_DATA_LEN 512 + +/* +* Hash length tested by test_blake2s(). BLAKE2s supports variable-length +* hashes. However, the only user of BLAKE2s in 'perf' uses 20-byte hashes, +* matching the length of the ELF build ID field. So that's the length we test. +*/ +#define HASH_LEN 20 + +/* Test the implementation of the BLAKE2s hash algorithm. */ +static int test_blake2s (void ) +{ +u8 data[MAX_DATA_LEN ]; +u8 hash[HASH_LEN ]; +u8 hash2[HASH_LEN ]; +struct blake2s_ctx main_ctx; +/* +* This value was generated by the following Python code: +* +* import hashlib +* +* data = bytes(i % 256 for i in range(513)) +* h = hashlib.blake2s(digest_size=20) +* for i in range(513): +* h.update(hashlib.blake2s(data=data[:i], digest_size=20).digest()) +* print(h.hexdigest()) +*/ +static const u8 expected_hash_of_hashes[20 ] = { +0xef ,0x9b ,0x13 ,0x98 ,0x78 ,0x8e ,0x74 ,0x59 ,0x9c ,0xd5 , +0x0c ,0xf0 ,0x33 ,0x97 ,0x79 ,0x3d ,0x3e ,0xd0 ,0x95 ,0xa6 + }; +size_t i; + +/* Generate MAX_DATA_LEN bytes of data. */ +for (i =0 ; i <MAX_DATA_LEN ; i++) + data[i] = i; + +blake2s_init (&main_ctx,sizeof (hash)); +for (i =0 ; i <=MAX_DATA_LEN ; i++) { +struct blake2s_ctx ctx; + +/* Compute the BLAKE2s hash of 'i' data bytes. */ +blake2s_init (&ctx,HASH_LEN ); +blake2s_update (&ctx, data, i); +blake2s_final (&ctx, hash); + +/* Verify that multiple updates produce the same result. */ +blake2s_init (&ctx,HASH_LEN ); +blake2s_update (&ctx, data, i /2 ); +blake2s_update (&ctx, &data[i /2 ], i - (i /2 )); +blake2s_final (&ctx, hash2); +TEST_ASSERT_VAL ("inconsistent BLAKE2s hashes" , +memcmp (hash, hash2,HASH_LEN ) ==0 ); + +/* +* Pass the hash to another BLAKE2s context, so that we +* incrementally compute the hash of all the hashes. +*/ +blake2s_update (&main_ctx, hash,HASH_LEN ); + } + +/* Verify the hash of all the hashes. */ +blake2s_final (&main_ctx, hash); +TEST_ASSERT_VAL ("wrong BLAKE2s hashes" , +memcmp (hash, expected_hash_of_hashes,HASH_LEN ) ==0 ); +return 0 ; +} +static int test__util (struct test_suite *t__maybe_unused ,int subtest__maybe_unused ) { +int ret; +TEST_ASSERT_VAL ("empty string" ,test_strreplace (' ' ,"" ,"123" ,"" ));TEST_ASSERT_VAL ("no match" ,test_strreplace ('5' ,"123" ,"4" ,"123" ));TEST_ASSERT_VAL ("replace 1" ,test_strreplace ('3' ,"123" ,"4" ,"124" ));TEST_ASSERT_VAL ("replace 2" ,test_strreplace ('a' ,"abcabc" ,"ef" ,"efbcefbc" ));TEST_ASSERT_VAL ("replace long" ,test_strreplace ('a' ,"abcabc" ,"longlong" ,"longlongbclonglongbc" )); -return test_sha1 (); + ret =test_sha1 (); +if (ret !=TEST_OK ) +return ret; + +return test_blake2s (); }DEFINE_SUITE ("util" , util);tools/perf/util/Build +1
-0@@ -3,10 +3,11 @@ include $(srctree)/tools/scripts/utilities.mak perf-util-y += arm64-frame-pointer-unwind-support.o perf-util-y += addr2line.o perf-util-y += addr_location.o perf-util-y += annotate.o +perf-util-y += blake2s.o perf-util-y += block-info.o perf-util-y += block-range.o perf-util-y += build-id.o perf-util-y += cacheline.o perf-util-y += capstone.otools/perf/util/blake2s.c new file +151
-0@@ -0,0 +1,151 @@ +// SPDX-License-Identifier: GPL-2.0 OR MIT +/* +* Copyright (C) 2015-2019 Jason A. Donenfeld <[email protected]>. All Rights Reserved. +* +* This is an implementation of the BLAKE2s hash and PRF functions. +* +* Information: https://blake2.net/ +*/ + +#include "blake2s.h" +#include <linux/kernel.h> + +static inline u32 ror32 (u32 v,int n) +{ +return (v >> n) | (v << (32 - n)); +} + +static inline void le32_to_cpu_array (u32 a[],size_t n) +{ +for (size_t i =0 ; i < n; i++) + a[i] =le32_to_cpu ((__force __le32 )a[i]); +} + +static inline void cpu_to_le32_array (u32 a[],size_t n) +{ +for (size_t i =0 ; i < n; i++) + a[i] = (__force u32 )cpu_to_le32 (a[i]); +} + +static const u8 blake2s_sigma[10 ][16 ] = { + {0 ,1 ,2 ,3 ,4 ,5 ,6 ,7 ,8 ,9 ,10 ,11 ,12 ,13 ,14 ,15 }, + {14 ,10 ,4 ,8 ,9 ,15 ,13 ,6 ,1 ,12 ,0 ,2 ,11 ,7 ,5 ,3 }, + {11 ,8 ,12 ,0 ,5 ,2 ,15 ,13 ,10 ,14 ,3 ,6 ,7 ,1 ,9 ,4 }, + {7 ,9 ,3 ,1 ,13 ,12 ,11 ,14 ,2 ,6 ,5 ,10 ,4 ,0 ,15 ,8 }, + {9 ,0 ,5 ,7 ,2 ,4 ,10 ,15 ,14 ,1 ,11 ,12 ,6 ,8 ,3 ,13 }, + {2 ,12 ,6 ,10 ,0 ,11 ,8 ,3 ,4 ,13 ,7 ,5 ,15 ,14 ,1 ,9 }, + {12 ,5 ,1 ,15 ,14 ,13 ,4 ,10 ,0 ,7 ,6 ,3 ,9 ,2 ,8 ,11 }, + {13 ,11 ,7 ,14 ,12 ,1 ,3 ,9 ,5 ,0 ,15 ,4 ,8 ,6 ,2 ,10 }, + {6 ,15 ,14 ,9 ,11 ,3 ,0 ,8 ,12 ,2 ,13 ,7 ,1 ,4 ,10 ,5 }, + {10 ,2 ,8 ,4 ,7 ,6 ,1 ,5 ,15 ,11 ,9 ,14 ,3 ,12 ,13 ,0 }, +}; + +static inline void blake2s_increment_counter (struct blake2s_ctx *ctx,u32 inc) +{ + ctx->t[0 ] += inc; + ctx->t[1 ] += (ctx->t[0 ] < inc); +} + +static void blake2s_compress (struct blake2s_ctx *ctx, +const u8 *data,size_t nblocks,u32 inc) +{ +u32 m[16 ]; +u32 v[16 ]; +int i; + +while (nblocks >0 ) { +blake2s_increment_counter (ctx, inc); +memcpy (m, data,BLAKE2S_BLOCK_SIZE ); +le32_to_cpu_array (m,ARRAY_SIZE (m)); +memcpy (v, ctx->h,32 ); + v[8 ] =BLAKE2S_IV0 ; + v[9 ] =BLAKE2S_IV1 ; + v[10 ] =BLAKE2S_IV2 ; + v[11 ] =BLAKE2S_IV3 ; + v[12 ] =BLAKE2S_IV4 ^ ctx->t[0 ]; + v[13 ] =BLAKE2S_IV5 ^ ctx->t[1 ]; + v[14 ] =BLAKE2S_IV6 ^ ctx->f[0 ]; + v[15 ] =BLAKE2S_IV7 ^ ctx->f[1 ]; + +#define G (r, i, a, b, c, d) do { \ + a += b + m[blake2s_sigma[r][2 * i + 0]]; \ + d = ror32(d ^ a, 16); \ + c += d; \ + b = ror32(b ^ c, 12); \ + a += b + m[blake2s_sigma[r][2 * i + 1]]; \ + d = ror32(d ^ a, 8); \ + c += d; \ + b = ror32(b ^ c, 7); \ +} while (0) + +#define ROUND (r) do { \ + G(r, 0, v[0], v[ 4], v[ 8], v[12]); \ + G(r, 1, v[1], v[ 5], v[ 9], v[13]); \ + G(r, 2, v[2], v[ 6], v[10], v[14]); \ + G(r, 3, v[3], v[ 7], v[11], v[15]); \ + G(r, 4, v[0], v[ 5], v[10], v[15]); \ + G(r, 5, v[1], v[ 6], v[11], v[12]); \ + G(r, 6, v[2], v[ 7], v[ 8], v[13]); \ + G(r, 7, v[3], v[ 4], v[ 9], v[14]); \ +} while (0) +ROUND (0 ); +ROUND (1 ); +ROUND (2 ); +ROUND (3 ); +ROUND (4 ); +ROUND (5 ); +ROUND (6 ); +ROUND (7 ); +ROUND (8 ); +ROUND (9 ); + +#undef G +#undef ROUND + +for (i =0 ; i <8 ; ++i) + ctx->h[i] ^= v[i] ^ v[i +8 ]; + + data +=BLAKE2S_BLOCK_SIZE ; + --nblocks; + } +} + +static inline void blake2s_set_lastblock (struct blake2s_ctx *ctx) +{ + ctx->f[0 ] =-1 ; +} + +void blake2s_update (struct blake2s_ctx *ctx,const u8 *in,size_t inlen) +{ +const size_t fill =BLAKE2S_BLOCK_SIZE - ctx->buflen; + +if (unlikely (!inlen)) +return ; +if (inlen > fill) { +memcpy (ctx->buf + ctx->buflen, in, fill); +blake2s_compress (ctx, ctx->buf,1 ,BLAKE2S_BLOCK_SIZE ); + ctx->buflen =0 ; + in += fill; + inlen -= fill; + } +if (inlen >BLAKE2S_BLOCK_SIZE ) { +const size_t nblocks =DIV_ROUND_UP (inlen,BLAKE2S_BLOCK_SIZE ); + +blake2s_compress (ctx, in, nblocks -1 ,BLAKE2S_BLOCK_SIZE ); + in +=BLAKE2S_BLOCK_SIZE * (nblocks -1 ); + inlen -=BLAKE2S_BLOCK_SIZE * (nblocks -1 ); + } +memcpy (ctx->buf + ctx->buflen, in, inlen); + ctx->buflen += inlen; +} + +void blake2s_final (struct blake2s_ctx *ctx,u8 *out) +{ +blake2s_set_lastblock (ctx); +memset (ctx->buf + ctx->buflen,0 , +BLAKE2S_BLOCK_SIZE - ctx->buflen);/* Padding */ +blake2s_compress (ctx, ctx->buf,1 , ctx->buflen); +cpu_to_le32_array (ctx->h,ARRAY_SIZE (ctx->h)); +memcpy (out, ctx->h, ctx->outlen); +memset (ctx,0 ,sizeof (*ctx)); +}tools/perf/util/blake2s.h new file +73
-0@@ -0,0 +1,73 @@ +/* SPDX-License-Identifier: GPL-2.0 OR MIT */ +/* +* Copyright (C) 2015-2019 Jason A. Donenfeld <[email protected]>. All Rights Reserved. +*/ + +#ifndef _CRYPTO_BLAKE2S_H +#define _CRYPTO_BLAKE2S_H + +#include <string.h> +#include <linux/types.h> + +#define BLAKE2S_BLOCK_SIZE 64 + +struct blake2s_ctx { +u32 h[8 ]; +u32 t[2 ]; +u32 f[2 ]; +u8 buf[BLAKE2S_BLOCK_SIZE ]; +unsigned int buflen; +unsigned int outlen; +}; + +enum blake2s_iv { +BLAKE2S_IV0 =0x6A09E667UL , +BLAKE2S_IV1 =0xBB67AE85UL , +BLAKE2S_IV2 =0x3C6EF372UL , +BLAKE2S_IV3 =0xA54FF53AUL , +BLAKE2S_IV4 =0x510E527FUL , +BLAKE2S_IV5 =0x9B05688CUL , +BLAKE2S_IV6 =0x1F83D9ABUL , +BLAKE2S_IV7 =0x5BE0CD19UL , +}; + +static inline void __blake2s_init (struct blake2s_ctx *ctx,size_t outlen, +const void *key,size_t keylen) +{ + ctx->h[0 ] =BLAKE2S_IV0 ^ (0x01010000 | keylen <<8 | outlen); + ctx->h[1 ] =BLAKE2S_IV1 ; + ctx->h[2 ] =BLAKE2S_IV2 ; + ctx->h[3 ] =BLAKE2S_IV3 ; + ctx->h[4 ] =BLAKE2S_IV4 ; + ctx->h[5 ] =BLAKE2S_IV5 ; + ctx->h[6 ] =BLAKE2S_IV6 ; + ctx->h[7 ] =BLAKE2S_IV7 ; + ctx->t[0 ] =0 ; + ctx->t[1 ] =0 ; + ctx->f[0 ] =0 ; + ctx->f[1 ] =0 ; + ctx->buflen =0 ; + ctx->outlen = outlen; +if (keylen) { +memcpy (ctx->buf, key, keylen); +memset (&ctx->buf[keylen],0 ,BLAKE2S_BLOCK_SIZE - keylen); + ctx->buflen =BLAKE2S_BLOCK_SIZE ; + } +} + +static inline void blake2s_init (struct blake2s_ctx *ctx,size_t outlen) +{ +__blake2s_init (ctx, outlen,NULL ,0 ); +} + +static inline void blake2s_init_key (struct blake2s_ctx *ctx,size_t outlen, +const void *key,size_t keylen) +{ +__blake2s_init (ctx, outlen, key, keylen); +} + +void blake2s_update (struct blake2s_ctx *ctx,const u8 *in,size_t inlen); + +void blake2s_final (struct blake2s_ctx *ctx,u8 *out); + +#endif /* _CRYPTO_BLAKE2S_H */ Signature · 1 line
2.52.0
Re: [PATCH v2 1/3] perf util: Add BLAKE2s support
Quoted 9 lines · Arnaldo Carvalho de Melo
Quoted 2 lines · Eric Biggers
Add BLAKE2s support to the perf utility library. The code is borrowed from the kernel.
We have
tools/perf/check-headers.shto check for drifts between things we borrow from the kernel, so that when the kernel fixes or improves things we borrowed, we notice and check if it makes sense for us to update the copy, seetools/include/uapi/README.
Quoted 5 lines
In this case the copy isn't verbatim, as not all is needed, perhaps it could be just to facilitate? I haven't checked, just mentioning it while processing this series, I'll check that later, maybe.
- Arnaldo
I think that would be more trouble than it's worth in this case. Every time I make a change to a UAPI header, even something trivial like updating a comment, I start getting emails about how a header changed and perf's copy of it is being updated too. It's actually kind of annoying. I don't think we need to add that for the BLAKE2s code too.
We can always resynchronize manually later if there's an important change.
But ultimately this code just implements BLAKE2s, which is a fixed algorithm. If it does so correctly, which I'm quite confident it does, future changes will just be refactoring, optimization, etc.
Of course, the use of BLAKE2s in 'perf' doesn't seem to be performance-critical, so any optimizations won't be that important for it. It will include just the generic C code anyway -- no assembly code.
- Eric
PATCH v2 2/3 perf genelf: Switch from SHA-1 to BLAKE2s for build ID generation
In reply to Eric BiggersRecent patches [1] [2] added an implementation of SHA-1 to perf and made it be used for build ID generation.
I had understood the choice of SHA-1, which is a legacy algorithm, to be for backwards compatibility.
It turns out, though, that there's no backwards compatibility requirement here other than the size of the build ID field, which is fixed at 20 bytes. Not only did the hash algorithm already change (from MD5 to SHA-1), but the inputs to the hash changed too: from 'load_addr || code' to just 'code', and now again to
'code || symtab || strsym' [3]. Different linkers generate different build IDs, with the LLVM linker using BLAKE3 hashes for example [4].
Therefore, we might as well switch to a more modern algorithm. Let's go with BLAKE2s. It's faster than SHA-1, isn't cryptographically broken, is easier to implement than BLAKE3, and the kernel's implementation in lib/crypto/blake2s.c is easily borrowed. It also natively supports variable-length hashes, so it can directly produce the needed 20 bytes.
Also make the following additional improvements:
- Hash the three inputs incrementally, so they don't all have to be concatenated into one buffer.
- Add tag/length prefixes to each of the three inputs, so that distinct input tuples reliably result in distinct hashes.
[1] https://lore.kernel.org/linux-perf-users/[email protected]/
[2] https://lore.kernel.org/linux-perf-users/[email protected]/
[3] https://lore.kernel.org/linux-perf-users/[email protected]/
[4] https://github.com/llvm/llvm-project/commit/d3e5b6f7539b86995aef6e2075c1edb3059385ce
--- tools/perf/util/genelf.c | 58 +++++++++++++++++++--------------------- 1 file changed, 28 insertions(+), 30 deletions(-)
tools/perf/util/genelf.c +28 -30
@@ -16,12 +16,12 @@#include <err.h> #ifdef HAVE_LIBDW_SUPPORT #include <dwarf.h> #endif +#include "blake2s.h" #include "genelf.h" -#include "sha1.h" #include "../util/jitdump.h" #include <linux/compiler.h> #ifndef NT_GNU_BUILD_ID #define NT_GNU_BUILD_ID 3
@@ -49,11 +49,11 @@ static char shd_string_table[] = { };static struct buildid_note {Elf_Note desc;/* descsz: size of build-id, must be multiple of 4 */ char name[4 ];/* GNU\0 */ -u8 build_id[SHA1_DIGEST_SIZE ]; +u8 build_id[20 ]; } bnote;static Elf_Sym symtab[]={/* symbol 0 MUST be the undefined symbol */ { .st_name =0 ,/* index in sym_string table */
@@ -150,13 +150,32 @@ jit_add_eh_frame_info(Elf *e, void* unwinding, uint64_t unwinding_header_size, shdr->sh_entsize =0 ;return 0 ; } +enum { +TAG_CODE =0 , +TAG_SYMTAB =1 , +TAG_STRSYM =2 , +}; + +/* +* Update the hash using the given data, also prepending a (tag, len) prefix to +* ensure that distinct input tuples reliably result in distinct hashes. +*/ +static void blake2s_update_tagged (struct blake2s_ctx *ctx,int tag, +const void *data,size_t len) +{ +u64 prefix = ((u64 )tag <<56 ) | len; + +blake2s_update (ctx, (const u8 *)&prefix,sizeof (prefix)); +blake2s_update (ctx, data, len); +} +/* * fd: file descriptor open for writing for the output file -* load_addr: code load address (could be zero, just used for buildid) +* load_addr: code load address (could be zero) * sym: function name (for native code - used as the symbol) * code: the native code * csize: the code size in bytes */ int
@@ -171,12 +190,11 @@ jit_write_elf(int fd, uint64_t load_addr __maybe_unused, const char *sym,Elf_Ehdr *ehdr;Elf_Phdr *phdr;Elf_Shdr *shdr;uint64_t eh_frame_base_offset;char *strsym =NULL ; -void *build_id_data =NULL , *tmp; -int build_id_data_len; +struct blake2s_ctx ctx;int symlen;int retval =-1 ;if (elf_version (EV_CURRENT ) ==EV_NONE ) {warnx ("ELF initialization failed" );
@@ -251,17 +269,12 @@ jit_write_elf(int fd, uint64_t load_addr __maybe_unused, const char *sym, shdr->sh_type =SHT_PROGBITS ; shdr->sh_addr =GEN_ELF_TEXT_OFFSET ; shdr->sh_flags =SHF_EXECINSTR |SHF_ALLOC ; shdr->sh_entsize =0 ; - build_id_data =malloc (csize); -if (build_id_data ==NULL ) { -warnx ("cannot allocate build-id data" ); - goto error; - } -memcpy (build_id_data, code, csize); - build_id_data_len = csize; +blake2s_init (&ctx,sizeof (bnote.build_id)); +blake2s_update_tagged (&ctx,TAG_CODE , code, csize);/* * Setup .eh_frame_hdr and .eh_frame */ if (unwinding) {
@@ -342,18 +355,11 @@ jit_write_elf(int fd, uint64_t load_addr __maybe_unused, const char *sym, shdr->sh_type =SHT_SYMTAB ; shdr->sh_flags =0 ; shdr->sh_entsize =sizeof (Elf_Sym); shdr->sh_link = unwinding ?6 :4 ;/* index of .strtab section */ - tmp =realloc (build_id_data, build_id_data_len +sizeof (symtab)); -if (tmp ==NULL ) { -warnx ("cannot allocate build-id data" ); - goto error; - } -memcpy (tmp + build_id_data_len, symtab,sizeof (symtab)); - build_id_data = tmp; - build_id_data_len +=sizeof (symtab); +blake2s_update_tagged (&ctx,TAG_SYMTAB , symtab,sizeof (symtab));/* * setup symbols string table * 2 = 1 for 0 in 1st entry, 1 for the 0 at end of symbol for 2nd entry */
@@ -393,18 +399,11 @@ jit_write_elf(int fd, uint64_t load_addr __maybe_unused, const char *sym, shdr->sh_name =25 ;/* offset in shd_string_table */ shdr->sh_type =SHT_STRTAB ; shdr->sh_flags =0 ; shdr->sh_entsize =0 ; - tmp =realloc (build_id_data, build_id_data_len + symlen); -if (tmp ==NULL ) { -warnx ("cannot allocate build-id data" ); - goto error; - } -memcpy (tmp + build_id_data_len, strsym, symlen); - build_id_data = tmp; - build_id_data_len += symlen; +blake2s_update_tagged (&ctx,TAG_STRSYM , strsym, symlen);/* * setup build-id section */ scn =elf_newscn (e);
@@ -420,11 +419,11 @@ jit_write_elf(int fd, uint64_t load_addr __maybe_unused, const char *sym, }/* * build-id generation */ -sha1 (build_id_data, build_id_data_len, bnote.build_id); +blake2s_final (&ctx, bnote.build_id); bnote.desc.namesz =sizeof (bnote.name);/* must include 0 termination */ bnote.desc.descsz =sizeof (bnote.build_id); bnote.desc.type =NT_GNU_BUILD_ID ;strcpy (bnote.name,"GNU" );
@@ -465,9 +464,8 @@ jit_write_elf(int fd, uint64_t load_addr __maybe_unused, const char *sym, retval =0 ; error: (void )elf_end (e);free (strsym); -free (build_id_data);return retval; }
Signature · 1 line
2.52.0
PATCH v2 3/3 perf util: Remove SHA-1 code
In reply to Eric BiggersNow that the SHA-1 code is no longer used, remove it.
--- tools/perf/tests/util.c | 49 --------------------- tools/perf/util/Build | 1 - tools/perf/util/sha1.c | 97 ----------------------------------------- tools/perf/util/sha1.h | 6 --- 4 files changed, 153 deletions(-) delete mode 100644 tools/perf/util/sha1.c delete mode 100644 tools/perf/util/sha1.h
tools/perf/tests/util.c +0 -49
@@ -1,10 +1,9 @@// SPDX-License-Identifier: GPL-2.0 #include "tests.h" #include "util/blake2s.h" #include "util/debug.h" -#include "util/sha1.h" #include <linux/compiler.h> #include <stdlib.h> #include <string2.h>
@@ -16,52 +15,10 @@ static int test_strreplace(char needle, const char *haystack,free (new);return ret ==0 ; } -#define MAX_LEN 512 - -/* Test sha1() for all lengths from 0 to MAX_LEN inclusively. */ -static int test_sha1 (void ) -{ -u8 data[MAX_LEN ]; -size_t digests_size = (MAX_LEN +1 ) *SHA1_DIGEST_SIZE ; -u8 *digests; -u8 digest_of_digests[SHA1_DIGEST_SIZE ]; -/* -* The correctness of this value was verified by running this test with -* sha1() replaced by OpenSSL's SHA1(). -*/ -static const u8 expected_digest_of_digests[SHA1_DIGEST_SIZE ] = { -0x74 ,0xcd ,0x4c ,0xb9 ,0xd8 ,0xa6 ,0xd5 ,0x95 ,0x22 ,0x8b , -0x7e ,0xd6 ,0x8b ,0x7e ,0x46 ,0x95 ,0x31 ,0x9b ,0xa2 ,0x43 , - }; -size_t i; - - digests =malloc (digests_size); -TEST_ASSERT_VAL ("failed to allocate digests" , digests !=NULL ); - -/* Generate MAX_LEN bytes of data. */ -for (i =0 ; i <MAX_LEN ; i++) - data[i] = i; - -/* Calculate a SHA-1 for each length 0 through MAX_LEN inclusively. */ -for (i =0 ; i <=MAX_LEN ; i++) -sha1 (data, i, &digests[i *SHA1_DIGEST_SIZE ]); - -/* Calculate digest of all digests calculated above. */ -sha1 (digests, digests_size, digest_of_digests); - -free (digests); - -/* Check for the expected result. */ -TEST_ASSERT_VAL ("wrong output from sha1()" , -memcmp (digest_of_digests, expected_digest_of_digests, -SHA1_DIGEST_SIZE ) ==0 ); -return 0 ; -} -/* Maximum data length tested by test_blake2s() */ #define MAX_DATA_LEN 512/* * Hash length tested by test_blake2s(). BLAKE2s supports variable-length
@@ -129,22 +86,16 @@ static int test_blake2s(void)return 0 ; }static int test__util (struct test_suite *t__maybe_unused ,int subtest__maybe_unused ) { -int ret; -TEST_ASSERT_VAL ("empty string" ,test_strreplace (' ' ,"" ,"123" ,"" ));TEST_ASSERT_VAL ("no match" ,test_strreplace ('5' ,"123" ,"4" ,"123" ));TEST_ASSERT_VAL ("replace 1" ,test_strreplace ('3' ,"123" ,"4" ,"124" ));TEST_ASSERT_VAL ("replace 2" ,test_strreplace ('a' ,"abcabc" ,"ef" ,"efbcefbc" ));TEST_ASSERT_VAL ("replace long" ,test_strreplace ('a' ,"abcabc" ,"longlong" ,"longlongbclonglongbc" )); - ret =test_sha1 (); -if (ret !=TEST_OK ) -return ret; -return test_blake2s (); }DEFINE_SUITE ("util" , util);
tools/perf/util/Build +0 -1
@@ -42,11 +42,10 @@ perf-util-y += rlimit.o perf-util-y += argv_split.o perf-util-y += rbtree.o perf-util-y += libstring.o perf-util-y += bitmap.o perf-util-y += hweight.o -perf-util-y += sha1.o perf-util-y += smt.o perf-util-y += strbuf.o perf-util-y += string.o perf-util-y += strlist.o perf-util-y += strfilter.o
tools/perf/util/sha1.c deleted +0 -97
97 lines not on this page · raw message
tools/perf/util/sha1.h deleted +0 -6
@@ -1,6 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -#include <linux/types.h> - -#define SHA1_DIGEST_SIZE 20 - -void sha1 (const void *data,size_t len,u8 out[SHA1_DIGEST_SIZE ]);
Signature · 1 line
2.52.0
Hello,
Quoted 11 lines · Eric Biggers
This series upgrades perf's build ID generation to a more modern hash algorithm and switches to an incremental hashing API.
It also fixes an issue where different (code, symtab, strsym) tuples didn't necessarily result in different hashes.
Note that the size of the build ID field stays the same.
This applies to the perf-tools-next branch of
https://git.kernel.org/pub/scm/linux/kernel/git/perf/perf-tools-next.git
Quoted 8 lines
Changed in v2:
- Split into three patches
- Improved a couple commentsEric Biggers (3):
perf util: Add BLAKE2s support
perf genelf: Switch from SHA-1 to BLAKE2s for build ID generation perf util: Remove SHA-1 code
Thanks a lot!
Namhyung
Quoted 19 lines
tools/perf/tests/util.c | 85 +++++++++++++-------- tools/perf/util/Build | 2 +- tools/perf/util/blake2s.c | 151 ++++++++++++++++++++++++++++++++++++++ tools/perf/util/blake2s.h | 73 ++++++++++++++++++ tools/perf/util/genelf.c | 58 +++++++-------- tools/perf/util/sha1.c | 97 ------------------------ tools/perf/util/sha1.h | 6 -- 7 files changed, 309 insertions(+), 163 deletions(-) create mode 100644 tools/perf/util/blake2s.c create mode 100644 tools/perf/util/blake2s.h delete mode 100644 tools/perf/util/sha1.c delete mode 100644 tools/perf/util/sha1.hbase-commit:
2eeb09fe1c5173b659929f92fee4461796ca8c14Signature · 1 line
2.52.0
Quoted 11 lines · Eric Biggers
This series upgrades perf's build ID generation to a more modern hash algorithm and switches to an incremental hashing API.
It also fixes an issue where different (code, symtab, strsym) tuples didn't necessarily result in different hashes.
Note that the size of the build ID field stays the same.
This applies to the perf-tools-next branch of
https://git.kernel.org/pub/scm/linux/kernel/git/perf/perf-tools-next.git
Quoted 8 lines
Changed in v2:
- Split into three patches
- Improved a couple commentsEric Biggers (3):
perf util: Add BLAKE2s support
perf genelf: Switch from SHA-1 to BLAKE2s for build ID generation perf util: Remove SHA-1 code
Any plan to apply this series to perf-tools-next?
- Eric
Quoted 6 lines · Eric Biggers
This series upgrades perf's build ID generation to a more modern hash algorithm and switches to an incremental hashing API.
It also fixes an issue where different (code, symtab, strsym) tuples didn't necessarily result in different hashes.
Quoted 8 lines
Note that the size of the build ID field stays the same.
This applies to the perf-tools-next branch of
https://git.kernel.org/pub/scm/linux/kernel/git/perf/perf-tools-next.gitChanged in v2:
- Split into three patches
- Improved a couple comments
Thanks, applied to perf-tools-next,
- Arnaldo