Skip to content

Commit 0d911e9

Browse files
authored
Merge pull request #5058 from willmmiles/0_15_x-update-source-version-check
0.15: Add old version check to OTA update
2 parents 2d0771f + 5a9bdad commit 0d911e9

File tree

2 files changed

+71
-32
lines changed

2 files changed

+71
-32
lines changed

wled00/wled_metadata.cpp

Lines changed: 70 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
#endif
1717

1818
constexpr uint32_t WLED_CUSTOM_DESC_MAGIC = 0x57535453; // "WSTS" (WLED System Tag Structure)
19-
constexpr uint32_t WLED_CUSTOM_DESC_VERSION = 1;
19+
constexpr uint32_t WLED_CUSTOM_DESC_VERSION = 2; // v1 - original PR; v2 - "safe to update from" version
2020

2121
// Compile-time validation that release name doesn't exceed maximum length
2222
static_assert(sizeof(WLED_RELEASE_NAME) <= WLED_RELEASE_NAME_MAX_LEN,
@@ -59,6 +59,7 @@ const wled_metadata_t __attribute__((section(BUILD_METADATA_SECTION))) WLED_BUIL
5959
TOSTRING(WLED_VERSION),
6060
WLED_RELEASE_NAME, // release_name
6161
std::integral_constant<uint32_t, djb2_hash_constexpr(WLED_RELEASE_NAME)>::value, // hash - computed at compile time; integral_constant enforces this
62+
{ 0, 0, 0 }, // All other platforms can update safely
6263
};
6364

