NetBSD-Bugs archive
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]
kern/60620: Fatal logic inversion in fdtbus_get_u32prop_with_default() breaks usmsc(4) and other FDT drivers
>Number: 60620
>Category: kern
>Synopsis: Fatal logic inversion in fdtbus_get_u32prop_with_default() breaks usmsc(4) and other FDT drivers
>Confidential: no
>Severity: serious
>Priority: high
>Responsible: kern-bug-people
>State: open
>Class: sw-bug
>Submitter-Id: net
>Arrival-Date: Wed Aug 19 05:05:00 +0000 2026
>Originator: Jun Ebihara
>Release: NetBSD 11.99.7
>Organization:
SOUM Corporation
>Environment:
NetBSD rpi 11.99.6 NetBSD 11.99.7 (RPI2) #0: Wed Aug 19 13:29:46 JST 2026 jun%soum.co.jp@localhost:/usr/src/sys/arch/evbarm/compile/GENERIC evbarm
>Description:
A fatal regression was introduced in sys/dev/fdt/fdt_subr.c (revision 1.43)
with the implementation of fdtbus_get_u32prop_with_default().
The internal logic of of_getprop_uint32() return value checking is inverted:
----------------------------------------------------------------------
static int
fdtbus_get_u32prop_with_default(int phandle, const char *prop, int dflt)
{
uint32_t val;
if (of_getprop_uint32(phandle, prop, &val)) {
val = dflt;
}
return val;
}
----------------------------------------------------------------------
In NetBSD, of_getprop_uint32() returns 0 on success and non-zero on failure.
As a result of the above implementation, when the property successfully
exists in the Device Tree, the code incorrectly overwrites `val` with `dflt`.
Conversely, if the property does not exist, it returns an uninitialized
stack variable `val`.
This completely breaks the parsing of crucial properties like "#address-cells"
and "#size-cells", causing fdtbus_get_reg64() to decode incorrect I/O base
addresses for various hardware components.
For instance, this causes the usmsc(4) driver to fail to talk to the hardware,
spatting the following errors during boot:
usmsc0: warning: Failed to read register 0x114
usmsc0: warning: MII is busy
Additionally, fdtbus_get_phandle_with_data() suffers from an uninitialized
variable risk where `cells_num` is left uninitialized if of_getprop_uint32()
fails, leading to an out-of-bounds pointer advance (`p += reclen`).
>How-To-Repeat:
Boot a NetBSD/evbarm kernel built with sys/dev/fdt/fdt_subr.c (v1.43) on
a target board utilizing FDT-based attachments (e.g., Raspberry Pi with usmsc).
Observe that the MAC address/MII registers fail to register properly.
or try this sample test code:
% gcc -Wall -o test_fdt test_fdt_subr.c
%./test_fdt
===> test_fdt_subr.c
#include <stdio.h>
#include <stdint.h>
#include <stdbool.h>
typedef uint32_t u_int;
static bool mock_property_exists = false;
static uint32_t mock_property_value = 0;
int
of_getprop_uint32(int phandle, const char *prop, uint32_t *val)
{
if (mock_property_exists) {
*val = mock_property_value;
return 0;
}
return -1;
}
static int
fdtbus_get_u32prop_with_default_ORIG(int phandle, const char *prop, int dflt)
{
uint32_t val;
if (of_getprop_uint32(phandle, prop, &val)) {
val = dflt;
}
return val;
}
static int
fdtbus_get_u32prop_with_default_FIXED(int phandle, const char *prop, int dflt)
{
uint32_t val;
if (of_getprop_uint32(phandle, prop, &val) != 0) {
return dflt;
}
return val;
}
int
main(void)
{
int dflt_input = 100;
int result_orig, result_fixed;
printf("=== FDT Regression Test Bench ===\n\n");
mock_property_exists = true;
mock_property_value = 200;
result_orig = fdtbus_get_u32prop_with_default_ORIG(1, "test-prop", dflt_input);
result_fixed = fdtbus_get_u32prop_with_default_FIXED(1, "test-prop", dflt_input);
printf("[Case 1] Property EXISTS (Expected output: 200)\n");
printf(" - ORIGINAL (Buggy): %d --> %s\n", result_orig,
(result_orig == 200) ? "PASS" : "FAIL (Inverted logic applied dflt or stack garbage)");
printf(" - FIXED (Patch): %d --> %s\n", result_fixed,
(result_fixed == 200) ? "PASS" : "FAIL");
printf("\n");
mock_property_exists = false;
result_orig = fdtbus_get_u32prop_with_default_ORIG(1, "test-prop", dflt_input);
result_fixed = fdtbus_get_u32prop_with_default_FIXED(1, "test-prop", dflt_input);
printf("[Case 2] Property DOES NOT EXIST (Expected output: 100)\n");
printf(" - ORIGINAL (Buggy): %d --> %s\n", result_orig,
(result_orig == dflt_input) ? "PASS" : "FAIL (Returned uninitialized variable)");
printf(" - FIXED (Patch): %d --> %s\n", result_fixed,
(result_fixed == dflt_input) ? "PASS" : "FAIL");
printf("\n");
printf("=== Test finished ===\n");
return 0;
}
Results:
=== FDT Regression Test Bench ===
[Case 1] Property EXISTS (Expected output: 200)
- ORIGINAL (Buggy): 100 --> FAIL (Inverted logic applied dflt or stack garbage)
- FIXED (Patch): 200 --> PASS
[Case 2] Property DOES NOT EXIST (Expected output: 100)
- ORIGINAL (Buggy): 200 --> FAIL (Returned uninitialized variable)
- FIXED (Patch): 100 --> PASS
=== Test finished ===
>Fix:
Apply the following patch to fix the logic inversion in
fdtbus_get_u32prop_with_default() and prevent the uninitialized variable
risk in fdtbus_get_phandle_with_data().
--- sys/dev/fdt/fdt_subr.c.orig 2026-08-07 14:31:36.000000000 +0900
+++ sys/dev/fdt/fdt_subr.c 2026-08-19 06:00:00.000000000 +0900
@@ -58,9 +58,9 @@
fdtbus_get_u32prop_with_default(int phandle, const char *prop, int dflt)
{
uint32_t val;
- if (of_getprop_uint32(phandle, prop, &val)) {
- val = dflt;
- }
+ if (of_getprop_uint32(phandle, prop, &val) != 0) {
+ return dflt;
+ }
return val;
}
@@ -95,11 +95,14 @@
for (int i = 0; len > 0; i++) {
u_int phandle_ref = be32toh(*p);
const u_int iparent = fdtbus_get_phandle_from_native(phandle_ref);
- uint32_t cells_num;
- of_getprop_uint32(iparent, cells, &cells_num);
+ uint32_t cells_num = 0;
+ if (of_getprop_uint32(iparent, cells, &cells_num) != 0) {
+ return EINVAL;
+ }
if (index == i) {
if (data != NULL) {
data->phandle = iparent;
data->count = cells_num;
data->values = p + offset;
}
goto done;
}
const u_int reclen = offset + cells_num;
+ if (len < (int)(reclen * sizeof(u_int))) {
+ return EINVAL;
+ }
len -= reclen * sizeof(u_int);
p += reclen;
}
Home |
Main Index |
Thread Index |
Old Index