From cff6d757e3ba609c08ef2aaa00f07e53551e5bf6 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Mon, 3 Jun 2024 07:11:10 +0200 Subject: Adding upstream version 3.0.0. Signed-off-by: Daniel Baumann --- src/tools.c | 368 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 357 insertions(+), 11 deletions(-) (limited to 'src/tools.c') diff --git a/src/tools.c b/src/tools.c index e1ba241..7608e7e 100644 --- a/src/tools.c +++ b/src/tools.c @@ -17,9 +17,14 @@ #endif #if defined(__FreeBSD__) +#include +#if __FreeBSD_version < 1300058 #include #include extern void *__elf_aux_vector; +#else +#include +#endif #endif #if defined(__NetBSD__) @@ -36,6 +41,7 @@ extern void *__elf_aux_vector; #include #include #include +#include #include #include #include @@ -47,6 +53,10 @@ extern void *__elf_aux_vector; #include #endif +#if defined(USE_PRCTL) +#include +#endif + #include #include #include @@ -1964,11 +1974,11 @@ int addr_is_local(const struct netns_entry *ns, * with the hexadecimal representation of their ASCII-code (2 digits) * prefixed by , and will store the result between (included) * and (excluded), and will always terminate the string with a '\0' - * before . The position of the '\0' is returned if the conversion - * completes. If bytes are missing between and , then the - * conversion will be incomplete and truncated. If <= , the '\0' - * cannot even be stored so we return without writing the 0. + * before . If bytes are missing between and , then the + * conversion will be incomplete and truncated. * The input string must also be zero-terminated. + * + * Return the address of the \0 character, or NULL on error */ const char hextab[16] = "0123456789ABCDEF"; char *encode_string(char *start, char *stop, @@ -1990,8 +2000,9 @@ char *encode_string(char *start, char *stop, string++; } *start = '\0'; + return start; } - return start; + return NULL; } /* @@ -2020,8 +2031,9 @@ char *encode_chunk(char *start, char *stop, str++; } *start = '\0'; + return start; } - return start; + return NULL; } /* @@ -2030,8 +2042,9 @@ char *encode_chunk(char *start, char *stop, * is reached or NULL-byte is encountered. The result will * be stored between (included) and (excluded). This * function will always try to terminate the resulting string with a '\0' - * before , and will return its position if the conversion - * completes. + * before . + * + * Return the address of the \0 character, or NULL on error */ char *escape_string(char *start, char *stop, const char escape, const long *map, @@ -2051,10 +2064,169 @@ char *escape_string(char *start, char *stop, string++; } *start = '\0'; + return start; + } + return NULL; +} + +/* CBOR helper to encode an uint64 value with prefix (3bits MAJOR type) + * according to RFC8949 + * + * CBOR encode ctx is provided in + * + * Returns the position of the last written byte on success and NULL on + * error. The function cannot write past + */ +char *cbor_encode_uint64_prefix(struct cbor_encode_ctx *ctx, + char *start, char *stop, uint64_t value, + uint8_t prefix) +{ + int nb_bytes = 0; + + /* + * For encoding logic, see: + * https://www.rfc-editor.org/rfc/rfc8949.html#name-specification-of-the-cbor-e + */ + if (value < 24) { + /* argument is the value itself */ + prefix |= value; + } + else { + if (value <= 0xFFU) { + /* 1-byte */ + nb_bytes = 1; + prefix |= 24; // 0x18 + } + else if (value <= 0xFFFFU) { + /* 2 bytes */ + nb_bytes = 2; + prefix |= 25; // 0x19 + } + else if (value <= 0xFFFFFFFFU) { + /* 4 bytes */ + nb_bytes = 4; + prefix |= 26; // 0x1A + } + else { + /* 8 bytes */ + nb_bytes = 8; + prefix |= 27; // 0x1B + } + } + + start = ctx->e_fct_byte(ctx, start, stop, prefix); + if (start == NULL) + return NULL; + + /* encode 1 byte at a time from higher bits to lower bits */ + while (nb_bytes) { + uint8_t cur_byte = (value >> ((nb_bytes - 1) * 8)) & 0xFFU; + + start = ctx->e_fct_byte(ctx, start, stop, cur_byte); + if (start == NULL) + return NULL; + + nb_bytes--; + } + + return start; +} + +/* CBOR helper to encode an int64 value according to RFC8949 + * + * CBOR encode ctx is provided in + * + * Returns the position of the last written byte on success and NULL on + * error. The function cannot write past + */ +char *cbor_encode_int64(struct cbor_encode_ctx *ctx, + char *start, char *stop, int64_t value) +{ + uint64_t absolute_value = llabs(value); + int cbor_prefix; + + /* + * For encoding logic, see: + * https://www.rfc-editor.org/rfc/rfc8949.html#name-specification-of-the-cbor-e + */ + if (value >= 0) + cbor_prefix = 0x00; // unsigned int + else { + cbor_prefix = 0x20; // negative int + /* N-1 for negative int */ + absolute_value -= 1; + } + return cbor_encode_uint64_prefix(ctx, start, stop, + absolute_value, cbor_prefix); +} + +/* CBOR helper to encode a string chunk according to RFC8949 + * + * if is NULL, then only the (with length) will be + * emitted + * + * CBOR encode ctx is provided in + * + * Returns the position of the last written byte on success and NULL on + * error. The function cannot write past + */ +char *cbor_encode_bytes_prefix(struct cbor_encode_ctx *ctx, + char *start, char *stop, + const char *bytes, size_t len, + uint8_t prefix) +{ + + size_t it = 0; + + /* write prefix (with text length as argument) */ + start = cbor_encode_uint64_prefix(ctx, start, stop, + len, prefix); + if (start == NULL) + return NULL; + + /* write actual bytes if provided */ + while (bytes && it < len) { + start = ctx->e_fct_byte(ctx, start, stop, bytes[it]); + if (start == NULL) + return NULL; + it++; } return start; } +/* CBOR helper to encode a text chunk according to RFC8949 + * + * if is NULL, then only the text prefix (with length) will be emitted + * + * CBOR encode ctx is provided in + * + * Returns the position of the last written byte on success and NULL on + * error. The function cannot write past + */ +char *cbor_encode_text(struct cbor_encode_ctx *ctx, + char *start, char *stop, + const char *text, size_t len) +{ + return cbor_encode_bytes_prefix(ctx, start, stop, text, len, 0x60); +} + +/* CBOR helper to encode a byte string chunk according to RFC8949 + * + * if is NULL, then only the byte string prefix (with length) will be + * emitted + * + * CBOR encode ctx is provided in + * + * Returns the position of the last written byte on success and NULL on + * error. The function cannot write past + */ +char *cbor_encode_bytes(struct cbor_encode_ctx *ctx, + char *start, char *stop, + const char *bytes, size_t len) +{ + return cbor_encode_bytes_prefix(ctx, start, stop, bytes, len, 0x40); +} + /* Check a string for using it in a CSV output format. If the string contains * one of the following four char <">, <,>, CR or LF, the string is * encapsulated between <"> and the <"> are escaped by a <""> sequence. @@ -4900,6 +5072,58 @@ void dump_addr_and_bytes(struct buffer *buf, const char *pfx, const void *addr, } } +/* Dumps the 64 bytes around at the end of with symbols + * decoding. An optional special pointer may be recognized (special), in + * which case its type (spec_type) and name (spec_name) will be reported. + * This is convenient for pool names but could be used for list heads or + * anything in that vein. +*/ +void dump_area_with_syms(struct buffer *output, const void *base, const void *addr, + const void *special, const char *spec_type, const char *spec_name) +{ + const char *start, *end, *p; + const void *tag; + + chunk_appendf(output, "Contents around address %p+%lu=%p:\n", base, (ulong)(addr - base), addr); + + /* dump in word-sized blocks */ + start = (const void *)(((uintptr_t)addr - 32) & -sizeof(void*)); + end = (const void *)(((uintptr_t)addr + 32 + sizeof(void*) - 1) & -sizeof(void*)); + + while (start < end) { + dump_addr_and_bytes(output, " ", start, sizeof(void*)); + chunk_strcat(output, " ["); + for (p = start; p < start + sizeof(void*); p++) { + if (!may_access(p)) + chunk_strcat(output, "*"); + else if (isprint((unsigned char)*p)) + chunk_appendf(output, "%c", *p); + else + chunk_strcat(output, "."); + } + + if (may_access(start)) + tag = *(const void **)start; + else + tag = NULL; + + if (special && tag == special) { + /* the pool can often be there so let's detect it */ + chunk_appendf(output, "] [%s:%s", spec_type, spec_name); + } + else if (tag) { + /* print pointers that resolve to a symbol */ + size_t back_data = output->data; + chunk_strcat(output, "] ["); + if (!resolve_sym_name(output, NULL, tag)) + output->data = back_data; + } + + chunk_strcat(output, "]\n"); + start = p; + } +} + /* print a line of text buffer (limited to 70 bytes) to . The format is : * <2 spaces> <70 chars max> <\n> * which is 60 chars per line. Non-printable chars \t, \n, \r and \e are @@ -5018,6 +5242,7 @@ const char *get_exec_path() if (execfn && execfn != ENOENT) ret = (const char *)execfn; #elif defined(__FreeBSD__) +#if __FreeBSD_version < 1300058 Elf_Auxinfo *auxv; for (auxv = __elf_aux_vector; auxv->a_type != AT_NULL; ++auxv) { if (auxv->a_type == AT_EXECPATH) { @@ -5025,6 +5250,14 @@ const char *get_exec_path() break; } } +#else + static char execpath[MAXPATHLEN]; + + if (execpath[0] == '\0') + elf_aux_info(AT_EXECPATH, execpath, MAXPATHLEN); + if (execpath[0] != '\0') + ret = execpath; +#endif #elif defined(__NetBSD__) AuxInfo *auxv; for (auxv = _dlauxinfo(); auxv->a_type != AT_NULL; ++auxv) { @@ -5511,10 +5744,10 @@ void ha_random_jump96(uint32_t dist) } } -/* Generates an RFC4122 UUID into chunk which must be at least 37 - * bytes large. +/* Generates an RFC 9562 version 4 UUID into chunk + * which must be at least 37 bytes large. */ -void ha_generate_uuid(struct buffer *output) +void ha_generate_uuid_v4(struct buffer *output) { uint32_t rnd[4]; uint64_t last; @@ -5535,6 +5768,31 @@ void ha_generate_uuid(struct buffer *output) (long long)((rnd[2] >> 14u) | ((uint64_t) rnd[3] << 18u)) & 0xFFFFFFFFFFFFull); } +/* Generates an RFC 9562 version 7 UUID into chunk + * which must be at least 37 bytes large. + */ +void ha_generate_uuid_v7(struct buffer *output) +{ + uint32_t rnd[3]; + uint64_t last; + uint64_t time; + + time = (date.tv_sec * 1000) + (date.tv_usec / 1000); + last = ha_random64(); + rnd[0] = last; + rnd[1] = last >> 32; + + last = ha_random64(); + rnd[2] = last; + + chunk_printf(output, "%8.8x-%4.4x-%4.4x-%4.4x-%12.12llx", + (uint)(time >> 16u), + (uint)(time & 0xFFFF), + ((rnd[0] >> 16u) & 0xFFF) | 0x7000, // highest 4 bits indicate the uuid version + (rnd[1] & 0x3FFF) | 0x8000, // the highest 2 bits indicate the UUID variant (10), + (long long)((rnd[1] >> 14u) | ((uint64_t) rnd[2] << 18u)) & 0xFFFFFFFFFFFFull); +} + /* only used by parse_line() below. It supports writing in place provided that * is updated to the next location before calling it. In that case, the @@ -6206,6 +6464,94 @@ int openssl_compare_current_name(const char *name) return 1; } +/* prctl/PR_SET_VMA wrapper to easily give a name to virtual memory areas, + * knowing their address and size. + * + * It is only intended for use with memory allocated using mmap (private or + * shared anonymous maps) or malloc (provided that is at least one page + * large), which is memory that may be released using munmap(). For memory + * allocated using malloc(), no naming will be attempted if the vma is less + * than one page large, because naming is only relevant for large memory + * blocks. For instance, glibc/malloc() will directly use mmap() once + * MMAP_THRESHOLD is reached (defaults to 128K), and will try to use the + * heap as much as possible below that. + * + * and are mandatory + * is optional, if != ~0, will be used to append an id after the name + * in order to differentiate 2 entries set using the same and + * + * The function does nothing if naming API is not available, and naming errors + * are ignored. + */ +void vma_set_name_id(void *addr, size_t size, const char *type, const char *name, unsigned int id) +{ + long pagesize = sysconf(_SC_PAGESIZE); + void *aligned_addr; + __maybe_unused size_t aligned_size; + + BUG_ON(!type || !name); + + /* prctl/PR_SET/VMA expects the start of an aligned memory address, but + * user may have provided address returned by malloc() which may not be + * aligned nor point to the beginning of the map + */ + aligned_addr = (void *)((uintptr_t)addr & -4096); + aligned_size = (((addr + size) - aligned_addr) + 4095) & -4096; + + if (aligned_addr != addr) { + /* provided pointer likely comes from malloc(), at least it + * doesn't come from mmap() which only returns aligned addresses + */ + if (size < pagesize) + return; + } +#if defined(USE_PRCTL) && defined(PR_SET_VMA) + { + /* + * From Linux 5.17 (and if the `CONFIG_ANON_VMA_NAME` kernel config is set)`, + * anonymous regions can be named. + * We intentionally ignore errors as it should not jeopardize the memory context + * mapping whatsoever (e.g. older kernels). + * + * The naming can take up to 79 characters, accepting valid ASCII values + * except [, ], \, $ and '. + * As a result, when looking for /proc//maps, we can see the anonymous range + * as follow : + * `7364c4fff000-736508000000 rw-s 00000000 00:01 3540 [anon_shmem:scope:name{-id}]` + * (MAP_SHARED) + * `7364c4fff000-736508000000 rw-s 00000000 00:01 3540 [anon:scope:name{-id}]` + * (MAP_PRIVATE) + */ + char fullname[80]; + int rn; + + if (id != ~0) + rn = snprintf(fullname, sizeof(fullname), "%s:%s-%u", type, name, id); + else + rn = snprintf(fullname, sizeof(fullname), "%s:%s", type, name); + + if (rn >= 0) { + /* Give a name to the map by setting PR_SET_VMA_ANON_NAME attribute + * using prctl/PR_SET_VMA combination. + * + * note from 'man prctl': + * assigning an attribute to a virtual memory area might prevent it + * from being merged with adjacent virtual memory areas due to the + * difference in that attribute's value. + */ + (void)prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, + aligned_addr, aligned_size, fullname); + } + } +#endif +} + +/* wrapper for vma_set_name_id() but without id */ +void vma_set_name(void *addr, size_t size, const char *type, const char *name) +{ + vma_set_name_id(addr, size, type, name, ~0); +} + #if defined(RTLD_DEFAULT) || defined(RTLD_NEXT) /* redefine dlopen() so that we can detect unexpected replacement of some * critical symbols, typically init/alloc/free functions coming from alternate -- cgit v1.2.3