6465
static const char repoString_s[] PROGMEM = WLED_REPO;
@@ -80,40 +81,47 @@ const __FlashStringHelper* brandString = FPSTR(brandString_s);
8081
* @return true if structure was found and extracted, false otherwise
8182
*/
8283
bool findWledMetadata(const uint8_t* binaryData, size_t dataSize, wled_metadata_t* extractedDesc) {
83-
if (!binaryData || !extractedDesc || dataSize < sizeof(wled_metadata_t)) {
84-
return false;
85-
}
84+
if (!binaryData || !extractedDesc || dataSize < sizeof(wled_metadata_t)) {
85+
return false;
86+
}
8687

87-
for (size_t offset = 0; offset <= dataSize - sizeof(wled_metadata_t); offset++) {
88-
const wled_metadata_t* custom_desc = (const wled_metadata_t*)(binaryData + offset);
88+
for (size_t offset = 0; offset <= dataSize - sizeof(wled_metadata_t); offset++) {
89+
if ((binaryData[offset]) == static_cast<char>(WLED_CUSTOM_DESC_MAGIC)) {
90+
// First byte matched; check next in an alignment-safe way
91+
uint32_t data_magic;
92+
memcpy(&data_magic, binaryData + offset, sizeof(data_magic));
93+
94+
// Check for magic number
95+
if (data_magic == WLED_CUSTOM_DESC_MAGIC) {
96+
wled_metadata_t candidate;
97+
memcpy(&candidate, binaryData + offset, sizeof(candidate));
98+
99+
// Found potential match, validate version
100+
if (candidate.desc_version > WLED_CUSTOM_DESC_VERSION) {
101+
DEBUG_PRINTF_P(PSTR("Found WLED structure at offset %u but version mismatch: %u\n"),
102+
offset, candidate.desc_version);
103+
continue;
104+
}
89105

90-
// Check for magic number
91-
if (custom_desc->magic == WLED_CUSTOM_DESC_MAGIC) {
92-
// Found potential match, validate version
93-
if (custom_desc->desc_version != WLED_CUSTOM_DESC_VERSION) {
94-
DEBUG_PRINTF_P(PSTR("Found WLED structure at offset %u but version mismatch: %u\n"),
95-
offset, custom_desc->desc_version);
96-
continue;
97-
}
98-
99-
// Validate hash using runtime function
100-
uint32_t expected_hash = djb2_hash_runtime(custom_desc->release_name);
101-
if (custom_desc->hash != expected_hash) {
102-
DEBUG_PRINTF_P(PSTR("Found WLED structure at offset %u but hash mismatch\n"), offset);
103-
continue;
104-
}
105-
106-
// Valid structure found - copy entire structure
107-
memcpy(extractedDesc, custom_desc, sizeof(wled_metadata_t));
108-
109-
DEBUG_PRINTF_P(PSTR("Extracted WLED structure at offset %u: '%s'\n"),
110-
offset, extractedDesc->release_name);
111-
return true;
106+
// Validate hash using runtime function
107+
uint32_t expected_hash = djb2_hash_runtime(candidate.release_name);
108+
if (candidate.hash != expected_hash) {
109+
DEBUG_PRINTF_P(PSTR("Found WLED structure at offset %u but hash mismatch\n"), offset);
110+
continue;
112111
}
112+
113+
// Valid structure found - copy entire structure
114+
*extractedDesc = candidate;
115+
116+
DEBUG_PRINTF_P(PSTR("Extracted WLED structure at offset %u: '%s'\n"),
117+
offset, extractedDesc->release_name);
118+
return true;
119+
}
113120
}
114-
115-
DEBUG_PRINTLN(F("No WLED custom description found in binary"));
116-
return false;
121+
}
122+
123+
DEBUG_PRINTLN(F("No WLED custom description found in binary"));
124+
return false;
117125
}
118126

119127

@@ -144,13 +152,43 @@ bool shouldAllowOTA(const wled_metadata_t& firmwareDescription, char* errorMessa
144152

145153
if (strncmp_P(safeFirmwareRelease, releaseString, WLED_RELEASE_NAME_MAX_LEN) != 0) {
146154
if (errorMessage && errorMessageLen > 0) {
147-
snprintf_P(errorMessage, errorMessageLen, PSTR("Firmware compatibility mismatch: current='%s', uploaded='%s'."),
155+
snprintf_P(errorMessage, errorMessageLen, PSTR("Firmware release name mismatch: current='%s', uploaded='%s'."),
148156
releaseString, safeFirmwareRelease);
149157
errorMessage[errorMessageLen - 1] = '\0'; // Ensure null termination
150158
}
151159
return false;
152160
}
153161

162+
if (firmwareDescription.desc_version > 1) {
163+
// Add safe version check
164+
// Parse our version (x.y.z) and compare it to the "safe version" array
165+
const char* our_version = versionString;
166+
for(unsigned v_index = 0; v_index < 3; ++v_index) {
167+
char* our_version_end = nullptr;
168+
long our_v_parsed = strtol(our_version, &our_version_end, 10);
169+
if (!our_version_end || (our_version_end == our_version)) {
170+
// We were built with a malformed version string
171+
// We blame the integrator and attempt the update anyways - nothing the user can do to fix this
172+
break;
173+
}
174+
175+
if (firmwareDescription.safe_update_version[v_index] > our_v_parsed) {
176+
if (errorMessage && errorMessageLen > 0) {
177+
snprintf_P(errorMessage, errorMessageLen, PSTR("Cannot update from this version: requires at least %d.%d.%d, current='%s'."),
178+
firmwareDescription.safe_update_version[0], firmwareDescription.safe_update_version[1], firmwareDescription.safe_update_version[2],
179+
versionString);
180+
errorMessage[errorMessageLen - 1] = '\0'; // Ensure null termination
181+
}
182+
return false;
183+
} else if (firmwareDescription.safe_update_version[v_index] < our_v_parsed) {
184+
break; // no need to check the other components
185+
}
186+
187+
if (*our_version_end == '.') ++our_version_end;
188+
our_version = our_version_end;
189+
}
190+
}
191+
154192
// TODO: additional checks go here
155193

156194
return true;

wled00/wled_metadata.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ typedef struct {
2626
char wled_version[WLED_VERSION_MAX_LEN];
2727
char release_name[WLED_RELEASE_NAME_MAX_LEN]; // Release name (null-terminated)
2828
uint32_t hash; // Structure sanity check
29+
uint8_t safe_update_version[3]; // Indicates version it's known to be safe to install this update from: major, minor, patch
2930
} __attribute__((packed)) wled_metadata_t;
3031

3132

0 commit comments

Comments
 (0)