Merge Official Source

Signed-off-by: Tianling Shen <cnsztl@immortalwrt.org>
This commit is contained in:
Tianling Shen
2026-05-16 16:35:14 +08:00
14 changed files with 1217 additions and 21 deletions
+22 -8
View File
@@ -13,7 +13,7 @@ include $(INCLUDE_DIR)/host-build.mk
define U-Boot/Default
BUILD_TARGET:=airoha
FIP_COMPRESS:=1
LZMA_COMPRESS:=1
endef
define U-Boot/an7581_rfb
@@ -36,9 +36,18 @@ define U-Boot/an7583_rfb
BL31_IMAGE:=an7583-bl31.bin
endef
define U-Boot/an7581_gemtek_w1700k
NAME:=Gemtek W1700K (UBI)
UBOOT_CONFIG:=an7581_w1700k
BUILD_DEVICES:=gemtek_w1700k-ubi
BUILD_SUBTARGET:=an7581
UBOOT_IMAGE:=u-boot.bin
endef
UBOOT_TARGETS := \
an7581_rfb \
an7583_rfb
an7583_rfb \
an7581_gemtek_w1700k
UBOOT_CUSTOMIZE_CONFIG := \
--disable TOOLS_KWBIMAGE \
@@ -54,15 +63,12 @@ define Build/fip-image-bl2
endef
define Build/fip-image
$(if $(FIP_COMPRESS), $(STAGING_DIR_HOST)/bin/lzma e \
$(PKG_BUILD_DIR)/u-boot.bin \
$(PKG_BUILD_DIR)/u-boot.bin.lzma)
$(if $(FIP_COMPRESS), $(STAGING_DIR_HOST)/bin/lzma e \
$(if $(LZMA_COMPRESS), $(STAGING_DIR_HOST)/bin/lzma e \
files/$(BL31_IMAGE) \
$(PKG_BUILD_DIR)/bl31.bin.lzma)
$(STAGING_DIR_HOST)/bin/fiptool create \
--soc-fw $(PKG_BUILD_DIR)/bl31.bin$(if $(FIP_COMPRESS),.lzma) \
--nt-fw $(PKG_BUILD_DIR)/u-boot.bin$(if $(FIP_COMPRESS),.lzma) \
--soc-fw $(PKG_BUILD_DIR)/bl31.bin$(if $(LZMA_COMPRESS),.lzma) \
--nt-fw $(PKG_BUILD_DIR)/u-boot.bin$(if $(LZMA_COMPRESS),.lzma) \
$(PKG_BUILD_DIR)/u-boot.fip
endef
@@ -73,6 +79,9 @@ endef
define Build/Compile
$(call Build/Compile/U-Boot)
$(if $(LZMA_COMPRESS), $(STAGING_DIR_HOST)/bin/lzma e \
$(PKG_BUILD_DIR)/u-boot.bin \
$(PKG_BUILD_DIR)/u-boot.bin.lzma)
ifeq ($(UBOOT_IMAGE),u-boot.fip)
$(call Build/fip-image-bl2)
$(call Build/fip-image)
@@ -88,6 +97,11 @@ define Build/InstallDev
ifeq ($(UBOOT_IMAGE),u-boot.fip)
$(INSTALL_DATA) $(PKG_BUILD_DIR)/bl2.fip $(STAGING_DIR_IMAGE)/$(BUILD_VARIANT)-bl2.fip
$(INSTALL_DATA) $(PKG_BUILD_DIR)/u-boot.fip $(STAGING_DIR_IMAGE)/$(BUILD_VARIANT)-bl31-u-boot.fip
else
$(INSTALL_DATA) $(PKG_BUILD_DIR)/u-boot.bin$(if $(LZMA_COMPRESS),.lzma) \
$(STAGING_DIR_IMAGE)/$(BUILD_VARIANT)-u-boot.bin$(if $(LZMA_COMPRESS),.lzma)
$(INSTALL_DATA) $(PKG_BUILD_DIR)/u-boot.dtb \
$(STAGING_DIR_IMAGE)/$(BUILD_VARIANT)-u-boot.dtb
endif
endef
@@ -0,0 +1,52 @@
From 6ecd1312f85bdd0a9be8cc1cdada58c83f68ad99 Mon Sep 17 00:00:00 2001
From: Kenneth Kasilag <kenneth@kasilag.me>
Date: Fri, 6 Mar 2026 06:26:03 +0000
Subject: [PATCH] uboot-airoha: add ethernet node for an7581 dtsi
The ethernet driver (airoha_eth) supports an7581 however it is
not declared in the dtsi. Add the ethernet node for the built in
airoha ethernet controller.
Signed-off-by: Kenneth Kasilag <kenneth@kasilag.me>
---
dts/upstream/src/arm64/airoha/en7581.dtsi | 36 +++++++++++++++++++
1 file changed, 36 insertions(+)
diff --git a/dts/upstream/src/arm64/airoha/en7581.dtsi b/dts/upstream/src/arm64/airoha/en7581.dtsi
index 69265e4a1fea97..3b319e7b72fb57 100644
--- a/dts/upstream/src/arm64/airoha/en7581.dtsi
+++ b/dts/upstream/src/arm64/airoha/en7581.dtsi
@@ -346,5 +346,33 @@
status = "disabled";
};
+
+ eth: ethernet@1fb50000 {
+ compatible = "airoha,en7581-eth";
+ reg = <0 0x1fb50000 0 0x2600>,
+ <0 0x1fb54000 0 0x2000>,
+ <0 0x1fb56000 0 0x2000>;
+ reg-names = "fe", "qdma0", "qdma1";
+
+ resets = <&scuclk EN7581_FE_RST>,
+ <&scuclk EN7581_FE_PDMA_RST>,
+ <&scuclk EN7581_FE_QDMA_RST>,
+ <&scuclk EN7581_DUAL_HSI0_MAC_RST>,
+ <&scuclk EN7581_DUAL_HSI1_MAC_RST>,
+ <&scuclk EN7581_HSI_MAC_RST>,
+ <&scuclk EN7581_XFP_MAC_RST>;
+ reset-names = "fe", "pdma", "qdma",
+ "hsi0-mac", "hsi1-mac",
+ "hsi-mac", "xfp-mac";
+
+ status = "disabled";
+ };
+
+ switch: switch@1fb58000 {
+ compatible = "airoha,en7581-switch";
+ reg = <0 0x1fb58000 0 0x8000>;
+
+ status = "disabled";
+ };
};
};
@@ -0,0 +1,21 @@
From 4fb890cd92dcf8985e76a544c949ad455784ed81 Mon Sep 17 00:00:00 2001
From: Dirk Buchwalder <buchwalder@posteo.de>
Date: Sun, 1 Mar 2026 13:48:42 +0100
Subject: [PATCH] uboot-airoha: add a label to the spi node
Signed-off-by: Dirk Buchwalder <buchwalder@posteo.de>
---
dts/upstream/src/arm64/airoha/en7581.dtsi | 2 -+
1 file changed, 1 insertion(+), 1 deletion(-)
--- a/dts/upstream/src/arm64/airoha/en7581.dtsi
+++ b/dts/upstream/src/arm64/airoha/en7581.dtsi
@@ -150,7 +150,7 @@
interrupts = <GIC_PPI 9 IRQ_TYPE_LEVEL_LOW>;
};
- spi@1fa10000 {
+ snfi: spi@1fa10000 {
compatible = "airoha,en7581-snand";
reg = <0x0 0x1fa10000 0x0 0x140>,
<0x0 0x1fa11000 0x0 0x160>;
@@ -0,0 +1,318 @@
From d6402eadaa56494b382e389d739b9f80d6883788 Mon Sep 17 00:00:00 2001
From: Kenneth Kasilag <kenneth@kasilag.me>
Date: Sat, 21 Feb 2026 21:49:46 +0000
Subject: [PATCH] an7581: add uboot chainloader for w1700k
Due to issues surrounding the implementation of the vendor BMT/BBT
on Airoha, upstream ATF + uboot has switched to UBI flash partitions.
However, some devices shipped on this platform are bootloader locked,
and thus it is impossible to replace ATF + uboot.
During testing for the Gemtek W1700K (#17869), sysupgrades from Linux
(which is unaware of the underlying BMT/BBT) would occasionally write
data into blocks which were remapped by the vendor uboot when it was
read on the following reboot, causing a soft brick.
An acceptable workaround [1],[2] was discussed where an intermediate
uboot would be written by the vendor uboot (which is aware of Airoha
BMT/BBT). This chainloader would then ignore the regions of flash
used by the vendor uboot, and store all relevant data inside of UBI.
UBI would then be used to handle bad block management. As the vendor
ATF + uboot do not read or interact with the UBI region, we would avoid
unwanted remaps from BMT/BBT.
This commit introduces support for building such a chainloader, by
packaging u-boot and DTS into a FIT image; to be flashed like a kernel.
Configuration for the Gemtek W1700K is provided as an example of how the
chainloader is used.
[1] https://github.com/openwrt/openwrt/pull/17869#discussion_r2836066746
[2] https://github.com/openwrt/openwrt/pull/17869#discussion_r2838395671
Signed-off-by: Kenneth Kasilag <kenneth@kasilag.me>
---
configs/an7581_w1700k_defconfig | 107 ++++++++++
defenvs/an7581_w1700k_env | 21 ++
dts/upstream/src/arm64/airoha/an7581-w1700k-ubi.dts | 87 +++++
3 files changed, 215 insertions(+), 0 deletions(-)
create mode 100644 configs/an7581_w1700k_defconfig
create mode 100644 defenvs/an7581_w1700k_env
create mode 100644 dts/upstream/src/arm64/airoha/an7581-w1700k-ubi.dts
--- /dev/null
+++ b/configs/an7581_w1700k_defconfig
@@ -0,0 +1,107 @@
+CONFIG_ARM=y
+CONFIG_ARCH_AIROHA=y
+CONFIG_AUTOBOOT_KEYED=y
+CONFIG_AUTOBOOT_MENU_SHOW=y
+CONFIG_POSITION_INDEPENDENT=y
+CONFIG_TARGET_AN7581=y
+CONFIG_TEXT_BASE=0x81E00000
+CONFIG_SYS_MALLOC_F_LEN=0x4000
+CONFIG_NR_DRAM_BANKS=1
+CONFIG_ENV_SIZE=0x4000
+CONFIG_ENV_OFFSET=0x7c000
+CONFIG_DM_GPIO=y
+CONFIG_DEFAULT_DEVICE_TREE="airoha/an7581-w1700k-ubi"
+CONFIG_SYS_LOAD_ADDR=0x81800000
+CONFIG_BUILD_TARGET="u-boot.bin"
+# CONFIG_EFI_LOADER is not set
+CONFIG_FIT=y
+CONFIG_FIT_VERBOSE=y
+CONFIG_BOOTDELAY=30
+CONFIG_DEFAULT_FDT_FILE="an7581-w1700k-ubi"
+CONFIG_SYS_PBSIZE=1049
+CONFIG_SYS_CONSOLE_IS_IN_ENV=y
+# CONFIG_DISPLAY_BOARDINFO is not set
+CONFIG_HUSH_PARSER=y
+CONFIG_SYS_PROMPT="U-Boot> "
+CONFIG_SYS_MAXARGS=8
+CONFIG_CMD_BOOTZ=y
+CONFIG_CMD_BOOTMENU=y
+# CONFIG_CMD_ELF is not set
+# CONFIG_CMD_XIMG is not set
+CONFIG_CMD_BIND=y
+CONFIG_CMD_GPIO=y
+CONFIG_CMD_MMC=y
+CONFIG_CMD_MTD=y
+CONFIG_CMD_SF_TEST=y
+CONFIG_CMD_SPI=y
+# CONFIG_CMD_SETEXPR is not set
+CONFIG_CMD_PING=y
+CONFIG_CMD_EXT4=y
+CONFIG_CMD_FAT=y
+CONFIG_CMD_FS_GENERIC=y
+CONFIG_CMD_MTDPARTS=y
+CONFIG_CMD_LOG=y
+CONFIG_OF_UPSTREAM=y
+CONFIG_ENV_OVERWRITE=y
+CONFIG_ENV_IS_IN_MMC=y
+# CONFIG_ENV_IS_IN_MTD is not set
+CONFIG_ENV_RELOC_GD_ENV_ADDR=y
+CONFIG_ENV_VARS_UBOOT_RUNTIME_CONFIG=y
+CONFIG_NET_RANDOM_ETHADDR=y
+CONFIG_SYS_RX_ETH_BUFFER=8
+CONFIG_REGMAP=y
+CONFIG_SYSCON=y
+CONFIG_CLK=y
+CONFIG_DMA=y
+CONFIG_LED=y
+CONFIG_LED_GPIO=y
+CONFIG_MMC_HS200_SUPPORT=y
+CONFIG_MMC_MTK=y
+CONFIG_MTD=y
+CONFIG_DM_MTD=y
+CONFIG_MTD_SPI_NAND=y
+CONFIG_DM_SPI_FLASH=y
+CONFIG_SPI_FLASH_EON=y
+CONFIG_SPI_FLASH_GIGADEVICE=y
+CONFIG_SPI_FLASH_ISSI=y
+CONFIG_SPI_FLASH_MACRONIX=y
+CONFIG_SPI_FLASH_SPANSION=y
+CONFIG_SPI_FLASH_STMICRO=y
+CONFIG_SPI_FLASH_WINBOND=y
+CONFIG_SPI_FLASH_MTD=y
+CONFIG_AIROHA_ETH=y
+CONFIG_DM_MDIO=y
+CONFIG_CMD_MII=y
+CONFIG_CMD_MDIO=y
+CONFIG_PHY=y
+CONFIG_PINCTRL=y
+CONFIG_PINCONF=y
+CONFIG_POWER_DOMAIN=y
+CONFIG_DM_REGULATOR=y
+CONFIG_DM_REGULATOR_FIXED=y
+CONFIG_RAM=y
+CONFIG_DM_SERIAL=y
+CONFIG_SYS_NS16550=y
+CONFIG_SPI=y
+CONFIG_DM_SPI=y
+CONFIG_AIROHA_SNFI_SPI=y
+CONFIG_CMD_UBI=y
+# CONFIG_CMD_UBI_RENAME is not set
+CONFIG_CMD_UBIFS=y
+CONFIG_ENV_IS_IN_UBI=y
+CONFIG_ENV_REDUNDANT=y
+CONFIG_ENV_UBI_PART="ubi"
+CONFIG_ENV_UBI_VOLUME="ubootenv"
+CONFIG_ENV_UBI_VOLUME_REDUND="ubootenv2"
+CONFIG_ENV_UBI_VID_OFFSET=0
+CONFIG_MTD_UBI=y
+CONFIG_MTD_UBI_MODULE=y
+CONFIG_MTD_UBI_WL_THRESHOLD=4096
+CONFIG_MTD_UBI_BEB_LIMIT=20
+# CONFIG_MTD_UBI_FASTMAP is not set
+CONFIG_UBI_BLOCK=y
+# CONFIG_UBIFS_SILENCE_MSG is not set
+# CONFIG_UBIFS_SILENCE_DEBUG_DUMP is not set
+CONFIG_ENV_USE_DEFAULT_ENV_TEXT_FILE=y
+CONFIG_ENV_DEFAULT_ENV_TEXT_FILE="defenvs/an7581_w1700k_env"
+CONFIG_SHA512=y
--- /dev/null
+++ b/defenvs/an7581_w1700k_env
@@ -0,0 +1,27 @@
+loadaddr=0x81800000
+initaddr=0x89000000
+ipaddr=192.168.1.1
+serverip=192.168.1.10
+bootfile=openwrt-airoha-an7581-gemtek_w1700k-ubi-initramfs-recovery.itb
+installfile=openwrt-airoha-an7581-gemtek_w1700k-ubi-initramfs-installer.itb
+bootled_pwr=status:red
+bootled_rec=status:blue
+boot_ubi=ubi part ubi && run boot_production ; run boot_recovery
+boot_production=led $bootled_pwr on ; run ubi_read_production && bootm $loadaddr#$bootconf ; led $bootled_pwr off
+boot_recovery=led $bootled_rec on ; run ubi_read_recovery ; bootm $initaddr#$bootconf ; led $bootled_rec off
+boot_tftp=tftpboot $initaddr $bootfile && bootm $initaddr#$bootconf
+boot_installer=tftpboot $initaddr $installfile && bootm $initaddr#$bootconf
+bootconf=config-1
+bootcmd=boot_ubi
+bootdelay=3
+bootmenu_confirm_return=askenv - Press ENTER to return to menu ; bootmenu 60
+bootmenu_default=0
+bootmenu_delay=3
+bootmenu_title= 34m( ( ( mOpenWrt34m ) ) )
+bootmenu_0=Run default boot command.=run boot_production
+bootmenu_1=Boot system via TFTP.=run boot_tftp ; run bootmenu_confirm_return
+bootmenu_2=Boot recovery system from flash.=run boot_recovery ; run bootmenu_confirm_return
+bootmenu_3=Boot installer via TFTP.=run boot_installer ; run bootmenu_confirm_return
+bootmenu_4=Reboot.=reset
+ubi_read_production=ubi read $loadaddr fit && iminfo $loadaddr
+ubi_read_recovery=ubi check recovery && ubi read $initaddr recovery
--- /dev/null
+++ b/dts/upstream/src/arm64/airoha/an7581-w1700k-ubi.dts
@@ -0,0 +1,131 @@
+// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+/dts-v1/;
+
+/* Bootloader installs ATF here */
+/memreserve/ 0x80000000 0x200000;
+
+#include <dt-bindings/input/linux-event-codes.h>
+#include <dt-bindings/gpio/gpio.h>
+#include "en7581.dtsi"
+
+/ {
+ model = "Gemtek W1700K (UBI)";
+ compatible = "gemtek,w1700k-ubi", "airoha,an7581", "airoha,en7581";
+
+ aliases {
+ serial0 = &uart1;
+ };
+
+ chosen {
+ stdout-path = "serial0:115200n8";
+ linux,usable-memory-range = <0x0 0x80200000 0x0 0x7fe00000>;
+ };
+
+ memory@80000000 {
+ device_type = "memory";
+ reg = <0x0 0x80000000 0x2 0x00000000>;
+ };
+
+ gpio-keys {
+ compatible = "gpio-keys";
+
+ key-restart {
+ label = "reset";
+ gpios = <&en7581_pinctrl 0 GPIO_ACTIVE_LOW>;
+ linux,code = <KEY_RESTART>;
+ };
+ };
+
+ gpio-leds {
+ compatible = "gpio-leds";
+
+ led_status_green {
+ label = "status:green";
+ gpios = <&en7581_pinctrl 17 GPIO_ACTIVE_LOW>;
+ default-state = "off";
+ };
+
+ led_status_blue {
+ label = "status:blue";
+ gpios = <&en7581_pinctrl 19 GPIO_ACTIVE_LOW>;
+ default-state = "off";
+ };
+
+ led_status_red {
+ label = "status:red";
+ gpios = <&en7581_pinctrl 29 GPIO_ACTIVE_LOW>;
+ default-state = "off";
+ };
+
+ led_status_white {
+ label = "status:white";
+ gpios = <&en7581_pinctrl 20 GPIO_ACTIVE_LOW>;
+ default-state = "off";
+ };
+ };
+};
+
+&snfi {
+ status = "okay";
+};
+
+&spi_nand {
+ partitions {
+ compatible = "fixed-partitions";
+ #address-cells = <1>;
+ #size-cells = <1>;
+
+ vendor@0 {
+ label = "vendor";
+ reg = <0x00000000 0x00600000>;
+ read-only;
+ };
+
+ chainloader@600000 {
+ label = "chainloader";
+ reg = <0x00600000 0x00100000>;
+ read-only;
+ };
+
+ ubi@700000 {
+ label = "ubi";
+ reg = <0x00700000 0x1b700000>;
+ };
+
+ /* reserved for bad block table */
+ reserved_bmt@1be00000 {
+ label = "reserved_bmt";
+ reg = <0x1be00000 0x04200000>;
+ read-only;
+ };
+ };
+};
+
+&en7581_pinctrl {
+ gpio-ranges = <&en7581_pinctrl 0 13 47>;
+
+ pcie0_rst_pins: pcie0-rst-pins {
+ conf {
+ pins = "pcie_reset0";
+ drive-open-drain = <1>;
+ };
+ };
+};
+
+&pcie0 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pcie0_rst_pins>;
+ status = "okay";
+};
+
+&i2c0 {
+ status = "okay";
+};
+
+&eth {
+ status = "okay";
+};
+
+&switch {
+ status = "okay";
+};
+2
View File
@@ -237,6 +237,7 @@ CONFIG_MTD_SPLIT_FIT_FW=y
CONFIG_MTD_UBI=y
CONFIG_MTD_UBI_BEB_LIMIT=20
CONFIG_MTD_UBI_BLOCK=y
CONFIG_MTD_UBI_NVMEM=y
CONFIG_MTD_UBI_WL_THRESHOLD=4096
CONFIG_MTK_NET_PHYLIB=y
CONFIG_MUTEX_SPIN_ON_OWNER=y
@@ -396,6 +397,7 @@ CONFIG_TREE_RCU=y
CONFIG_TREE_SRCU=y
CONFIG_UBIFS_FS=y
# CONFIG_UNMAP_KERNEL_AT_EL0 is not set
CONFIG_UIMAGE_FIT_BLK=y
CONFIG_USB_SUPPORT=y
CONFIG_USER_STACKTRACE_SUPPORT=y
CONFIG_VDSO_GETRANDOM=y
-4
View File
@@ -1003,7 +1003,6 @@
gsw_phy1: ethernet-phy@9 {
compatible = "ethernet-phy-ieee802.3-c22";
reg = <9>;
interrupts = <1>;
phy-mode = "internal";
status = "disabled";
@@ -1026,7 +1025,6 @@
gsw_phy2: ethernet-phy@a {
compatible = "ethernet-phy-ieee802.3-c22";
reg = <10>;
interrupts = <2>;
phy-mode = "internal";
status = "disabled";
@@ -1049,7 +1047,6 @@
gsw_phy3: ethernet-phy@b {
compatible = "ethernet-phy-ieee802.3-c22";
reg = <11>;
interrupts = <3>;
phy-mode = "internal";
status = "disabled";
@@ -1072,7 +1069,6 @@
gsw_phy4: ethernet-phy@c {
compatible = "ethernet-phy-ieee802.3-c22";
reg = <12>;
interrupts = <4>;
phy-mode = "internal";
status = "disabled";
+27
View File
@@ -12,6 +12,33 @@ define Build/an7581-bl31-uboot
cat $(STAGING_DIR_IMAGE)/an7581_$1-bl31-u-boot.fip >> $@
endef
define Build/an7581-chainloader
$(INSTALL_DIR) $(KDIR)/chainload-fit-$(notdir $@)
@if [ -f "$(STAGING_DIR_IMAGE)/an7581_$1-u-boot.bin.lzma" ]; then \
KERNEL="$(STAGING_DIR_IMAGE)/an7581_$1-u-boot.bin.lzma"; \
COMP="lzma"; \
else \
KERNEL="$(STAGING_DIR_IMAGE)/an7581_$1-u-boot.bin"; \
COMP="none"; \
fi; \
$(TOPDIR)/scripts/mkits.sh \
-D $(DEVICE_NAME) \
-o $(KDIR)/chainload-fit-$(notdir $@)/u-boot.its \
-k $$KERNEL \
-C $$COMP \
-a 0x80200000 -e 0x80200000 \
-c conf-uboot \
-A arm64 -v u-boot \
-d $(STAGING_DIR_IMAGE)/an7581_$1-u-boot.dtb \
-s 0x82000000
PATH=$(LINUX_DIR)/scripts/dtc:$(PATH) \
$(STAGING_DIR_HOST)/bin/mkimage \
-D "-i $(KDIR)/chainload-fit-$(notdir $@)" \
-f $(KDIR)/chainload-fit-$(notdir $@)/u-boot.its \
$(STAGING_DIR_IMAGE)/an7581_$1-chainload-u-boot.itb
cat $(STAGING_DIR_IMAGE)/an7581_$1-chainload-u-boot.itb >> $@
endef
define Device/FitImageLzma
KERNEL_SUFFIX := -uImage.itb
KERNEL = kernel-bin | lzma | fit lzma $$(KDIR)/image-$$(DEVICE_DTS).dtb
@@ -0,0 +1,241 @@
From b61fcc3c7f95734e14741d8787fc9eb69b8437d4 Mon Sep 17 00:00:00 2001
From: Daniel Golle <daniel@makrotopia.org>
Date: Sat, 11 Apr 2026 22:47:15 +0100
Subject: [PATCH 1/2] PM / devfreq: mtk-cci: use devres for resource management
in probe
Convert mtk_ccifreq_probe() to use devm-managed resource cleanup
instead of manual goto-based error handling.
This pattern (devm_add_action_or_reset with a regulator disable
callback) is well-established in the kernel, used by drivers such as
ads7846, max6639 and others.
Signed-off-by: Daniel Golle <daniel@makrotopia.org>
---
drivers/devfreq/mtk-cci-devfreq.c | 150 +++++++++++++++---------------
1 file changed, 73 insertions(+), 77 deletions(-)
--- a/drivers/devfreq/mtk-cci-devfreq.c
+++ b/drivers/devfreq/mtk-cci-devfreq.c
@@ -246,6 +246,21 @@ static struct devfreq_dev_profile mtk_cc
.target = mtk_ccifreq_target,
};
+static void mtk_ccifreq_regulator_disable(void *data)
+{
+ regulator_disable(data);
+}
+
+static void mtk_ccifreq_clk_disable_unprepare(void *data)
+{
+ clk_disable_unprepare(data);
+}
+
+static void mtk_ccifreq_opp_of_remove_table(void *data)
+{
+ dev_pm_opp_of_remove_table(data);
+}
+
static int mtk_ccifreq_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
@@ -266,44 +281,47 @@ static int mtk_ccifreq_probe(struct plat
platform_set_drvdata(pdev, drv);
drv->cci_clk = devm_clk_get(dev, "cci");
- if (IS_ERR(drv->cci_clk)) {
- ret = PTR_ERR(drv->cci_clk);
- return dev_err_probe(dev, ret, "failed to get cci clk\n");
- }
+ if (IS_ERR(drv->cci_clk))
+ return dev_err_probe(dev, PTR_ERR(drv->cci_clk),
+ "failed to get cci clk\n");
drv->inter_clk = devm_clk_get(dev, "intermediate");
- if (IS_ERR(drv->inter_clk)) {
- ret = PTR_ERR(drv->inter_clk);
- return dev_err_probe(dev, ret,
+ if (IS_ERR(drv->inter_clk))
+ return dev_err_probe(dev, PTR_ERR(drv->inter_clk),
"failed to get intermediate clk\n");
- }
drv->proc_reg = devm_regulator_get_optional(dev, "proc");
- if (IS_ERR(drv->proc_reg)) {
- ret = PTR_ERR(drv->proc_reg);
- return dev_err_probe(dev, ret,
+ if (IS_ERR(drv->proc_reg))
+ return dev_err_probe(dev, PTR_ERR(drv->proc_reg),
"failed to get proc regulator\n");
- }
ret = regulator_enable(drv->proc_reg);
- if (ret) {
- dev_err(dev, "failed to enable proc regulator\n");
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "failed to enable proc regulator\n");
+
+ ret = devm_add_action_or_reset(dev, mtk_ccifreq_regulator_disable,
+ drv->proc_reg);
+ if (ret)
return ret;
- }
drv->sram_reg = devm_regulator_get_optional(dev, "sram");
if (IS_ERR(drv->sram_reg)) {
- ret = PTR_ERR(drv->sram_reg);
- if (ret == -EPROBE_DEFER)
- goto out_free_resources;
+ if (PTR_ERR(drv->sram_reg) == -EPROBE_DEFER)
+ return -EPROBE_DEFER;
drv->sram_reg = NULL;
} else {
ret = regulator_enable(drv->sram_reg);
- if (ret) {
- dev_err(dev, "failed to enable sram regulator\n");
- goto out_free_resources;
- }
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "failed to enable sram regulator\n");
+
+ ret = devm_add_action_or_reset(dev,
+ mtk_ccifreq_regulator_disable,
+ drv->sram_reg);
+ if (ret)
+ return ret;
}
/*
@@ -317,31 +335,36 @@ static int mtk_ccifreq_probe(struct plat
ret = clk_prepare_enable(drv->cci_clk);
if (ret)
- goto out_free_resources;
+ return ret;
+
+ ret = devm_add_action_or_reset(dev, mtk_ccifreq_clk_disable_unprepare,
+ drv->cci_clk);
+ if (ret)
+ return ret;
ret = dev_pm_opp_of_add_table(dev);
- if (ret) {
- dev_err(dev, "failed to add opp table: %d\n", ret);
- goto out_disable_cci_clk;
- }
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to add opp table\n");
+
+ ret = devm_add_action_or_reset(dev, mtk_ccifreq_opp_of_remove_table,
+ dev);
+ if (ret)
+ return ret;
rate = clk_get_rate(drv->inter_clk);
opp = dev_pm_opp_find_freq_ceil(dev, &rate);
- if (IS_ERR(opp)) {
- ret = PTR_ERR(opp);
- dev_err(dev, "failed to get intermediate opp: %d\n", ret);
- goto out_remove_opp_table;
- }
+ if (IS_ERR(opp))
+ return dev_err_probe(dev, PTR_ERR(opp),
+ "failed to get intermediate opp\n");
+
drv->inter_voltage = dev_pm_opp_get_voltage(opp);
dev_pm_opp_put(opp);
rate = U32_MAX;
opp = dev_pm_opp_find_freq_floor(drv->dev, &rate);
- if (IS_ERR(opp)) {
- dev_err(dev, "failed to get opp\n");
- ret = PTR_ERR(opp);
- goto out_remove_opp_table;
- }
+ if (IS_ERR(opp))
+ return dev_err_probe(dev, PTR_ERR(opp),
+ "failed to get opp\n");
opp_volt = dev_pm_opp_get_voltage(opp);
dev_pm_opp_put(opp);
@@ -349,63 +372,36 @@ static int mtk_ccifreq_probe(struct plat
if (ret) {
dev_err(dev, "failed to scale to highest voltage %lu in proc_reg\n",
opp_volt);
- goto out_remove_opp_table;
+ return ret;
}
passive_data = devm_kzalloc(dev, sizeof(*passive_data), GFP_KERNEL);
- if (!passive_data) {
- ret = -ENOMEM;
- goto out_remove_opp_table;
- }
+ if (!passive_data)
+ return -ENOMEM;
passive_data->parent_type = CPUFREQ_PARENT_DEV;
drv->devfreq = devm_devfreq_add_device(dev, &mtk_ccifreq_profile,
DEVFREQ_GOV_PASSIVE,
passive_data);
- if (IS_ERR(drv->devfreq)) {
- ret = -EPROBE_DEFER;
- dev_err(dev, "failed to add devfreq device: %ld\n",
- PTR_ERR(drv->devfreq));
- goto out_remove_opp_table;
- }
+ if (IS_ERR(drv->devfreq))
+ return dev_err_probe(dev, -EPROBE_DEFER,
+ "failed to add devfreq device: %ld\n",
+ PTR_ERR(drv->devfreq));
drv->opp_nb.notifier_call = mtk_ccifreq_opp_notifier;
ret = dev_pm_opp_register_notifier(dev, &drv->opp_nb);
- if (ret) {
- dev_err(dev, "failed to register opp notifier: %d\n", ret);
- goto out_remove_opp_table;
- }
- return 0;
-
-out_remove_opp_table:
- dev_pm_opp_of_remove_table(dev);
-
-out_disable_cci_clk:
- clk_disable_unprepare(drv->cci_clk);
-
-out_free_resources:
- if (regulator_is_enabled(drv->proc_reg))
- regulator_disable(drv->proc_reg);
- if (!IS_ERR_OR_NULL(drv->sram_reg) &&
- regulator_is_enabled(drv->sram_reg))
- regulator_disable(drv->sram_reg);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "failed to register opp notifier\n");
- return ret;
+ return 0;
}
static void mtk_ccifreq_remove(struct platform_device *pdev)
{
- struct device *dev = &pdev->dev;
- struct mtk_ccifreq_drv *drv;
-
- drv = platform_get_drvdata(pdev);
+ struct mtk_ccifreq_drv *drv = platform_get_drvdata(pdev);
- dev_pm_opp_unregister_notifier(dev, &drv->opp_nb);
- dev_pm_opp_of_remove_table(dev);
- clk_disable_unprepare(drv->cci_clk);
- regulator_disable(drv->proc_reg);
- if (drv->sram_reg)
- regulator_disable(drv->sram_reg);
+ dev_pm_opp_unregister_notifier(&pdev->dev, &drv->opp_nb);
}
static const struct mtk_ccifreq_platform_data mt8183_platform_data = {
@@ -0,0 +1,53 @@
From b19968e432fe2ebe3af4bc9190923edb70b6aeee Mon Sep 17 00:00:00 2001
From: Daniel Golle <daniel@makrotopia.org>
Date: Mon, 13 Apr 2026 15:22:05 +0100
Subject: [PATCH 2/2] PM / devfreq: mtk-cci: check cpufreq availability early
Check spufreq availablility early at probe so -EPROBE_DEFER is emitted
before touching any regulators.
Signed-off-by: Daniel Golle <daniel@makrotopia.org>
---
drivers/devfreq/mtk-cci-devfreq.c | 18 +++++++++++++++++-
1 file changed, 17 insertions(+), 1 deletion(-)
--- a/drivers/devfreq/mtk-cci-devfreq.c
+++ b/drivers/devfreq/mtk-cci-devfreq.c
@@ -4,6 +4,7 @@
*/
#include <linux/clk.h>
+#include <linux/cpufreq.h>
#include <linux/devfreq.h>
#include <linux/minmax.h>
#include <linux/module.h>
@@ -263,13 +264,28 @@ static void mtk_ccifreq_opp_of_remove_ta
static int mtk_ccifreq_probe(struct platform_device *pdev)
{
+ struct devfreq_passive_data *passive_data;
struct device *dev = &pdev->dev;
+ struct cpufreq_policy *policy;
struct mtk_ccifreq_drv *drv;
- struct devfreq_passive_data *passive_data;
struct dev_pm_opp *opp;
unsigned long rate, opp_volt;
int ret;
+ /*
+ * Check if cpufreq is available before touching any regulators.
+ * The passive devfreq governor needs cpufreq as its parent and
+ * will return -EPROBE_DEFER if it is not yet registered. If we
+ * enable the proc regulator (CPU core power) before this check,
+ * the subsequent probe failure causes devres to disable that
+ * regulator, potentially cutting CPU core voltage and hanging
+ * the system.
+ */
+ policy = cpufreq_cpu_get(0);
+ if (!policy)
+ return -EPROBE_DEFER;
+ cpufreq_cpu_put(policy);
+
drv = devm_kzalloc(dev, sizeof(*drv), GFP_KERNEL);
if (!drv)
return -ENOMEM;
@@ -0,0 +1,312 @@
From 67219f0130ec7c876ac0b299046460fad31caabf Mon Sep 17 00:00:00 2001
From: Rich Felker <dalias@aerifal.cx>
Date: Mon, 30 Mar 2026 16:00:50 -0400
Subject: fix pathological slowness & incorrect mappings in iconv gb18030
decoder
in order to implement the "UTF" aspect of gb18030 (ability to
represent arbitrary unicode characters not present in the 2-byte
mapping), we have to apply the index obtained from the encoded 4-byte
sequence into the set of unmapped characters. this was done by
scanning repeatedly over the table of mapped characters and counting
off mapped characters below a running index by which to adjust the
running index by on each iteration. this iterative process eventually
leaves us with the value of the Nth unmapped character replacing the
index, but depending on which particular character that is, the number
of iterations needed to find it can be in the tens of thousands, and
each iteration traverses the whole 126x190 table in the inner loop.
this can lead to run times exceeding an entire second per character on
moderate-speed machines.
on top of that, the transformation logic produced wrong results for
BMP characters above the the surrogate range, as a result of not
correctly accounting for it being excluded, and for characters outside
the BMP, as a result of a misunderstanding of how gb18030 encodes
them.
this patch replaces the unmapped character lookup with a single linear
search of a list of unmapped ranges. there are only 206 such ranges,
and these are permanently assigned and unchangeable as a consequence
of the character encoding having to be stable, so a simple array of
16-bit start/length values for each range consumes only 824 bytes, a
very reasonable size cost here.
this new table accounts for the previously-incorrect surrogate
handling, and non-BMP characters are handled correctly by a single
offset, without the need for any unmapped-range search.
there are still a small number of mappings that are incorrect due to
late changes made in the definition of gb18030, swapping PUA
codepoints with proper Unicode characters. correcting these requires a
postprocessing step that will be added later.
---
src/locale/gb18030utf.h | 206 ++++++++++++++++++++++++++++++++++++++++
src/locale/iconv.c | 33 +++++--
2 files changed, 230 insertions(+), 9 deletions(-)
create mode 100644 src/locale/gb18030utf.h
--- /dev/null
+++ b/src/locale/gb18030utf.h
@@ -0,0 +1,206 @@
+{ 0x80, 36 },
+{ 0xa5, 2 },
+{ 0xa9, 7 },
+{ 0xb2, 5 },
+{ 0xb8, 31 },
+{ 0xd8, 8 },
+{ 0xe2, 6 },
+{ 0xeb, 1 },
+{ 0xee, 4 },
+{ 0xf4, 3 },
+{ 0xf8, 1 },
+{ 0xfb, 1 },
+{ 0xfd, 4 },
+{ 0x102, 17 },
+{ 0x114, 7 },
+{ 0x11c, 15 },
+{ 0x12c, 24 },
+{ 0x145, 3 },
+{ 0x149, 4 },
+{ 0x14e, 29 },
+{ 0x16c, 98 },
+{ 0x1cf, 1 },
+{ 0x1d1, 1 },
+{ 0x1d3, 1 },
+{ 0x1d5, 1 },
+{ 0x1d7, 1 },
+{ 0x1d9, 1 },
+{ 0x1db, 1 },
+{ 0x1dd, 28 },
+{ 0x1fa, 87 },
+{ 0x252, 15 },
+{ 0x262, 101 },
+{ 0x2c8, 1 },
+{ 0x2cc, 13 },
+{ 0x2da, 183 },
+{ 0x3a2, 1 },
+{ 0x3aa, 7 },
+{ 0x3c2, 1 },
+{ 0x3ca, 55 },
+{ 0x402, 14 },
+{ 0x450, 1 },
+{ 0x452, 7102 },
+{ 0x2011, 2 },
+{ 0x2017, 1 },
+{ 0x201a, 2 },
+{ 0x201e, 7 },
+{ 0x2027, 9 },
+{ 0x2031, 1 },
+{ 0x2034, 1 },
+{ 0x2036, 5 },
+{ 0x203c, 112 },
+{ 0x20ad, 86 },
+{ 0x2104, 1 },
+{ 0x2106, 3 },
+{ 0x210a, 12 },
+{ 0x2117, 10 },
+{ 0x2122, 62 },
+{ 0x216c, 4 },
+{ 0x217a, 22 },
+{ 0x2194, 2 },
+{ 0x219a, 110 },
+{ 0x2209, 6 },
+{ 0x2210, 1 },
+{ 0x2212, 3 },
+{ 0x2216, 4 },
+{ 0x221b, 2 },
+{ 0x2221, 2 },
+{ 0x2224, 1 },
+{ 0x2226, 1 },
+{ 0x222c, 2 },
+{ 0x222f, 5 },
+{ 0x2238, 5 },
+{ 0x223e, 10 },
+{ 0x2249, 3 },
+{ 0x224d, 5 },
+{ 0x2253, 13 },
+{ 0x2262, 2 },
+{ 0x2268, 6 },
+{ 0x2270, 37 },
+{ 0x2296, 3 },
+{ 0x229a, 11 },
+{ 0x22a6, 25 },
+{ 0x22c0, 82 },
+{ 0x2313, 333 },
+{ 0x246a, 10 },
+{ 0x249c, 100 },
+{ 0x254c, 4 },
+{ 0x2574, 13 },
+{ 0x2590, 3 },
+{ 0x2596, 10 },
+{ 0x25a2, 16 },
+{ 0x25b4, 8 },
+{ 0x25be, 8 },
+{ 0x25c8, 3 },
+{ 0x25cc, 2 },
+{ 0x25d0, 18 },
+{ 0x25e6, 31 },
+{ 0x2607, 2 },
+{ 0x260a, 54 },
+{ 0x2641, 1 },
+{ 0x2643, 2110 },
+{ 0x2e82, 2 },
+{ 0x2e85, 3 },
+{ 0x2e89, 2 },
+{ 0x2e8d, 10 },
+{ 0x2e98, 15 },
+{ 0x2ea8, 2 },
+{ 0x2eab, 3 },
+{ 0x2eaf, 4 },
+{ 0x2eb4, 2 },
+{ 0x2eb8, 3 },
+{ 0x2ebc, 14 },
+{ 0x2ecb, 293 },
+{ 0x2ffc, 4 },
+{ 0x3004, 1 },
+{ 0x3018, 5 },
+{ 0x301f, 2 },
+{ 0x302a, 20 },
+{ 0x303f, 2 },
+{ 0x3094, 7 },
+{ 0x309f, 2 },
+{ 0x30f7, 5 },
+{ 0x30ff, 6 },
+{ 0x312a, 246 },
+{ 0x322a, 7 },
+{ 0x3232, 113 },
+{ 0x32a4, 234 },
+{ 0x3390, 12 },
+{ 0x339f, 2 },
+{ 0x33a2, 34 },
+{ 0x33c5, 9 },
+{ 0x33cf, 2 },
+{ 0x33d3, 2 },
+{ 0x33d6, 113 },
+{ 0x3448, 43 },
+{ 0x3474, 298 },
+{ 0x359f, 111 },
+{ 0x360f, 11 },
+{ 0x361b, 765 },
+{ 0x3919, 85 },
+{ 0x396f, 96 },
+{ 0x39d1, 14 },
+{ 0x39e0, 147 },
+{ 0x3a74, 218 },
+{ 0x3b4f, 287 },
+{ 0x3c6f, 113 },
+{ 0x3ce1, 885 },
+{ 0x4057, 264 },
+{ 0x4160, 471 },
+{ 0x4338, 116 },
+{ 0x43ad, 4 },
+{ 0x43b2, 43 },
+{ 0x43de, 248 },
+{ 0x44d7, 373 },
+{ 0x464d, 20 },
+{ 0x4662, 193 },
+{ 0x4724, 5 },
+{ 0x472a, 82 },
+{ 0x477d, 16 },
+{ 0x478e, 441 },
+{ 0x4948, 50 },
+{ 0x497b, 2 },
+{ 0x497e, 4 },
+{ 0x4984, 1 },
+{ 0x4987, 20 },
+{ 0x499c, 3 },
+{ 0x49a0, 22 },
+{ 0x49b8, 703 },
+{ 0x4c78, 39 },
+{ 0x4ca4, 111 },
+{ 0x4d1a, 148 },
+{ 0x4daf, 81 },
+{ 0x9fa6, 14426 },
+{ 0xe76c, 1 },
+{ 0xe7c8, 1 },
+{ 0xe7e7, 13 },
+{ 0xe815, 1 },
+{ 0xe819, 5 },
+{ 0xe81f, 7 },
+{ 0xe827, 4 },
+{ 0xe82d, 4 },
+{ 0xe833, 8 },
+{ 0xe83c, 7 },
+{ 0xe844, 16 },
+{ 0xe856, 14 },
+{ 0xe865, 4295 },
+{ 0xf92d, 76 },
+{ 0xf97a, 27 },
+{ 0xf996, 81 },
+{ 0xf9e8, 9 },
+{ 0xf9f2, 26 },
+{ 0xfa10, 1 },
+{ 0xfa12, 1 },
+{ 0xfa15, 3 },
+{ 0xfa19, 6 },
+{ 0xfa22, 1 },
+{ 0xfa25, 2 },
+{ 0xfa2a, 1030 },
+{ 0xfe32, 1 },
+{ 0xfe45, 4 },
+{ 0xfe53, 1 },
+{ 0xfe58, 1 },
+{ 0xfe67, 1 },
+{ 0xfe6c, 149 },
+{ 0xff5f, 129 },
+{ 0xffe6, 26 },
--- a/src/locale/iconv.c
+++ b/src/locale/iconv.c
@@ -74,6 +74,10 @@ static const unsigned short gb18030[126]
#include "gb18030.h"
};
+static const unsigned short gb18030utf[][2] = {
+#include "gb18030utf.h"
+};
+
static const unsigned short big5[89][157] = {
#include "big5.h"
};
@@ -224,6 +228,8 @@ static unsigned uni_to_jis(unsigned c)
}
}
+#define countof(a) (sizeof (a) / sizeof *(a))
+
size_t iconv(iconv_t cd, char **restrict in, size_t *restrict inb, char **restrict out, size_t *restrict outb)
{
size_t x=0;
@@ -423,15 +429,24 @@ size_t iconv(iconv_t cd, char **restrict
d = *((unsigned char *)*in + 3);
if (d-'0'>9) goto ilseq;
c += d-'0';
- c += 128;
- for (d=0; d<=c; ) {
- k = 0;
- for (int i=0; i<126; i++)
- for (int j=0; j<190; j++)
- if (gb18030[i][j]-d <= c-d)
- k++;
- d = c+1;
- c += k;
+ /* Starting at 90 30 81 30 (189000), mapping is
+ * linear without gaps, to U+10000 and up. */
+ if (c >= 189000) {
+ c -= 189000;
+ c += 0x10000;
+ if (c >= 0x110000) goto ilseq;
+ break;
+ }
+ /* Otherwise we must process an index into set
+ * of characters unmapped by 2-byte table. */
+ for (int i=0; ; i++) {
+ if (i==countof(gb18030utf))
+ goto ilseq;
+ if (c<gb18030utf[i][1]) {
+ c += gb18030utf[i][0];
+ break;
+ }
+ c -= gb18030utf[i][1];
}
break;
}
@@ -0,0 +1,39 @@
From 228da39e38c1cae13cbe637e771412c1984dba5d Mon Sep 17 00:00:00 2001
From: Rich Felker <dalias@aerifal.cx>
Date: Thu, 9 Apr 2026 22:51:30 -0400
Subject: qsort: fix leonardo heap corruption from bug in doubleword ctz
primitive
the pntz function, implementing a "count trailing zeros" variant for a
bit vector consisting of two size_t words, erroneously returned zero
rather than the number of bits in the low word when the first bit set
was the low bit of the high word.
as a result, a loop in the trinkle function which should have a
guaranteed small bound on the number of iterations, could run
unboundedly, thereby overflowing a stack-based working-space array
which was sized for the bound.
CVE-2026-40200 has been assigned for this issue.
---
src/stdlib/qsort.c | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
--- a/src/stdlib/qsort.c
+++ b/src/stdlib/qsort.c
@@ -34,11 +34,11 @@
typedef int (*cmpfun)(const void *, const void *, void *);
+/* returns index of first bit set, excluding the low bit assumed to always
+ * be set, starting from low bit of p[0] up through high bit of p[1] */
static inline int pntz(size_t p[2]) {
- int r = ntz(p[0] - 1);
- if(r != 0 || (r = 8*sizeof(size_t) + ntz(p[1])) != 8*sizeof(size_t)) {
- return r;
- }
+ if (p[0] != 1) return ntz(p[0] - 1);
+ if (p[1]) return 8*sizeof(size_t) + ntz(p[1]);
return 0;
}
@@ -0,0 +1,87 @@
From b3291b9a9f77f1f993d2b4f8c68a26cf09221ae7 Mon Sep 17 00:00:00 2001
From: Rich Felker <dalias@aerifal.cx>
Date: Thu, 9 Apr 2026 23:40:53 -0400
Subject: qsort: hard-preclude oob array writes independent of any invariants
while the root cause of CVE-2026-40200 was a faulty ctz primitive, the
fallout of the bug would have been limited to erroneous sorting or
infinite loop if not for the stores to a stack-based array that
depended on trusting invariants in order not to go out of bounds.
increase the size of the array to a power of two so that we can mask
indices into it to force them into range. in the absence of any
further bug, the masking is a no-op, but it does not have any
measurable performance cost, and it makes spatial memory safety
trivial to prove (and for readers not familiar with the algorithms to
trust).
---
src/stdlib/qsort.c | 20 +++++++++++++-------
1 file changed, 13 insertions(+), 7 deletions(-)
--- a/src/stdlib/qsort.c
+++ b/src/stdlib/qsort.c
@@ -89,10 +89,16 @@ static inline void shr(size_t p[2], int
p[1] >>= n;
}
+/* power-of-two length for working array so that we can mask indices and
+ * not depend on any invariant of the algorithm for spatial memory safety.
+ * the original size was just 14*sizeof(size_t)+1 */
+#define AR_LEN (16 * sizeof(size_t))
+#define AR_MASK (AR_LEN - 1)
+
static void sift(unsigned char *head, size_t width, cmpfun cmp, void *arg, int pshift, size_t lp[])
{
unsigned char *rt, *lf;
- unsigned char *ar[14 * sizeof(size_t) + 1];
+ unsigned char *ar[AR_LEN];
int i = 1;
ar[0] = head;
@@ -104,16 +110,16 @@ static void sift(unsigned char *head, si
break;
}
if(cmp(lf, rt, arg) >= 0) {
- ar[i++] = lf;
+ ar[i++ & AR_MASK] = lf;
head = lf;
pshift -= 1;
} else {
- ar[i++] = rt;
+ ar[i++ & AR_MASK] = rt;
head = rt;
pshift -= 2;
}
}
- cycle(width, ar, i);
+ cycle(width, ar, i & AR_MASK);
}
static void trinkle(unsigned char *head, size_t width, cmpfun cmp, void *arg, size_t pp[2], int pshift, int trusty, size_t lp[])
@@ -121,7 +127,7 @@ static void trinkle(unsigned char *head,
unsigned char *stepson,
*rt, *lf;
size_t p[2];
- unsigned char *ar[14 * sizeof(size_t) + 1];
+ unsigned char *ar[AR_LEN];
int i = 1;
int trail;
@@ -142,7 +148,7 @@ static void trinkle(unsigned char *head,
}
}
- ar[i++] = stepson;
+ ar[i++ & AR_MASK] = stepson;
head = stepson;
trail = pntz(p);
shr(p, trail);
@@ -150,7 +156,7 @@ static void trinkle(unsigned char *head,
trusty = 0;
}
if(!trusty) {
- cycle(width, ar, i);
+ cycle(width, ar, i & AR_MASK);
sift(head, width, cmp, arg, pshift, lp);
}
}
@@ -0,0 +1,34 @@
From 5122f9f3c99fee366167c5de98b31546312921ab Mon Sep 17 00:00:00 2001
From: Luca Kellermann <mailto.luca.kellermann@gmail.com>
Date: Fri, 10 Apr 2026 03:03:22 +0200
Subject: qsort: fix shift UB in shl and shr
if shl() or shr() are called with n==8*sizeof(size_t), n is adjusted
to 0. the shift by (sizeof(size_t) * 8 - n) that then follows will
consequently shift by the width of size_t, which is UB and in practice
produces an incorrect result.
return early in this case. the bitvector p was already shifted by the
required amount.
---
src/stdlib/qsort.c | 2 ++
1 file changed, 2 insertions(+)
--- a/src/stdlib/qsort.c
+++ b/src/stdlib/qsort.c
@@ -71,6 +71,7 @@ static inline void shl(size_t p[2], int
n -= 8 * sizeof(size_t);
p[1] = p[0];
p[0] = 0;
+ if (!n) return;
}
p[1] <<= n;
p[1] |= p[0] >> (sizeof(size_t) * 8 - n);
@@ -83,6 +84,7 @@ static inline void shr(size_t p[2], int
n -= 8 * sizeof(size_t);
p[0] = p[1];
p[1] = 0;
+ if (!n) return;
}
p[0] >>= n;
p[0] |= p[1] << (sizeof(size_t) * 8 - n);
@@ -24,7 +24,7 @@
static const unsigned short jis0208[84][94] = {
#include "jis0208.h"
};
@@ -85,6 +88,7 @@ static const unsigned short hkscs[] = {
@@ -89,6 +92,7 @@ static const unsigned short hkscs[] = {
static const unsigned short ksc[93][94] = {
#include "ksc.h"
};
@@ -32,7 +32,7 @@
static const unsigned short rev_jis[] = {
#include "revjis.h"
@@ -205,6 +209,7 @@ static unsigned legacy_map(const unsigne
@@ -209,6 +213,7 @@ static unsigned legacy_map(const unsigne
return x < 256 ? x : legacy_chars[x-256];
}
@@ -40,15 +40,15 @@
static unsigned uni_to_jis(unsigned c)
{
unsigned nel = sizeof rev_jis / sizeof *rev_jis;
@@ -223,6 +228,7 @@ static unsigned uni_to_jis(unsigned c)
@@ -227,6 +232,7 @@ static unsigned uni_to_jis(unsigned c)
}
}
}
+#endif
size_t iconv(iconv_t cd, char **restrict in, size_t *restrict inb, char **restrict out, size_t *restrict outb)
{
@@ -319,6 +325,7 @@ size_t iconv(iconv_t cd, char **restrict
#define countof(a) (sizeof (a) / sizeof *(a))
@@ -325,6 +331,7 @@ size_t iconv(iconv_t cd, char **restrict
}
type = scd->state;
continue;
@@ -56,7 +56,7 @@
case SHIFT_JIS:
if (c < 128) break;
if (c-0xa1 <= 0xdf-0xa1) {
@@ -518,6 +525,7 @@ size_t iconv(iconv_t cd, char **restrict
@@ -533,6 +540,7 @@ size_t iconv(iconv_t cd, char **restrict
c = ksc[c][d];
if (!c) goto ilseq;
break;
@@ -64,7 +64,7 @@
default:
if (!c) break;
c = legacy_map(map, c);
@@ -559,6 +567,7 @@ size_t iconv(iconv_t cd, char **restrict
@@ -574,6 +582,7 @@ size_t iconv(iconv_t cd, char **restrict
}
}
goto subst;
@@ -72,7 +72,7 @@
case SHIFT_JIS:
if (c < 128) goto revout;
if (c == 0xa5) {
@@ -632,6 +641,7 @@ size_t iconv(iconv_t cd, char **restrict
@@ -647,6 +656,7 @@ size_t iconv(iconv_t cd, char **restrict
*(*out)++ = 'B';
*outb -= 8;
break;