From 7dafc5c9255c5436d7f8adc1078b7c01c36e7f07 Mon Sep 17 00:00:00 2001 From: Eugene Uriev Date: Sun, 31 Mar 2024 23:03:21 +0300 Subject: mcheck: introduce essentials of mcheck The core part of mcheck, but without memalign. memalign - to be added in ensuing commits. Signed-off-by: Eugene Uriev --- include/mcheck.h | 42 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 include/mcheck.h (limited to 'include') diff --git a/include/mcheck.h b/include/mcheck.h new file mode 100644 index 00000000000..a049745e4e3 --- /dev/null +++ b/include/mcheck.h @@ -0,0 +1,42 @@ +/* SPDX-License-Identifier: GPL-2.1+ */ +/* + * Copyright (C) 1996-2024 Free Software Foundation, Inc. + * This file is part of the GNU C Library. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * . + */ +#ifndef _MCHECK_H +#define _MCHECK_H 1 + +/* + * Return values for `mprobe': these are the kinds of inconsistencies that + * `mcheck' enables detection of. + */ +enum mcheck_status { + MCHECK_DISABLED = -1, /* Consistency checking is not turned on. */ + MCHECK_OK, /* Block is fine. */ + MCHECK_FREE, /* Block freed twice. */ + MCHECK_HEAD, /* Memory before the block was clobbered. */ + MCHECK_TAIL /* Memory after the block was clobbered. */ +}; + +typedef void (*mcheck_abortfunc_t)(enum mcheck_status); + +int mcheck(mcheck_abortfunc_t func); + +/* + * Check for aberrations in a particular malloc'd block. These are the + * same checks that `mcheck' does, when you free or reallocate a block. + */ +enum mcheck_status mprobe(void *__ptr); + +#endif -- cgit v1.2.3 From 18c1bfafe0ccdd3229d91bbb07ed942e9f233f93 Mon Sep 17 00:00:00 2001 From: Eugene Uriev Date: Sun, 31 Mar 2024 23:03:24 +0300 Subject: mcheck: add pedantic mode support The pedantic mode is run-time contolled, so appropriate registry take place everytime. Maybe it's worth to use compile-time control only. So, the registry could be optimized out by an #ifdef. Signed-off-by: Eugene Uriev --- common/dlmalloc.c | 12 ++++++++++++ common/mcheck_core.inc.h | 41 +++++++++++++++++++++++++++++++++++++++++ include/mcheck.h | 9 +++++++++ 3 files changed, 62 insertions(+) (limited to 'include') diff --git a/common/dlmalloc.c b/common/dlmalloc.c index 73c04af2a36..a0616217d49 100644 --- a/common/dlmalloc.c +++ b/common/dlmalloc.c @@ -2233,6 +2233,7 @@ void cfree(mem) Void_t *mem; Void_t *mALLOc(size_t bytes) { + mcheck_pedantic_prehook(); size_t fullsz = mcheck_alloc_prehook(bytes); void *p = mALLOc_impl(fullsz); @@ -2245,6 +2246,7 @@ void fREe(Void_t *mem) { fREe_impl(mcheck_free_prehook(mem)); } Void_t *rEALLOc(Void_t *oldmem, size_t bytes) { + mcheck_pedantic_prehook(); if (bytes == 0) { if (oldmem) fREe(oldmem); @@ -2265,6 +2267,7 @@ Void_t *rEALLOc(Void_t *oldmem, size_t bytes) Void_t *mEMALIGn(size_t alignment, size_t bytes) { + mcheck_pedantic_prehook(); size_t fullsz = mcheck_memalign_prehook(alignment, bytes); void *p = mEMALIGn_impl(alignment, fullsz); @@ -2277,6 +2280,7 @@ Void_t *mEMALIGn(size_t alignment, size_t bytes) Void_t *cALLOc(size_t n, size_t elem_size) { + mcheck_pedantic_prehook(); // NB: here is no overflow check. size_t fullsz = mcheck_alloc_prehook(n * elem_size); void *p = cALLOc_impl(1, fullsz); @@ -2287,12 +2291,20 @@ Void_t *cALLOc(size_t n, size_t elem_size) } // mcheck API { +int mcheck_pedantic(mcheck_abortfunc_t f) +{ + mcheck_initialize(f, 1); + return 0; +} + int mcheck(mcheck_abortfunc_t f) { mcheck_initialize(f, 0); return 0; } +void mcheck_check_all(void) { mcheck_pedantic_check(); } + enum mcheck_status mprobe(void *__ptr) { return mcheck_mprobe(__ptr); } // mcheck API } #endif diff --git a/common/mcheck_core.inc.h b/common/mcheck_core.inc.h index b038bb0539b..85a34de2958 100644 --- a/common/mcheck_core.inc.h +++ b/common/mcheck_core.inc.h @@ -30,6 +30,8 @@ * Unlike glibc-clients, U-Boot has limited malloc-usage, and only one thread. * So it's better to make the protection heavier. * Thus overflow canary here is greater, than glibc's one. Underflow canary is bigger too. + * U-Boot also allows to use fixed-size heap-registry, instead of double-linked list in glibc. + * * Heavy canary allows to catch not only memset(..)-errors, * but overflow/underflow of struct-array access: * { @@ -61,8 +63,14 @@ #define FREEFLOOD ((char)0xf5) #define PADDINGFLOOD ((char)0x58) +// my normal run demands 4427-6449 chunks: +#define REGISTRY_SZ 6608 #define CANARY_DEPTH 2 +// avoid problems with BSS at early stage: +static char mcheck_pedantic_flag __section(".data") = 0; +static void *mcheck_registry[REGISTRY_SZ] __section(".data") = {0}; + typedef unsigned long long mcheck_elem; typedef struct { mcheck_elem elems[CANARY_DEPTH]; @@ -159,6 +167,12 @@ static void *mcheck_free_helper(void *ptr, int clean_content) if (clean_content) mcheck_flood(ptr, FREEFLOOD, mcheck_allign_customer_size(hdr->size)); + for (i = 0; i < REGISTRY_SZ; ++i) + if (mcheck_registry[i] == hdr) { + mcheck_registry[i] = 0; + break; + } + return (char *)hdr - hdr->aln_skip; } @@ -197,6 +211,17 @@ static void *mcheck_allocated_helper(void *altoghether_ptr, size_t customer_sz, for (i = 0; i < CANARY_DEPTH; ++i) tail->elems[i] = MAGICTAIL; + + for (i = 0; i < REGISTRY_SZ; ++i) + if (!mcheck_registry[i]) { + mcheck_registry[i] = hdr; + return payload; // normal end + } + + static char *overflow_msg = "\n\n\nERROR: mcheck registry overflow, pedantic check would be incomplete!!\n\n\n\n"; + + printf("%s", overflow_msg); + overflow_msg = "(mcheck registry full)"; return payload; } @@ -227,9 +252,25 @@ static enum mcheck_status mcheck_mprobe(void *ptr) return mcheck_checkhdr(hdr); } +static void mcheck_pedantic_check(void) +{ + int i; + + for (i = 0; i < REGISTRY_SZ; ++i) + if (mcheck_registry[i]) + mcheck_checkhdr(mcheck_registry[i]); +} + +static void mcheck_pedantic_prehook(void) +{ + if (mcheck_pedantic_flag) + mcheck_pedantic_check(); +} + static void mcheck_initialize(mcheck_abortfunc_t new_func, char pedantic_flag) { mcheck_abortfunc = (new_func) ? new_func : &mcheck_default_abort; + mcheck_pedantic_flag = pedantic_flag; } #endif diff --git a/include/mcheck.h b/include/mcheck.h index a049745e4e3..f4c9b7e61c8 100644 --- a/include/mcheck.h +++ b/include/mcheck.h @@ -33,6 +33,15 @@ typedef void (*mcheck_abortfunc_t)(enum mcheck_status); int mcheck(mcheck_abortfunc_t func); +/* + * Similar to `mcheck' but performs checks for all block whenever one of + * the memory handling functions is called. This can be very slow. + */ +int mcheck_pedantic(mcheck_abortfunc_t f); + +/* Force check of all blocks now. */ +void mcheck_check_all(void); + /* * Check for aberrations in a particular malloc'd block. These are the * same checks that `mcheck' does, when you free or reallocate a block. -- cgit v1.2.3 From 707a6dfb2b8429b3d361c8c884ada48ebae6f1e4 Mon Sep 17 00:00:00 2001 From: Eugene Uriev Date: Sun, 31 Mar 2024 23:03:27 +0300 Subject: mcheck: let mcheck_abortfunc_t print the pointer Signed-off-by: Eugene Uriev --- common/mcheck_core.inc.h | 16 ++++++++-------- include/mcheck.h | 2 +- 2 files changed, 9 insertions(+), 9 deletions(-) (limited to 'include') diff --git a/common/mcheck_core.inc.h b/common/mcheck_core.inc.h index 2f11ac567f6..69021409922 100644 --- a/common/mcheck_core.inc.h +++ b/common/mcheck_core.inc.h @@ -90,7 +90,7 @@ struct mcheck_hdr { mcheck_canary canary; /* Magic number to check header integrity. */ }; -static void mcheck_default_abort(enum mcheck_status status) +static void mcheck_default_abort(enum mcheck_status status, const void *p) { const char *msg; @@ -111,7 +111,7 @@ static void mcheck_default_abort(enum mcheck_status status) msg = "bogus mcheck_status, library is buggy\n"; break; } - printf("\n\nmcheck: %s!!! [%zu]\n\n", msg, mcheck_chunk_count_max); + printf("\n\nmcheck: %p:%s!!! [%zu]\n\n", p, msg, mcheck_chunk_count_max); } static mcheck_abortfunc_t mcheck_abortfunc = &mcheck_default_abort; @@ -124,9 +124,9 @@ static inline size_t allign_size_up(size_t sz, size_t grain) #define mcheck_allign_customer_size(SZ) allign_size_up(SZ, sizeof(mcheck_elem)) #define mcheck_evaluate_memalign_prefix_size(ALIGN) allign_size_up(sizeof(struct mcheck_hdr), ALIGN) -static enum mcheck_status mcheck_OnNok(enum mcheck_status status) +static enum mcheck_status mcheck_OnNok(enum mcheck_status status, const void *p) { - (*mcheck_abortfunc)(status); + (*mcheck_abortfunc)(status, p); return status; } @@ -136,11 +136,11 @@ static enum mcheck_status mcheck_checkhdr(const struct mcheck_hdr *hdr) for (i = 0; i < CANARY_DEPTH; ++i) if (hdr->canary.elems[i] == MAGICFREE) - return mcheck_OnNok(MCHECK_FREE); + return mcheck_OnNok(MCHECK_FREE, hdr + 1); for (i = 0; i < CANARY_DEPTH; ++i) if (hdr->canary.elems[i] != MAGICWORD) - return mcheck_OnNok(MCHECK_HEAD); + return mcheck_OnNok(MCHECK_HEAD, hdr + 1); const size_t payload_size = hdr->size; const size_t payload_size_aligned = mcheck_allign_customer_size(payload_size); @@ -150,13 +150,13 @@ static enum mcheck_status mcheck_checkhdr(const struct mcheck_hdr *hdr) for (i = 0; i < padd_size; ++i) if (payload[payload_size + i] != PADDINGFLOOD) - return mcheck_OnNok(MCHECK_TAIL); + return mcheck_OnNok(MCHECK_TAIL, hdr + 1); const mcheck_canary *tail = (const mcheck_canary *)&payload[payload_size_aligned]; for (i = 0; i < CANARY_DEPTH; ++i) if (tail->elems[i] != MAGICTAIL) - return mcheck_OnNok(MCHECK_TAIL); + return mcheck_OnNok(MCHECK_TAIL, hdr + 1); return MCHECK_OK; } diff --git a/include/mcheck.h b/include/mcheck.h index f4c9b7e61c8..bd506ae6291 100644 --- a/include/mcheck.h +++ b/include/mcheck.h @@ -29,7 +29,7 @@ enum mcheck_status { MCHECK_TAIL /* Memory after the block was clobbered. */ }; -typedef void (*mcheck_abortfunc_t)(enum mcheck_status); +typedef void (*mcheck_abortfunc_t)(enum mcheck_status, const void *p); int mcheck(mcheck_abortfunc_t func); -- cgit v1.2.3