From mboxrd@z Thu Jan 1 00:00:00 1970 Delivery-date: Fri, 19 Sep 2025 12:07:16 +0200 Received: from metis.whiteo.stw.pengutronix.de ([2a0a:edc0:2:b01:1d::104]) by lore.white.stw.pengutronix.de with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.96) (envelope-from ) id 1uzY1M-004oSk-32 for lore@lore.pengutronix.de; Fri, 19 Sep 2025 12:07:16 +0200 Received: from localhost ([127.0.0.1] helo=metis.whiteo.stw.pengutronix.de) by metis.whiteo.stw.pengutronix.de with esmtp (Exim 4.92) (envelope-from ) id 1uzY1M-00087z-Ha; Fri, 19 Sep 2025 12:07:16 +0200 Received: from mail.thorsis.com ([217.92.40.78]) by metis.whiteo.stw.pengutronix.de with esmtps (TLS1.3:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1uzY0v-0007bP-S8 for ptxdist@pengutronix.de; Fri, 19 Sep 2025 12:06:51 +0200 Received: from [127.0.0.1] (localhost [127.0.0.1]) by localhost (Mailerdaemon) with ESMTPSA id 0BFF51489C1F for ; Fri, 19 Sep 2025 12:06:49 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=thorsis.com; s=dkim; t=1758276409; h=from:subject:date:message-id:to:mime-version:content-type: content-transfer-encoding:in-reply-to:references; bh=ijVid7SA8ObBu+vcKHXN0J2iVG6Irh1RbosDPDEjSoI=; b=iCV9Cd7booFeddXKqI5cwjCY1zozyOmCEYX7NWy1AXDpP8iI67w/080Q/o5A/gSR9T4LFD fDej9AZf2ZtFKELCfJtK8+nurnagt7R9ecYhD1eX2ejNPUTSuGdt326+pI9mvihlYXspjG 6ho+GNV4bI4EsoApu7+YgmVatrhk7P59Pc5DVDuxkO3YrYDL+hg+mAY+6H02z5JsuugoBt p2hMbqCsZ38PGjOcte5jowhaWN2BgEBlR6xINqbbm7NdkwqkJoFo7ahll3XGNx5YcTb3Y9 hgawkl4tfh+rQMnscRvgHoCSe4Zl7Ik7/LPyItwtCzT6b9lKuCQdMke9TGTFWw== To: ptxdist@pengutronix.de Date: Fri, 19 Sep 2025 12:06:42 +0200 Message-Id: <20250919100643.199174-5-ada@thorsis.com> X-Mailer: git-send-email 2.39.5 In-Reply-To: <20250919100643.199174-1-ada@thorsis.com> References: <20250919100643.199174-1-ada@thorsis.com> MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-Last-TLS-Session-Version: TLSv1.3 X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on metis.whiteo.stw.pengutronix.de X-Spam-Level: X-Spam-Status: No, score=-3.3 required=4.0 tests=AWL,BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,RCVD_IN_VALIDITY_RPBL_BLOCKED, RCVD_IN_VALIDITY_SAFE_BLOCKED,SPF_HELO_NONE,SPF_PASS, T_FILL_THIS_FORM_SHORT autolearn=ham autolearn_force=no version=3.4.2 Subject: [ptxdist] [PATCH v2 4/5] libubootenv: Import some fixes from master X-BeenThere: ptxdist@pengutronix.de X-Mailman-Version: 2.1.29 Precedence: list List-Id: PTXdist Development Mailing List List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , From: Alexander Dahl via ptxdist Reply-To: ptxdist@pengutronix.de Cc: Alexander Dahl Sender: "ptxdist" X-SA-Exim-Connect-IP: 127.0.0.1 X-SA-Exim-Mail-From: ptxdist-bounces@pengutronix.de X-SA-Exim-Scanned: No (on metis.whiteo.stw.pengutronix.de); SAEximRunCond expanded to false Not part of a new libubootenv release yet, but at least the ubi volume fix is necessary. Otherwise fw_printenv might fail on arm v7a storing u-boot env in an ubi volume like this: root@DistroKit:/mnt/data/tmp fw_printenv *** buffer overflow detected ***: terminated Aborted (core dumped) Picked the other fixes along the way. Signed-off-by: Alexander Dahl --- Notes: v2: - new patch, not present in v1 .../0001-Make-libyaml-optional.patch | 1507 +++++++++++++++++ ..._config-fix-segfault-on-empty-config.patch | 40 + ...-Fix-warning-when-copying-UBI-volume.patch | 35 + ...segfault-due-to-uninitialized-pointe.patch | 32 + ...nfig.c-Catch-NULL-pointer-for-calloc.patch | 30 + patches/libubootenv-0.3.6/series | 8 + rules/libubootenv.make | 3 +- 7 files changed, 1654 insertions(+), 1 deletion(-) create mode 100644 patches/libubootenv-0.3.6/0001-Make-libyaml-optional.patch create mode 100644 patches/libubootenv-0.3.6/0002-extended_config-fix-segfault-on-empty-config.patch create mode 100644 patches/libubootenv-0.3.6/0003-BUG-Fix-warning-when-copying-UBI-volume.patch create mode 100644 patches/libubootenv-0.3.6/0004-libubootenv-fix-segfault-due-to-uninitialized-pointe.patch create mode 100644 patches/libubootenv-0.3.6/0005-extended_config.c-Catch-NULL-pointer-for-calloc.patch create mode 100644 patches/libubootenv-0.3.6/series diff --git a/patches/libubootenv-0.3.6/0001-Make-libyaml-optional.patch b/patches/libubootenv-0.3.6/0001-Make-libyaml-optional.patch new file mode 100644 index 000000000..631d0f1ef --- /dev/null +++ b/patches/libubootenv-0.3.6/0001-Make-libyaml-optional.patch @@ -0,0 +1,1507 @@ +From: Stefano Babic +Date: Tue, 29 Oct 2024 09:23:20 +0100 +Subject: [PATCH] Make libyaml optional + +NewYAML format is required for extended features because the format +foreseen by U-Boot is very limited. However, some systems due to +low resources don't want to link to libyaml. Add the option +NO_YML_SUPPORT to disable YAML configuration file and just use +fw_env.config in the U-Boot format. + +There are no functional changes in this patch - function depending on +YML are moved in a separate file, and some functions are factorized. + +Signed-off-by: Stefano Babic +--- + CMakeLists.txt | 6 + + src/CMakeLists.txt | 9 +- + src/common.c | 248 +++++++++++++++++++ + src/common.h | 15 ++ + src/extended_config.c | 452 ++++++++++++++++++++++++++++++++++ + src/uboot_env.c | 655 +------------------------------------------------- + 6 files changed, 735 insertions(+), 650 deletions(-) + create mode 100644 src/common.c + create mode 100644 src/common.h + create mode 100644 src/extended_config.c + +diff --git a/CMakeLists.txt b/CMakeLists.txt +index 3bb93e16b42b..796d7bcffb29 100644 +--- a/CMakeLists.txt ++++ b/CMakeLists.txt +@@ -12,6 +12,8 @@ set(VERSION "0.3.6") + SET(SOVERSION "0") + add_definitions(-DVERSION="${VERSION}") + ++option(NO_YML_SUPPORT "YML Support") ++ + if(DEFAULT_CFG_FILE) + add_definitions(-DDEFAULT_CFG_FILE="${DEFAULT_CFG_FILE}") + endif(DEFAULT_CFG_FILE) +@@ -20,6 +22,10 @@ if(DEFAULT_ENV_FILE) + add_definitions(-DDEFAULT_ENV_FILE="${DEFAULT_ENV_FILE}") + endif(DEFAULT_ENV_FILE) + ++if(NO_YML_SUPPORT) ++ add_definitions(-DNO_YAML_SUPPORT) ++endif(NO_YML_SUPPORT) ++ + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=gnu99") + + #set(CMAKE_C_FLAGS_DEBUG "-g") +diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt +index 63d48225f83b..c56d0c756528 100644 +--- a/src/CMakeLists.txt ++++ b/src/CMakeLists.txt +@@ -6,6 +6,9 @@ cmake_minimum_required (VERSION 2.6) + SET(libubootenv_SOURCES + uboot_env.c + uboot_mtd.c ++ extended_config.c ++ common.c ++ common.h + uboot_private.h + ) + +@@ -22,7 +25,11 @@ SET_TARGET_PROPERTIES(ubootenv PROPERTIES VERSION ${VERSION} SOVERSION ${SOVERSI + ADD_LIBRARY(ubootenv_static STATIC ${libubootenv_SOURCES} ${include_HEADERS}) + SET_TARGET_PROPERTIES(ubootenv_static PROPERTIES OUTPUT_NAME ubootenv) + add_executable(fw_printenv fw_printenv.c) +-target_link_libraries(ubootenv z yaml) ++target_link_libraries(ubootenv z) ++if (NOT NO_YML_SUPPORT) ++target_link_libraries(ubootenv yaml) ++endif(NOT NO_YML_SUPPORT) ++ + target_link_libraries(fw_printenv ubootenv) + add_custom_target(fw_setenv ALL ${CMAKE_COMMAND} -E create_symlink fw_printenv fw_setenv) + +diff --git a/src/common.c b/src/common.c +new file mode 100644 +index 000000000000..2cbde93a7140 +--- /dev/null ++++ b/src/common.c +@@ -0,0 +1,248 @@ ++/* ++ * (C) Copyright 2024 ++ * Stefano Babic, ++ * ++ * SPDX-License-Identifier: LGPL-2.1-or-later ++ */ ++ ++#define _GNU_SOURCE ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#ifdef __FreeBSD__ ++#include ++#define BLKGETSIZE64 DIOCGMEDIASIZE ++#else ++#include ++#endif ++ ++#include "uboot_private.h" ++#include "common.h" ++ ++static enum device_type get_device_type(char *device) ++{ ++ enum device_type type = DEVICE_NONE; ++ ++ if (!strncmp(device, DEVICE_MTD_NAME, strlen(DEVICE_MTD_NAME))) ++ if (strchr(device, DEVNAME_SEPARATOR)) { ++ type = DEVICE_UBI; ++ } else { ++ type = DEVICE_MTD; ++ } ++ else if (!strncmp(device, DEVICE_UBI_NAME, strlen(DEVICE_UBI_NAME))) ++ type = DEVICE_UBI; ++ else if (strlen(device) > 0) ++ type = DEVICE_FILE; ++ ++ return type; ++} ++ ++int normalize_device_path(char *path, struct uboot_flash_env *dev) ++{ ++ char *sep = NULL, *normalized = NULL; ++ size_t normalized_len = 0, volume_len = 0, output_len = 0; ++ ++ /* ++ * if volume name is present, split into device path and volume ++ * since only the device path needs normalized ++ */ ++ sep = strchr(path, DEVNAME_SEPARATOR); ++ if (sep) ++ { ++ volume_len = strlen(sep); ++ *sep = '\0'; ++ } ++ ++ if ((normalized = realpath(path, NULL)) == NULL) ++ { ++ /* device file didn't exist */ ++ return -EINVAL; ++ } ++ ++ normalized_len = strlen(normalized); ++ output_len = sizeof(dev->devname) - 1; /* leave room for null */ ++ if ((normalized_len + volume_len) > output_len) ++ { ++ /* full name is too long to fit */ ++ free(normalized); ++ return -EINVAL; ++ } ++ ++ /* ++ * save normalized path to device file, ++ * and possibly append separator char & volume name ++ */ ++ memset(dev->devname, 0, sizeof(dev->devname)); ++ strncpy(dev->devname, normalized, output_len); ++ free(normalized); ++ ++ if (sep) ++ { ++ *sep = DEVNAME_SEPARATOR; ++ strncpy(dev->devname + normalized_len, sep, output_len - normalized_len); ++ } ++ ++ return 0; ++} ++ ++void set_var_access_type(struct var_entry *entry, const char *pvarflags) ++{ ++ if (entry) { ++ for (int i = 0; i < strlen(pvarflags); i++) { ++ switch (pvarflags[i]) { ++ case 's': ++ entry->type = TYPE_ATTR_STRING; ++ break; ++ case 'd': ++ entry->type = TYPE_ATTR_DECIMAL; ++ break; ++ case 'x': ++ entry->type = TYPE_ATTR_HEX; ++ break; ++ case 'b': ++ entry->type = TYPE_ATTR_BOOL; ++ break; ++ case 'i': ++ entry->type = TYPE_ATTR_IP; ++ break; ++ case 'm': ++ entry->type = TYPE_ATTR_MAC; ++ break; ++ case 'a': ++ entry->access = ACCESS_ATTR_ANY; ++ break; ++ case 'r': ++ entry->access = ACCESS_ATTR_READ_ONLY; ++ break; ++ case 'o': ++ entry->access = ACCESS_ATTR_WRITE_ONCE; ++ break; ++ case 'c': ++ entry->access = ACCESS_ATTR_CHANGE_DEFAULT; ++ break; ++ default: /* ignore it */ ++ break; ++ } ++ } ++ } ++} ++ ++struct var_entry *create_var_entry(const char *name) ++{ ++ struct var_entry *entry; ++ ++ entry = (struct var_entry *)calloc(1, sizeof(*entry)); ++ if (!entry) ++ return NULL; ++ entry->name = strdup(name); ++ if (!entry->name) { ++ free(entry); ++ return NULL; ++ } ++ ++ return entry; ++} ++ ++bool check_compatible_devices(struct uboot_ctx *ctx) ++{ ++ if (!ctx->redundant) ++ return true; ++ ++ if (ctx->envdevs[0].mtdinfo.type != ctx->envdevs[1].mtdinfo.type) ++ return false; ++ if (ctx->envdevs[0].flagstype != ctx->envdevs[1].flagstype) ++ return false; ++ if (ctx->envdevs[0].envsize != ctx->envdevs[1].envsize) ++ return false; ++ ++ return true; ++} ++ ++int check_env_device(struct uboot_flash_env *dev) ++{ ++ int fd, ret; ++ struct stat st; ++ ++ dev->device_type = get_device_type(dev->devname); ++ if (dev->device_type == DEVICE_NONE) ++ return -EBADF; ++ ++ if (dev->device_type == DEVICE_UBI) { ++ ret = libubootenv_ubi_update_name(dev); ++ if (ret) ++ return ret; ++ } ++ ++ ret = stat(dev->devname, &st); ++ if (ret < 0) ++ return -EBADF; ++ fd = open(dev->devname, O_RDONLY); ++ if (fd < 0) ++ return -EBADF; ++ ++ if (S_ISCHR(st.st_mode)) { ++ if (dev->device_type == DEVICE_MTD) { ++ ret = libubootenv_mtdgetinfo(fd, dev); ++ if (ret < 0 || (dev->mtdinfo.type != MTD_NORFLASH && ++ dev->mtdinfo.type != MTD_NANDFLASH)) { ++ close(fd); ++ return -EBADF; ++ } ++ if (dev->sectorsize == 0) { ++ dev->sectorsize = dev->mtdinfo.erasesize; ++ } ++ } ++ } ++ ++ switch (dev->device_type) { ++ case DEVICE_FILE: ++ dev->flagstype = FLAGS_INCREMENTAL; ++ break; ++ case DEVICE_MTD: ++ switch (dev->mtdinfo.type) { ++ case MTD_NORFLASH: ++ dev->flagstype = FLAGS_BOOLEAN; ++ break; ++ case MTD_NANDFLASH: ++ dev->flagstype = FLAGS_INCREMENTAL; ++ }; ++ break; ++ case DEVICE_UBI: ++ dev->flagstype = FLAGS_INCREMENTAL; ++ break; ++ default: ++ close(fd); ++ return -EBADF; ++ }; ++ ++ /* ++ * Check for negative offsets, treat it as backwards offset ++ * from the end of the block device ++ */ ++ if (dev->offset < 0) { ++ uint64_t blkdevsize; ++ int rc; ++ ++ rc = ioctl(fd, BLKGETSIZE64, &blkdevsize); ++ if (rc < 0) { ++ close(fd); ++ return -EINVAL; ++ } ++ ++ dev->offset += blkdevsize; ++ } ++ ++ close(fd); ++ ++ return 0; ++} +diff --git a/src/common.h b/src/common.h +new file mode 100644 +index 000000000000..adacd4896741 +--- /dev/null ++++ b/src/common.h +@@ -0,0 +1,15 @@ ++/* ++ * (C) Copyright 2024 ++ * Stefano Babic, ++ * ++ * SPDX-License-Identifier: LGPL-2.1-or-later ++ */ ++ ++ ++#include "uboot_private.h" ++ ++struct var_entry *create_var_entry(const char *name); ++void set_var_access_type(struct var_entry *entry, const char *pvarflags); ++int normalize_device_path(char *path, struct uboot_flash_env *dev); ++int check_env_device(struct uboot_flash_env *dev); ++bool check_compatible_devices(struct uboot_ctx *ctx); +diff --git a/src/extended_config.c b/src/extended_config.c +new file mode 100644 +index 000000000000..ec814f484efd +--- /dev/null ++++ b/src/extended_config.c +@@ -0,0 +1,452 @@ ++/* ++ * (C) Copyright 2024 ++ * Stefano Babic, ++ * ++ * SPDX-License-Identifier: LGPL-2.1-or-later ++ */ ++ ++/** ++ * @file extended_config.c ++ * ++ * @brief Implement the extended config file YAML ++ * ++ */ ++#define _GNU_SOURCE ++ ++#if !defined(NO_YAML_SUPPORT) ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "uboot_private.h" ++#include "common.h" ++ ++/* yaml_* functions return 1 on success and 0 on failure. */ ++enum yaml_status { ++ SUCCESS = 0, ++ FAILURE = 1 ++}; ++ ++enum yaml_state { ++ STATE_START, /* start state */ ++ STATE_STREAM, /* start/end stream */ ++ STATE_DOCUMENT, /* start/end document */ ++ STATE_SECTION, /* top level */ ++ ++ STATE_NAMESPACE, /* Init Configuration Namespace */ ++ STATE_NAMESPACE_FIELDS, /* namespace key list */ ++ STATE_NKEY, /* Check key names */ ++ STATE_NSIZE, /* Size key-value pair */ ++ STATE_NLOCKFILE, /* Lockfile key-value pair */ ++ STATE_DEVVALUES, /* Devices key names */ ++ STATE_WRITELIST, /* List with vars that are accepted by write ++ * if list is missing, all vars are accepted ++ * var is in the format name:flags, see U-Boot ++ * documentation ++ */ ++ ++ STATE_NPATH, ++ STATE_NOFFSET, ++ STATE_NSECTORSIZE, ++ STATE_NUNLOCK, ++ STATE_STOP /* end state */ ++}; ++ ++typedef enum yaml_parse_error_e { ++ YAML_UNEXPECTED_STATE, ++ YAML_UNEXPECTED_KEY, ++ YAML_BAD_DEVICE, ++ YAML_BAD_DEVNAME, ++ YAML_BAD_VARLIST, ++ YAML_DUPLICATE_VARLIST, ++ YAML_OOM, ++} yaml_parse_error_type_t; ++ ++struct parser_state { ++ enum yaml_state state; /* The current parse state */ ++ struct uboot_ctx *ctxsets; /* Array of vars set ctx */ ++ struct uboot_ctx *ctx; /* Current ctx in parsing */ ++ unsigned int nelem; /* Number of elemets in ctxsets */ ++ unsigned int cdev; /* current device in parsing */ ++ yaml_parse_error_type_t error; /* error causing parser to stop */ ++ yaml_event_type_t event_type; /* event type causing error */ ++}; ++ ++static int consume_event(struct parser_state *s, yaml_event_t *event) ++{ ++ char *value; ++ struct uboot_flash_env *dev; ++ struct uboot_ctx *newctx; ++ int cdev; ++ ++ switch (s->state) { ++ case STATE_START: ++ switch (event->type) { ++ case YAML_STREAM_START_EVENT: ++ s->state = STATE_STREAM; ++ break; ++ default: ++ s->error = YAML_UNEXPECTED_STATE; ++ s->event_type = event->type; ++ return FAILURE; ++ } ++ break; ++ ++ case STATE_STREAM: ++ switch (event->type) { ++ case YAML_DOCUMENT_START_EVENT: ++ s->state = STATE_DOCUMENT; ++ break; ++ case YAML_STREAM_END_EVENT: ++ s->state = STATE_STOP; ++ break; ++ default: ++ s->error = YAML_UNEXPECTED_STATE; ++ s->event_type = event->type; ++ return FAILURE; ++ } ++ break; ++ ++ case STATE_DOCUMENT: ++ switch (event->type) { ++ case YAML_MAPPING_START_EVENT: ++ s->state = STATE_SECTION; ++ break; ++ case YAML_DOCUMENT_END_EVENT: ++ s->state = STATE_STREAM; ++ break; ++ default: ++ s->error = YAML_UNEXPECTED_STATE; ++ s->event_type = event->type; ++ return FAILURE; ++ } ++ break; ++ ++ case STATE_SECTION: ++ switch (event->type) { ++ case YAML_SCALAR_EVENT: ++ value = (char *)event->data.scalar.value; ++ newctx = calloc (s->nelem + 1, sizeof(*newctx)); ++ for (int i = 0; i < s->nelem; i++) { ++ newctx[i] = s->ctxsets[i]; ++ } ++ if (s->ctxsets) free(s->ctxsets); ++ s->ctxsets = newctx; ++ s->ctx = &newctx[s->nelem]; ++ s->ctx->name = strdup(value); ++ s->nelem++; ++ s->state = STATE_NAMESPACE; ++ break; ++ case YAML_MAPPING_END_EVENT: ++ s->state = STATE_DOCUMENT; ++ break; ++ case YAML_DOCUMENT_END_EVENT: ++ s->state = STATE_STREAM; ++ break; ++ default: ++ s->error = YAML_UNEXPECTED_STATE; ++ s->event_type = event->type; ++ return FAILURE; ++ } ++ break; ++ ++ case STATE_NAMESPACE: ++ switch (event->type) { ++ case YAML_MAPPING_START_EVENT: ++ s->state = STATE_NAMESPACE_FIELDS; ++ break; ++ default: ++ s->error = YAML_UNEXPECTED_STATE; ++ s->event_type = event->type; ++ return FAILURE; ++ } ++ break; ++ ++ case STATE_NAMESPACE_FIELDS: ++ switch (event->type) { ++ case YAML_SCALAR_EVENT: ++ value = (char *)event->data.scalar.value; ++ if (!strcmp(value, "size")) { ++ s->state = STATE_NSIZE; ++ } else if (!strcmp(value, "lockfile")) { ++ s->state = STATE_NLOCKFILE; ++ } else if (!strcmp(value, "devices")) { ++ s->state = STATE_DEVVALUES; ++ s->cdev = 0; ++ } else if (!strcmp(value, "writelist")) { ++ s->state = STATE_WRITELIST; ++ } else { ++ s->error = YAML_UNEXPECTED_KEY; ++ s->event_type = event->type; ++ return FAILURE; ++ } ++ break; ++ case YAML_MAPPING_END_EVENT: ++ s->state = STATE_SECTION; ++ break; ++ default: ++ s->error = YAML_UNEXPECTED_STATE; ++ s->event_type = event->type; ++ return FAILURE; ++ } ++ break; ++ ++ case STATE_NSIZE: ++ switch (event->type) { ++ case YAML_SCALAR_EVENT: ++ value = (char *)event->data.scalar.value; ++ errno = 0; ++ s->ctx->size = strtoull(value, NULL, 0); ++ s->state = STATE_NAMESPACE_FIELDS; ++ break; ++ default: ++ s->error = YAML_UNEXPECTED_STATE; ++ s->event_type = event->type; ++ return FAILURE; ++ } ++ break; ++ ++ case STATE_NLOCKFILE: ++ switch (event->type) { ++ case YAML_SCALAR_EVENT: ++ value = (char *)event->data.scalar.value; ++ s->ctx->lockfile = strdup(value); ++ s->state = STATE_NAMESPACE_FIELDS; ++ break; ++ default: ++ s->error = YAML_UNEXPECTED_STATE; ++ s->event_type = event->type; ++ return FAILURE; ++ } ++ break; ++ ++ case STATE_DEVVALUES: ++ switch (event->type) { ++ case YAML_MAPPING_START_EVENT: ++ case YAML_SEQUENCE_START_EVENT: ++ break; ++ case YAML_MAPPING_END_EVENT: ++ dev = &s->ctx->envdevs[s->cdev]; ++ if (check_env_device(dev) < 0) { ++ s->error = YAML_BAD_DEVICE; ++ s->event_type = event->type; ++ return FAILURE; ++ } ++ s->cdev++; ++ break; ++ case YAML_SEQUENCE_END_EVENT: ++ s->state = STATE_NAMESPACE_FIELDS; ++ break; ++ case YAML_SCALAR_EVENT: ++ value = (char *)event->data.scalar.value; ++ if (s->cdev) ++ s->ctx->redundant = true; ++ if (!strcmp(value, "path")) { ++ s->state = STATE_NPATH; ++ } else if (!strcmp(value, "offset")) { ++ s->state = STATE_NOFFSET; ++ } else if (!strcmp(value, "sectorsize")) { ++ s->state = STATE_NSECTORSIZE; ++ } else if (!strcmp(value, "disablelock")) { ++ s->state = STATE_NUNLOCK; ++ } else { ++ s->error = YAML_UNEXPECTED_KEY; ++ s->event_type = event->type; ++ return FAILURE; ++ } ++ break; ++ default: ++ s->error = YAML_UNEXPECTED_STATE; ++ s->event_type = event->type; ++ return FAILURE; ++ } ++ break; ++ ++ case STATE_WRITELIST: ++ switch (event->type) { ++ ++ char *varflag, *name; ++ struct var_entry *entry; ++ ++ case YAML_MAPPING_START_EVENT: ++ case YAML_SEQUENCE_START_EVENT: ++ break; ++ case YAML_MAPPING_END_EVENT: ++ break; ++ case YAML_SEQUENCE_END_EVENT: ++ s->state = STATE_NAMESPACE_FIELDS; ++ break; ++ case YAML_SCALAR_EVENT: ++ value = (char *)event->data.scalar.value; ++ ++ /* ++ * Format is name:flags, split it into two values ++ */ ++ varflag = strchr(value, ':'); ++ if (!varflag || varflag > value + (strlen(value) - 1)) { ++ s->error = YAML_BAD_VARLIST; ++ s->event_type = event->type; ++ return FAILURE; ++ } ++ *varflag++ = '\0'; ++ ++ /* ++ * Check there is not yet an entry for this variable ++ */ ++ LIST_FOREACH(entry, &s->ctx->writevarlist, next) { ++ if (strcmp(entry->name, value) == 0) { ++ s->error = YAML_DUPLICATE_VARLIST; ++ s->event_type = event->type; ++ return FAILURE; ++ } ++ } ++ ++ /* ++ * Insert variable with its configuration into the list ++ * of modifiable vars ++ */ ++ entry = create_var_entry(value); ++ if (!entry) { ++ s->error = YAML_OOM; ++ s->event_type = event->type; ++ return FAILURE; ++ } ++ set_var_access_type(entry, varflag); ++ LIST_INSERT_HEAD(&s->ctx->writevarlist, entry, next); ++ ++#if !defined(NDEBUG) ++ fprintf(stdout, "Writelist: %s flags %s\n", value, varflag); ++#endif ++ break; ++ default: ++ s->error = YAML_UNEXPECTED_STATE; ++ s->event_type = event->type; ++ return FAILURE; ++ } ++ break; ++ ++ case STATE_NPATH: ++ switch (event->type) { ++ case YAML_SCALAR_EVENT: ++ dev = &s->ctx->envdevs[s->cdev]; ++ value = (char *)event->data.scalar.value; ++ if (normalize_device_path(value, dev) < 0) { ++ s->error = YAML_BAD_DEVNAME; ++ s->event_type = event->type; ++ return FAILURE; ++ } ++ dev->envsize = s->ctx->size; ++ s->state = STATE_DEVVALUES; ++ break; ++ default: ++ s->error = YAML_UNEXPECTED_STATE; ++ s->event_type = event->type; ++ return FAILURE; ++ } ++ break; ++ ++ case STATE_NOFFSET: ++ switch (event->type) { ++ case YAML_SCALAR_EVENT: ++ dev = &s->ctx->envdevs[s->cdev]; ++ value = (char *)event->data.scalar.value; ++ dev->offset = strtoull(value, NULL, 0); ++ s->state = STATE_DEVVALUES; ++ break; ++ default: ++ s->error = YAML_UNEXPECTED_STATE; ++ s->event_type = event->type; ++ return FAILURE; ++ } ++ break; ++ ++ case STATE_NSECTORSIZE: ++ switch (event->type) { ++ case YAML_SCALAR_EVENT: ++ dev = &s->ctx->envdevs[s->cdev]; ++ value = (char *)event->data.scalar.value; ++ dev->sectorsize = strtoull(value, NULL, 0); ++ s->state = STATE_DEVVALUES; ++ break; ++ default: ++ s->error = YAML_UNEXPECTED_STATE; ++ s->event_type = event->type; ++ return FAILURE; ++ } ++ break; ++ ++ case STATE_NUNLOCK: ++ switch (event->type) { ++ case YAML_SCALAR_EVENT: ++ dev = &s->ctx->envdevs[s->cdev]; ++ value = (char *)event->data.scalar.value; ++ if (!strcmp(value, "yes")) ++ dev->disable_mtd_lock = 1; ++ s->state = STATE_DEVVALUES; ++ break; ++ default: ++ s->error = YAML_UNEXPECTED_STATE; ++ s->event_type = event->type; ++ return FAILURE; ++ } ++ break; ++ ++ case STATE_STOP: ++ break; ++ } ++ return SUCCESS; ++} ++ ++int parse_yaml_config(struct uboot_ctx **ctxlist, FILE *fp) ++{ ++ yaml_parser_t parser; ++ yaml_event_t event; ++ enum yaml_status status; ++ struct parser_state state; ++ struct uboot_ctx *ctx; ++ ++ if (!yaml_parser_initialize(&parser)) ++ return -ENOMEM; ++ ++ /* Set input file */ ++ yaml_parser_set_input_file(&parser, fp); ++ memset(&state, 0, sizeof(state)); ++ state.state = STATE_START; ++ do { ++ if (!yaml_parser_parse(&parser, &event)) { ++ status = FAILURE; ++ goto cleanup; ++ } ++ status = consume_event(&state, &event); ++ yaml_event_delete(&event); ++ if (status == FAILURE) { ++ goto cleanup; ++ } ++ } while (state.state != STATE_STOP); ++ ++ state.ctxsets[0].nelem = state.nelem; ++ ++ for (int i = 0; i < state.nelem; i++) { ++ ctx = &state.ctxsets[i]; ++ ctx->ctxlist = &state.ctxsets[0]; ++ if (ctx->redundant && !check_compatible_devices(ctx)) { ++ status = FAILURE; ++ break; ++ } ++ } ++ ++ ++cleanup: ++ yaml_parser_delete(&parser); ++ if (status == FAILURE) { ++ if (state.ctxsets) free (state.ctxsets); ++ state.ctxsets = NULL; ++ } ++ *ctxlist = state.ctxsets; ++ return status; ++} ++#endif +diff --git a/src/uboot_env.c b/src/uboot_env.c +index 0b4f9f4d0458..d8b93dac7479 100644 +--- a/src/uboot_env.c ++++ b/src/uboot_env.c +@@ -21,12 +21,6 @@ + #include + #include + #include +-#ifdef __FreeBSD__ +-#include +-#define BLKGETSIZE64 DIOCGMEDIASIZE +-#else +-#include +-#endif + #include + #include + #include +@@ -38,60 +32,15 @@ + #include + #include + #include +-#include + + #include "uboot_private.h" ++#include "common.h" + +-/* yaml_* functions return 1 on success and 0 on failure. */ +-enum yaml_status { +- SUCCESS = 0, +- FAILURE = 1 +-}; +- +-enum yaml_state { +- STATE_START, /* start state */ +- STATE_STREAM, /* start/end stream */ +- STATE_DOCUMENT, /* start/end document */ +- STATE_SECTION, /* top level */ +- +- STATE_NAMESPACE, /* Init Configuration Namespace */ +- STATE_NAMESPACE_FIELDS, /* namespace key list */ +- STATE_NKEY, /* Check key names */ +- STATE_NSIZE, /* Size key-value pair */ +- STATE_NLOCKFILE, /* Lockfile key-value pair */ +- STATE_DEVVALUES, /* Devices key names */ +- STATE_WRITELIST, /* List with vars that are accepted by write +- * if list is missing, all vars are accepted +- * var is in the format name:flags, see U-Boot +- * documentation +- */ +- +- STATE_NPATH, +- STATE_NOFFSET, +- STATE_NSECTORSIZE, +- STATE_NUNLOCK, +- STATE_STOP /* end state */ +-}; +- +-typedef enum yaml_parse_error_e { +- YAML_UNEXPECTED_STATE, +- YAML_UNEXPECTED_KEY, +- YAML_BAD_DEVICE, +- YAML_BAD_DEVNAME, +- YAML_BAD_VARLIST, +- YAML_DUPLICATE_VARLIST, +- YAML_OOM, +-} yaml_parse_error_type_t; +- +-struct parser_state { +- enum yaml_state state; /* The current parse state */ +- struct uboot_ctx *ctxsets; /* Array of vars set ctx */ +- struct uboot_ctx *ctx; /* Current ctx in parsing */ +- unsigned int nelem; /* Number of elemets in ctxsets */ +- unsigned int cdev; /* current device in parsing */ +- yaml_parse_error_type_t error; /* error causing parser to stop */ +- yaml_event_type_t event_type; /* event type causing error */ +-}; ++#if defined(NO_YAML_SUPPORT) ++#define parse_yaml_config(ctx,fp) -1 ++#else ++extern int parse_yaml_config(struct uboot_ctx **ctxlist, FILE *fp); ++#endif + + #define FREE_ENTRY do { \ + free(entry->name); \ +@@ -152,64 +101,6 @@ static char attr_tostring(type_attribute a) + return 's'; + } + +-static void set_var_access_type(struct var_entry *entry, const char *pvarflags) +-{ +- if (entry) { +- for (int i = 0; i < strlen(pvarflags); i++) { +- switch (pvarflags[i]) { +- case 's': +- entry->type = TYPE_ATTR_STRING; +- break; +- case 'd': +- entry->type = TYPE_ATTR_DECIMAL; +- break; +- case 'x': +- entry->type = TYPE_ATTR_HEX; +- break; +- case 'b': +- entry->type = TYPE_ATTR_BOOL; +- break; +- case 'i': +- entry->type = TYPE_ATTR_IP; +- break; +- case 'm': +- entry->type = TYPE_ATTR_MAC; +- break; +- case 'a': +- entry->access = ACCESS_ATTR_ANY; +- break; +- case 'r': +- entry->access = ACCESS_ATTR_READ_ONLY; +- break; +- case 'o': +- entry->access = ACCESS_ATTR_WRITE_ONCE; +- break; +- case 'c': +- entry->access = ACCESS_ATTR_CHANGE_DEFAULT; +- break; +- default: /* ignore it */ +- break; +- } +- } +- } +-} +- +-static struct var_entry *create_var_entry(const char *name) +-{ +- struct var_entry *entry; +- +- entry = (struct var_entry *)calloc(1, sizeof(*entry)); +- if (!entry) +- return NULL; +- entry->name = strdup(name); +- if (!entry->name) { +- free(entry); +- return NULL; +- } +- +- return entry; +-} +- + static char access_tostring(access_attribute a) + { + switch(a) { +@@ -389,166 +280,6 @@ static int __libuboot_set_env(struct uboot_ctx *ctx, const char *varname, const + return 0; + } + +-static enum device_type get_device_type(char *device) +-{ +- enum device_type type = DEVICE_NONE; +- +- if (!strncmp(device, DEVICE_MTD_NAME, strlen(DEVICE_MTD_NAME))) +- if (strchr(device, DEVNAME_SEPARATOR)) { +- type = DEVICE_UBI; +- } else { +- type = DEVICE_MTD; +- } +- else if (!strncmp(device, DEVICE_UBI_NAME, strlen(DEVICE_UBI_NAME))) +- type = DEVICE_UBI; +- else if (strlen(device) > 0) +- type = DEVICE_FILE; +- +- return type; +-} +- +-static int normalize_device_path(char *path, struct uboot_flash_env *dev) +-{ +- char *sep = NULL, *normalized = NULL; +- size_t normalized_len = 0, volume_len = 0, output_len = 0; +- +- /* +- * if volume name is present, split into device path and volume +- * since only the device path needs normalized +- */ +- sep = strchr(path, DEVNAME_SEPARATOR); +- if (sep) +- { +- volume_len = strlen(sep); +- *sep = '\0'; +- } +- +- if ((normalized = realpath(path, NULL)) == NULL) +- { +- /* device file didn't exist */ +- return -EINVAL; +- } +- +- normalized_len = strlen(normalized); +- output_len = sizeof(dev->devname) - 1; /* leave room for null */ +- if ((normalized_len + volume_len) > output_len) +- { +- /* full name is too long to fit */ +- free(normalized); +- return -EINVAL; +- } +- +- /* +- * save normalized path to device file, +- * and possibly append separator char & volume name +- */ +- memset(dev->devname, 0, sizeof(dev->devname)); +- strncpy(dev->devname, normalized, output_len); +- free(normalized); +- +- if (sep) +- { +- *sep = DEVNAME_SEPARATOR; +- strncpy(dev->devname + normalized_len, sep, output_len - normalized_len); +- } +- +- return 0; +-} +- +-static int check_env_device(struct uboot_flash_env *dev) +-{ +- int fd, ret; +- struct stat st; +- +- dev->device_type = get_device_type(dev->devname); +- if (dev->device_type == DEVICE_NONE) +- return -EBADF; +- +- if (dev->device_type == DEVICE_UBI) { +- ret = libubootenv_ubi_update_name(dev); +- if (ret) +- return ret; +- } +- +- ret = stat(dev->devname, &st); +- if (ret < 0) +- return -EBADF; +- fd = open(dev->devname, O_RDONLY); +- if (fd < 0) +- return -EBADF; +- +- if (S_ISCHR(st.st_mode)) { +- if (dev->device_type == DEVICE_MTD) { +- ret = libubootenv_mtdgetinfo(fd, dev); +- if (ret < 0 || (dev->mtdinfo.type != MTD_NORFLASH && +- dev->mtdinfo.type != MTD_NANDFLASH)) { +- close(fd); +- return -EBADF; +- } +- if (dev->sectorsize == 0) { +- dev->sectorsize = dev->mtdinfo.erasesize; +- } +- } +- } +- +- switch (dev->device_type) { +- case DEVICE_FILE: +- dev->flagstype = FLAGS_INCREMENTAL; +- break; +- case DEVICE_MTD: +- switch (dev->mtdinfo.type) { +- case MTD_NORFLASH: +- dev->flagstype = FLAGS_BOOLEAN; +- break; +- case MTD_NANDFLASH: +- dev->flagstype = FLAGS_INCREMENTAL; +- }; +- break; +- case DEVICE_UBI: +- dev->flagstype = FLAGS_INCREMENTAL; +- break; +- default: +- close(fd); +- return -EBADF; +- }; +- +- /* +- * Check for negative offsets, treat it as backwards offset +- * from the end of the block device +- */ +- if (dev->offset < 0) { +- uint64_t blkdevsize; +- int rc; +- +- rc = ioctl(fd, BLKGETSIZE64, &blkdevsize); +- if (rc < 0) { +- close(fd); +- return -EINVAL; +- } +- +- dev->offset += blkdevsize; +- } +- +- close(fd); +- +- return 0; +-} +- +-static bool check_compatible_devices(struct uboot_ctx *ctx) +-{ +- if (!ctx->redundant) +- return true; +- +- if (ctx->envdevs[0].mtdinfo.type != ctx->envdevs[1].mtdinfo.type) +- return false; +- if (ctx->envdevs[0].flagstype != ctx->envdevs[1].flagstype) +- return false; +- if (ctx->envdevs[0].envsize != ctx->envdevs[1].envsize) +- return false; +- +- return true; +-} +- + static int fileread(struct uboot_flash_env *dev, void *data) + { + int ret = 0; +@@ -1035,380 +766,6 @@ static int libuboot_load(struct uboot_ctx *ctx) + return ctx->valid ? 0 : -ENODATA; + } + +-static int consume_event(struct parser_state *s, yaml_event_t *event) +-{ +- char *value; +- struct uboot_flash_env *dev; +- struct uboot_ctx *newctx; +- int cdev; +- +- switch (s->state) { +- case STATE_START: +- switch (event->type) { +- case YAML_STREAM_START_EVENT: +- s->state = STATE_STREAM; +- break; +- default: +- s->error = YAML_UNEXPECTED_STATE; +- s->event_type = event->type; +- return FAILURE; +- } +- break; +- +- case STATE_STREAM: +- switch (event->type) { +- case YAML_DOCUMENT_START_EVENT: +- s->state = STATE_DOCUMENT; +- break; +- case YAML_STREAM_END_EVENT: +- s->state = STATE_STOP; +- break; +- default: +- s->error = YAML_UNEXPECTED_STATE; +- s->event_type = event->type; +- return FAILURE; +- } +- break; +- +- case STATE_DOCUMENT: +- switch (event->type) { +- case YAML_MAPPING_START_EVENT: +- s->state = STATE_SECTION; +- break; +- case YAML_DOCUMENT_END_EVENT: +- s->state = STATE_STREAM; +- break; +- default: +- s->error = YAML_UNEXPECTED_STATE; +- s->event_type = event->type; +- return FAILURE; +- } +- break; +- +- case STATE_SECTION: +- switch (event->type) { +- case YAML_SCALAR_EVENT: +- value = (char *)event->data.scalar.value; +- newctx = calloc (s->nelem + 1, sizeof(*newctx)); +- for (int i = 0; i < s->nelem; i++) { +- newctx[i] = s->ctxsets[i]; +- } +- if (s->ctxsets) free(s->ctxsets); +- s->ctxsets = newctx; +- s->ctx = &newctx[s->nelem]; +- s->ctx->name = strdup(value); +- s->nelem++; +- s->state = STATE_NAMESPACE; +- break; +- case YAML_MAPPING_END_EVENT: +- s->state = STATE_DOCUMENT; +- break; +- case YAML_DOCUMENT_END_EVENT: +- s->state = STATE_STREAM; +- break; +- default: +- s->error = YAML_UNEXPECTED_STATE; +- s->event_type = event->type; +- return FAILURE; +- } +- break; +- +- case STATE_NAMESPACE: +- switch (event->type) { +- case YAML_MAPPING_START_EVENT: +- s->state = STATE_NAMESPACE_FIELDS; +- break; +- default: +- s->error = YAML_UNEXPECTED_STATE; +- s->event_type = event->type; +- return FAILURE; +- } +- break; +- +- case STATE_NAMESPACE_FIELDS: +- switch (event->type) { +- case YAML_SCALAR_EVENT: +- value = (char *)event->data.scalar.value; +- if (!strcmp(value, "size")) { +- s->state = STATE_NSIZE; +- } else if (!strcmp(value, "lockfile")) { +- s->state = STATE_NLOCKFILE; +- } else if (!strcmp(value, "devices")) { +- s->state = STATE_DEVVALUES; +- s->cdev = 0; +- } else if (!strcmp(value, "writelist")) { +- s->state = STATE_WRITELIST; +- } else { +- s->error = YAML_UNEXPECTED_KEY; +- s->event_type = event->type; +- return FAILURE; +- } +- break; +- case YAML_MAPPING_END_EVENT: +- s->state = STATE_SECTION; +- break; +- default: +- s->error = YAML_UNEXPECTED_STATE; +- s->event_type = event->type; +- return FAILURE; +- } +- break; +- +- case STATE_NSIZE: +- switch (event->type) { +- case YAML_SCALAR_EVENT: +- value = (char *)event->data.scalar.value; +- errno = 0; +- s->ctx->size = strtoull(value, NULL, 0); +- s->state = STATE_NAMESPACE_FIELDS; +- break; +- default: +- s->error = YAML_UNEXPECTED_STATE; +- s->event_type = event->type; +- return FAILURE; +- } +- break; +- +- case STATE_NLOCKFILE: +- switch (event->type) { +- case YAML_SCALAR_EVENT: +- value = (char *)event->data.scalar.value; +- s->ctx->lockfile = strdup(value); +- s->state = STATE_NAMESPACE_FIELDS; +- break; +- default: +- s->error = YAML_UNEXPECTED_STATE; +- s->event_type = event->type; +- return FAILURE; +- } +- break; +- +- case STATE_DEVVALUES: +- switch (event->type) { +- case YAML_MAPPING_START_EVENT: +- case YAML_SEQUENCE_START_EVENT: +- break; +- case YAML_MAPPING_END_EVENT: +- dev = &s->ctx->envdevs[s->cdev]; +- if (check_env_device(dev) < 0) { +- s->error = YAML_BAD_DEVICE; +- s->event_type = event->type; +- return FAILURE; +- } +- s->cdev++; +- break; +- case YAML_SEQUENCE_END_EVENT: +- s->state = STATE_NAMESPACE_FIELDS; +- break; +- case YAML_SCALAR_EVENT: +- value = (char *)event->data.scalar.value; +- if (s->cdev) +- s->ctx->redundant = true; +- if (!strcmp(value, "path")) { +- s->state = STATE_NPATH; +- } else if (!strcmp(value, "offset")) { +- s->state = STATE_NOFFSET; +- } else if (!strcmp(value, "sectorsize")) { +- s->state = STATE_NSECTORSIZE; +- } else if (!strcmp(value, "disablelock")) { +- s->state = STATE_NUNLOCK; +- } else { +- s->error = YAML_UNEXPECTED_KEY; +- s->event_type = event->type; +- return FAILURE; +- } +- break; +- default: +- s->error = YAML_UNEXPECTED_STATE; +- s->event_type = event->type; +- return FAILURE; +- } +- break; +- +- case STATE_WRITELIST: +- switch (event->type) { +- +- char *varflag, *name; +- struct var_entry *entry; +- +- case YAML_MAPPING_START_EVENT: +- case YAML_SEQUENCE_START_EVENT: +- break; +- case YAML_MAPPING_END_EVENT: +- break; +- case YAML_SEQUENCE_END_EVENT: +- s->state = STATE_NAMESPACE_FIELDS; +- break; +- case YAML_SCALAR_EVENT: +- value = (char *)event->data.scalar.value; +- +- /* +- * Format is name:flags, split it into two values +- */ +- varflag = strchr(value, ':'); +- if (!varflag || varflag > value + (strlen(value) - 1)) { +- s->error = YAML_BAD_VARLIST; +- s->event_type = event->type; +- return FAILURE; +- } +- *varflag++ = '\0'; +- +- /* +- * Check there is not yet an entry for this variable +- */ +- LIST_FOREACH(entry, &s->ctx->writevarlist, next) { +- if (strcmp(entry->name, value) == 0) { +- s->error = YAML_DUPLICATE_VARLIST; +- s->event_type = event->type; +- return FAILURE; +- } +- } +- +- /* +- * Insert variable with its configuration into the list +- * of modifiable vars +- */ +- entry = create_var_entry(value); +- if (!entry) { +- s->error = YAML_OOM; +- s->event_type = event->type; +- return FAILURE; +- } +- set_var_access_type(entry, varflag); +- LIST_INSERT_HEAD(&s->ctx->writevarlist, entry, next); +- +-#if !defined(NDEBUG) +- fprintf(stdout, "Writelist: %s flags %s\n", value, varflag); +-#endif +- break; +- default: +- s->error = YAML_UNEXPECTED_STATE; +- s->event_type = event->type; +- return FAILURE; +- } +- break; +- +- case STATE_NPATH: +- switch (event->type) { +- case YAML_SCALAR_EVENT: +- dev = &s->ctx->envdevs[s->cdev]; +- value = (char *)event->data.scalar.value; +- if (normalize_device_path(value, dev) < 0) { +- s->error = YAML_BAD_DEVNAME; +- s->event_type = event->type; +- return FAILURE; +- } +- dev->envsize = s->ctx->size; +- s->state = STATE_DEVVALUES; +- break; +- default: +- s->error = YAML_UNEXPECTED_STATE; +- s->event_type = event->type; +- return FAILURE; +- } +- break; +- +- case STATE_NOFFSET: +- switch (event->type) { +- case YAML_SCALAR_EVENT: +- dev = &s->ctx->envdevs[s->cdev]; +- value = (char *)event->data.scalar.value; +- dev->offset = strtoull(value, NULL, 0); +- s->state = STATE_DEVVALUES; +- break; +- default: +- s->error = YAML_UNEXPECTED_STATE; +- s->event_type = event->type; +- return FAILURE; +- } +- break; +- +- case STATE_NSECTORSIZE: +- switch (event->type) { +- case YAML_SCALAR_EVENT: +- dev = &s->ctx->envdevs[s->cdev]; +- value = (char *)event->data.scalar.value; +- dev->sectorsize = strtoull(value, NULL, 0); +- s->state = STATE_DEVVALUES; +- break; +- default: +- s->error = YAML_UNEXPECTED_STATE; +- s->event_type = event->type; +- return FAILURE; +- } +- break; +- +- case STATE_NUNLOCK: +- switch (event->type) { +- case YAML_SCALAR_EVENT: +- dev = &s->ctx->envdevs[s->cdev]; +- value = (char *)event->data.scalar.value; +- if (!strcmp(value, "yes")) +- dev->disable_mtd_lock = 1; +- s->state = STATE_DEVVALUES; +- break; +- default: +- s->error = YAML_UNEXPECTED_STATE; +- s->event_type = event->type; +- return FAILURE; +- } +- break; +- +- case STATE_STOP: +- break; +- } +- return SUCCESS; +-} +- +-int parse_yaml_config(struct uboot_ctx **ctxlist, FILE *fp) +-{ +- yaml_parser_t parser; +- yaml_event_t event; +- enum yaml_status status; +- struct parser_state state; +- struct uboot_ctx *ctx; +- +- if (!yaml_parser_initialize(&parser)) +- return -ENOMEM; +- +- /* Set input file */ +- yaml_parser_set_input_file(&parser, fp); +- memset(&state, 0, sizeof(state)); +- state.state = STATE_START; +- do { +- if (!yaml_parser_parse(&parser, &event)) { +- status = FAILURE; +- goto cleanup; +- } +- status = consume_event(&state, &event); +- yaml_event_delete(&event); +- if (status == FAILURE) { +- goto cleanup; +- } +- } while (state.state != STATE_STOP); +- +- state.ctxsets[0].nelem = state.nelem; +- +- for (int i = 0; i < state.nelem; i++) { +- ctx = &state.ctxsets[i]; +- ctx->ctxlist = &state.ctxsets[0]; +- if (ctx->redundant && !check_compatible_devices(ctx)) { +- status = FAILURE; +- break; +- } +- } +- +- +-cleanup: +- yaml_parser_delete(&parser); +- if (status == FAILURE) { +- if (state.ctxsets) free (state.ctxsets); +- state.ctxsets = NULL; +- } +- *ctxlist = state.ctxsets; +- return status; +-} +- + #define LINE_LENGTH 2048 + int libuboot_load_file(struct uboot_ctx *ctx, const char *filename) + { diff --git a/patches/libubootenv-0.3.6/0002-extended_config-fix-segfault-on-empty-config.patch b/patches/libubootenv-0.3.6/0002-extended_config-fix-segfault-on-empty-config.patch new file mode 100644 index 000000000..a4574ff5f --- /dev/null +++ b/patches/libubootenv-0.3.6/0002-extended_config-fix-segfault-on-empty-config.patch @@ -0,0 +1,40 @@ +From: James Hilliard +Date: Thu, 24 Apr 2025 19:53:01 -0600 +Subject: [PATCH] extended_config: fix segfault on empty config + +We need to validate that we have nelem and bail out if we don't. + +Fixes: +==245== Invalid write of size 4 +==245== at 0x4849E0C: parse_yaml_config (uboot_env.c:1390) +==245== by 0x484A717: libuboot_read_config_ext (uboot_env.c:1484) +==245== by 0x13F253: bootloader_initialize.constprop.0 (uboot.c:45) +==245== by 0x13F457: do_env_set (uboot.c:72) +==245== by 0x12B057: update_transaction_state (stream_interface.c:139) +==245== by 0x12C367: extract_files (stream_interface.c:297) +==245== by 0x12C367: network_initializer (stream_interface.c:658) +==245== by 0x4EA89AF: ??? (in /usr/lib/libc.so.6) +==245== by 0x4F0989B: ??? (in /usr/lib/libc.so.6) +==245== Address 0x2e8 is not stack'd, malloc'd or (recently) free'd + +Signed-off-by: James Hilliard +--- + src/extended_config.c | 5 +++++ + 1 file changed, 5 insertions(+) + +diff --git a/src/extended_config.c b/src/extended_config.c +index ec814f484efd..45df3ec7a7ca 100644 +--- a/src/extended_config.c ++++ b/src/extended_config.c +@@ -428,6 +428,11 @@ int parse_yaml_config(struct uboot_ctx **ctxlist, FILE *fp) + } + } while (state.state != STATE_STOP); + ++ if (state.nelem == 0) { ++ status = FAILURE; ++ goto cleanup; ++ } ++ + state.ctxsets[0].nelem = state.nelem; + + for (int i = 0; i < state.nelem; i++) { diff --git a/patches/libubootenv-0.3.6/0003-BUG-Fix-warning-when-copying-UBI-volume.patch b/patches/libubootenv-0.3.6/0003-BUG-Fix-warning-when-copying-UBI-volume.patch new file mode 100644 index 000000000..1ead89548 --- /dev/null +++ b/patches/libubootenv-0.3.6/0003-BUG-Fix-warning-when-copying-UBI-volume.patch @@ -0,0 +1,35 @@ +From: Stefano Babic +Date: Tue, 29 Apr 2025 13:49:15 +0200 +Subject: [PATCH] BUG: Fix warning when copying UBI volume +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Fix: + +/home/stefano/SWUpdate/libubootenv/src/uboot_mtd.c: In function ‘libubootenv_ubi_update_name’: +/home/stefano/SWUpdate/libubootenv/src/uboot_mtd.c:283:17: warning: ‘memset’ writing 256 bytes into a region of size 236 overflows the destination [-Wstringop-overflow=] + 283 | memset(volume, 0, DEVNAME_MAX_LENGTH); + | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/home/stefano/SWUpdate/libubootenv/src/uboot_mtd.c:223:14: note: destination object ‘volume’ of size 236 + +This can lead to overwrite the buffer for the volumes. + +Signed-off-by: Stefano Babic +--- + src/uboot_mtd.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/uboot_mtd.c b/src/uboot_mtd.c +index 81376e4ddc09..24e817d88944 100644 +--- a/src/uboot_mtd.c ++++ b/src/uboot_mtd.c +@@ -280,7 +280,7 @@ int libubootenv_ubi_update_name(struct uboot_flash_env *dev) + memset(device, 0, DEVNAME_MAX_LENGTH); + memcpy(device, dev->devname, sep - dev->devname); + +- memset(volume, 0, DEVNAME_MAX_LENGTH); ++ memset(volume, 0, VOLNAME_MAX_LENGTH); + sscanf(sep + 1, "%s", &volume[0]); + + dev_id = ubi_get_dev_id(device); diff --git a/patches/libubootenv-0.3.6/0004-libubootenv-fix-segfault-due-to-uninitialized-pointe.patch b/patches/libubootenv-0.3.6/0004-libubootenv-fix-segfault-due-to-uninitialized-pointe.patch new file mode 100644 index 000000000..e7ae92fb8 --- /dev/null +++ b/patches/libubootenv-0.3.6/0004-libubootenv-fix-segfault-due-to-uninitialized-pointe.patch @@ -0,0 +1,32 @@ +From: Mohamed-nour Toumi +Date: Mon, 28 Jul 2025 22:25:35 +0200 +Subject: [PATCH] libubootenv: fix segfault due to uninitialized pointer in + config parser + +Issue: The issue was introduced in commit c478e8d9, which replaced the use of %ms in sscanf() with a calloc()-based workaround for platforms where %ms is not supported (e.g., FreeBSD). +However, this change inadvertently introduced a logic flaw: it uses calloc to emulate %ms% in sscanf() but also added 2 free instructions which could lead to free non-allocated memory on tmp when sscanf() +is used to perform the dynamic allocation. + +Fix: Ensure `tmp` is initialized to NULL before each call to sscanf with `%ms` +in `libuboot_read_config_ext()`. This prevents `free(tmp)` from crashing +when sscanf fails to allocate memory (e.g., due to malformed config lines). + +Fixes segmentation fault observed when running swupdate with a ubootenv config file. + +Signed-off-by: Mohamed-nour Toumi +--- + src/uboot_env.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/src/uboot_env.c b/src/uboot_env.c +index d8b93dac7479..9641800f4bdc 100644 +--- a/src/uboot_env.c ++++ b/src/uboot_env.c +@@ -871,6 +871,7 @@ int libuboot_read_config_ext(struct uboot_ctx **ctxlist, const char *config) + tmp, + #else + (void)len; ++ tmp = NULL; + ret = sscanf(line, "%ms %lli %zx %zx %lx %d", + &tmp, + #endif diff --git a/patches/libubootenv-0.3.6/0005-extended_config.c-Catch-NULL-pointer-for-calloc.patch b/patches/libubootenv-0.3.6/0005-extended_config.c-Catch-NULL-pointer-for-calloc.patch new file mode 100644 index 000000000..9c89141c7 --- /dev/null +++ b/patches/libubootenv-0.3.6/0005-extended_config.c-Catch-NULL-pointer-for-calloc.patch @@ -0,0 +1,30 @@ +From: Steffen Kothe +Date: Sun, 31 Aug 2025 19:07:56 +0000 +Subject: [PATCH] extended_config.c: Catch NULL pointer for calloc + +calloc is not guaranteed to return a valid pointer to a free memory +area. + +Hence check for possible NULL return and fail immediately. + +Addresses possible CWE-690. + +Signed-off-by: Steffen Kothe +Acked-by: Stefano Babic +--- + src/extended_config.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/src/extended_config.c b/src/extended_config.c +index 45df3ec7a7ca..c3782e1fba94 100644 +--- a/src/extended_config.c ++++ b/src/extended_config.c +@@ -131,6 +131,8 @@ static int consume_event(struct parser_state *s, yaml_event_t *event) + case YAML_SCALAR_EVENT: + value = (char *)event->data.scalar.value; + newctx = calloc (s->nelem + 1, sizeof(*newctx)); ++ if (newctx == NULL) ++ return FAILURE; + for (int i = 0; i < s->nelem; i++) { + newctx[i] = s->ctxsets[i]; + } diff --git a/patches/libubootenv-0.3.6/series b/patches/libubootenv-0.3.6/series new file mode 100644 index 000000000..b9356ee68 --- /dev/null +++ b/patches/libubootenv-0.3.6/series @@ -0,0 +1,8 @@ +# generated by git-ptx-patches +#tag:base --start-number 1 +0001-Make-libyaml-optional.patch +0002-extended_config-fix-segfault-on-empty-config.patch +0003-BUG-Fix-warning-when-copying-UBI-volume.patch +0004-libubootenv-fix-segfault-due-to-uninitialized-pointe.patch +0005-extended_config.c-Catch-NULL-pointer-for-calloc.patch +# 8a759d09e861f42f9d568274b3aeb30b - git-ptx-patches magic diff --git a/rules/libubootenv.make b/rules/libubootenv.make index 74bfaaf19..d6753cd87 100644 --- a/rules/libubootenv.make +++ b/rules/libubootenv.make @@ -34,7 +34,8 @@ LIBUBOOTENV_LICENSE_FILES := \ LIBUBOOTENV_CONF_TOOL := cmake LIBUBOOTENV_CONF_OPT := \ $(CROSS_CMAKE_USR) \ - -DBUILD_DOC=OFF + -DBUILD_DOC=OFF \ + -DNO_YML_SUPPORT=OFF # ---------------------------------------------------------------------------- # Target-Install -- 2.39.5