From 4f5651906c066bae1aa893e17c25078790223168 Mon Sep 17 00:00:00 2001 From: evgeny Date: Wed, 21 Oct 2026 19:09:03 +0200 Subject: [PATCH] Report precise INI read failures and identity repair reasons --- src/config_ini.h | 48 +++++++++++++++++++++++++++++++------------- src/config_updater.c | 23 +++++++++++++++++---- 2 files changed, 53 insertions(+), 18 deletions(-) diff --git a/src/config_ini.h b/src/config_ini.h index b5c226ef..fe4c5c67 100644 --- a/src/config_ini.h +++ b/src/config_ini.h @@ -10,6 +10,7 @@ #include "../lib/mem.h" #include "../lib/strbuf.h" #include +#include #include #include @@ -54,10 +55,16 @@ static inline int config_ini_key_value(const char *line, char *key, size_t key_c /* Проверить физические строки до разбора, чтобы не принять хвост длинной строки за новую опцию. */ static inline int config_ini_validate(const char *text, size_t length, const char *filename) { size_t column = 0, line = 1; - if (!text && length) { DEBUG_ERROR(DEBUG_CATEGORY_CONFIG, "%s: NULL configuration buffer", filename); return -1; } + if (!text && length) { DEBUG_ERROR(DEBUG_CATEGORY_CONFIG, "%s: NULL configuration buffer", filename); errno = EINVAL; return -1; } for (size_t i = 0; i < length; i++) { - if (!text[i] || (text[i] != '\n' && ++column >= CONFIG_INI_LINE_SIZE - 1)) { - DEBUG_ERROR(DEBUG_CATEGORY_CONFIG, "%s:%zu: INI contains NUL or a line longer than 1022 bytes", filename, line); + if (!text[i]) { + DEBUG_ERROR(DEBUG_CATEGORY_CONFIG, "%s:%zu: embedded NUL at byte %zu", filename, line, i); + errno = EINVAL; + return -1; + } + if (text[i] != '\n' && ++column >= CONFIG_INI_LINE_SIZE - 1) { + DEBUG_ERROR(DEBUG_CATEGORY_CONFIG, "%s:%zu: INI line exceeds 1022 bytes", filename, line); + errno = EOVERFLOW; return -1; } if (text[i] == '\n') { column = 0; line++; } @@ -73,26 +80,39 @@ static inline int config_ini_read(FILE *file, const char *filename, char **text, *text = NULL; *length = 0; while ((count = fread(chunk, 1, sizeof(chunk), file)) != 0) { - if (memchr(chunk, '\0', count) || output.len > UINT32_MAX / 2 - count - 1 || - strbuf_addf(&output, "%.*s", (int)count, chunk) < 0) { - DEBUG_ERROR(DEBUG_CATEGORY_CONFIG, "%s: cannot read INI text (NUL, size overflow or allocation failure)", filename); - strbuf_free(&output); - return -1; + const char *nul = memchr(chunk, '\0', count); + if (nul) { + DEBUG_ERROR(DEBUG_CATEGORY_CONFIG, "%s: embedded NUL at byte %zu", filename, output.len + (size_t)(nul - chunk)); + errno = EINVAL; + goto failed; + } + if (output.len > UINT32_MAX / 2 - count - 1) { + DEBUG_ERROR(DEBUG_CATEGORY_CONFIG, "%s: INI text exceeds allocator size limit", filename); + errno = EFBIG; + goto failed; + } + if (strbuf_addf(&output, "%.*s", (int)count, chunk) < 0) { + DEBUG_ERROR(DEBUG_CATEGORY_CONFIG, "%s: allocation failed while reading INI at byte %zu", filename, output.len); + errno = ENOMEM; + goto failed; } } - if (ferror(file) || config_ini_validate(strbuf_str(&output), output.len, filename)) { - DEBUG_ERROR(DEBUG_CATEGORY_CONFIG, "%s: configuration read/validation failed", filename); - strbuf_free(&output); - return -1; + if (ferror(file)) { + DEBUG_ERROR(DEBUG_CATEGORY_CONFIG, "%s: read failed at byte %zu: %s", filename, output.len, strerror(errno)); + goto failed; } + if (config_ini_validate(strbuf_str(&output), output.len, filename)) goto failed; if (!output.buf && strbuf_addf(&output, "%s", "") < 0) { DEBUG_ERROR(DEBUG_CATEGORY_CONFIG, "%s: cannot allocate empty configuration buffer", filename); - strbuf_free(&output); - return -1; + errno = ENOMEM; + goto failed; } *length = output.len; *text = strbuf_detach(&output); return 0; +failed: + strbuf_free(&output); + return -1; } static inline int config_ini_nibble(unsigned char ch) { diff --git a/src/config_updater.c b/src/config_updater.c index 6841587a..fb21a652 100644 --- a/src/config_updater.c +++ b/src/config_updater.c @@ -107,7 +107,10 @@ static int config_edit(const char *text, size_t length, const struct config_fiel int section = CFG_OTHER_SECTION; for (size_t pos = 0;;) { /* Запас покрывает вставки полей и строки: strbuf не должен вырасти до 2^32 байтов. */ - if (output->len > UINT32_MAX / 2 - 16384) return -1; + if (output->len > UINT32_MAX / 2 - 16384) { + DEBUG_ERROR(DEBUG_CATEGORY_CONFIG, "edited INI exceeds allocator size limit"); + return -1; + } for (int s = CFG_GLOBAL_SECTION; s < CFG_SECTION_COUNT; s++) { if (ends[s] == pos && config_insert_missing(output, fields, s, 1, eol)) return -1; } @@ -130,7 +133,10 @@ static int config_edit(const char *text, size_t length, const struct config_fiel } else if (strbuf_addf(output, "%.*s", (int)(end - pos), text + pos) < 0) return -1; pos = end; } - if (output->len > UINT32_MAX / 2 - 16384) return -1; + if (output->len > UINT32_MAX / 2 - 16384) { + DEBUG_ERROR(DEBUG_CATEGORY_CONFIG, "edited INI exceeds allocator size limit"); + return -1; + } for (int s = CFG_GLOBAL_SECTION; s < CFG_SECTION_COUNT; s++) { if (ends[s] == SIZE_MAX && config_insert_missing(output, fields, s, 0, eol)) return -1; } @@ -192,7 +198,12 @@ static int config_publish(const char *filename, const char *text, size_t length, struct stat current; if (!lstat(filename, ¤t)) { if (!exists || !S_ISREG(current.st_mode) || current.st_nlink != 1 || - current.st_dev != original->st_dev || current.st_ino != original->st_ino) { errno = EINVAL; goto done; } + current.st_dev != original->st_dev || current.st_ino != original->st_ino) { + DEBUG_ERROR(DEBUG_CATEGORY_CONFIG, "%s: destination changed or unsupported: existed=%d mode=%o links=%llu", + filename, exists, (unsigned)current.st_mode, (unsigned long long)current.st_nlink); + errno = EINVAL; + goto done; + } } else if (exists || errno != ENOENT) goto done; parent = u_strdup(filename); if (!parent) goto done; @@ -327,7 +338,8 @@ int config_ensure_keys_and_node_id(const char *filename) { struct strbuf output = strbuf_new(); if (config_ini_hex(fields[CFG_PRIVATE].value, private_key, sizeof(private_key))) { struct SC_MYKEYS keys; - DEBUG_WARN(DEBUG_CATEGORY_CONFIG, "%s: generating identity: effective private key missing or invalid", filename); + DEBUG_WARN(DEBUG_CATEGORY_CONFIG, "%s: generating identity: effective private key %s (length=%zu)", filename, + fields[CFG_PRIVATE].present ? "invalid" : "missing", strlen(fields[CFG_PRIVATE].value)); if (sc_generate_keypair(&keys) != SC_OK) goto done; memcpy(private_key, keys.private_key, sizeof(private_key)); bytes_to_hex(private_key, sizeof(private_key), fields[CFG_PRIVATE].value, sizeof(fields[CFG_PRIVATE].value)); @@ -348,11 +360,14 @@ int config_ensure_keys_and_node_id(const char *filename) { if (reality_enabled) { if (config_ini_hex(fields[CFG_REALITY_PRIVATE].value, reality_private, sizeof(reality_private))) { uint8_t public_part[REALITY_AUTH_KEY_SIZE]; + DEBUG_WARN(DEBUG_CATEGORY_CONFIG, "%s: generating REALITY private key: %s", filename, + fields[CFG_REALITY_PRIVATE].present ? "invalid" : "missing"); if (reality_generate_keypair(reality_private, public_part) != REALITY_OK) goto done; bytes_to_hex(reality_private, sizeof(reality_private), fields[CFG_REALITY_PRIVATE].value, sizeof(fields[CFG_REALITY_PRIVATE].value)); fields[CFG_REALITY_PRIVATE].update = 1; } if (config_ini_hex_list(fields[CFG_REALITY_IDS].value, &ids[0][0], REALITY_SHORT_ID_SIZE, REALITY_MAX_SHORT_IDS, &id_count) || !id_count) { + DEBUG_WARN(DEBUG_CATEGORY_CONFIG, "%s: generating 4 REALITY short IDs: effective list missing, empty or invalid", filename); id_count = 4; if (random_bytes(&ids[0][0], (size_t)id_count * REALITY_SHORT_ID_SIZE)) goto done; char *cursor = fields[CFG_REALITY_IDS].value;