Skip to content

Conversation

@UncleGrumpy
Copy link
Collaborator

@UncleGrumpy UncleGrumpy commented Feb 10, 2026

Fixes an FD leak, the image file was never closed properly after writing. Fixes a misleading error message when a binary overflows its partition. Catches errors in file:write/2 operations, rather than blindly continuing.

Closes #2084
Closes #2094

These changes are made under both the "Apache 2.0" and the "GNU Lesser General
Public License 2.1 or later" license terms (dual license).

SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later

@UncleGrumpy UncleGrumpy force-pushed the mkimage-fix branch 2 times, most recently from dc96177 to ff8e506 Compare February 10, 2026 01:50
@UncleGrumpy UncleGrumpy marked this pull request as draft February 10, 2026 01:59
@UncleGrumpy UncleGrumpy marked this pull request as ready for review February 10, 2026 02:24
@UncleGrumpy UncleGrumpy requested a review from pguyot February 10, 2026 02:25
@UncleGrumpy UncleGrumpy force-pushed the mkimage-fix branch 2 times, most recently from 729ef65 to 89313e8 Compare February 10, 2026 15:54
SegmentEnd =
case try_read(SegmentPaths) of
{ok, Data} ->
file:write(Fout, Data),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From opus:
The return value of file:write(Fout, Data) is ignored. If the write fails (e.g., disk full), the script continues silently.

Fix: Change to ok = file:write(Fout, Data) to crash on error.

Copy link
Collaborator Author

@UncleGrumpy UncleGrumpy Feb 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should cause a crash, as the result of file:write/2 will not be ok, so the script will not continue, but it will leave the file descriptor open, which is a problem.

(Edit: I did miss adding the ok = file:write(Fout, Data) here, I had fixed other occurrences, but missed that one. So it would NOT have crashed as it was)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

silently progessing doesn't seem good.. but nitpick

maybe try/after:

Screenshot 2026-02-10 at 21 06 04

Copy link
Collaborator Author

@UncleGrumpy UncleGrumpy Feb 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I fixed this to throw a useful error message, and close the file descriptor when an error is encountered.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

try/after doesn't really apply here, errors from file:write/2 are returned as a tuple, not thrown.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Screenshot 2026-02-10 at 21 06 04

I couldn't view the screenshot, There is a message that the file is corrupted.

Copy link
Collaborator Author

@UncleGrumpy UncleGrumpy Feb 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I fixed this to throw a useful error message, and close the file descriptor when an error is encountered.

This had a bad line in it, it should be good now.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

try/after doesn't really apply here, errors from file:write/2 are returned as a tuple, not thrown.

but the after path is always run right? I'm no erlang expert - but seems to be DRY - but whatever you prefer!
screenshot

Copy link
Collaborator Author

@UncleGrumpy UncleGrumpy Feb 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, after is always run. That makes sense. Updated to use a single file:close/1 with try/after.

if
Segments == [] ->
%% Pad to end on 32-byte alignment, since we don't have a next offset to pad too
SectorPad = 32 - (SegmentEnd rem 32),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From opus:

The padding calculation logic is slightly incorrect. 32 - (SegmentEnd rem 32) will result in 32 when SegmentEnd is perfectly aligned (remainder 0), causing 32 bytes of unnecessary padding to be written. The case 0 -> ok is unreachable.

Fix: Change the calculation to SectorPad = (32 - (SegmentEnd rem 32)) rem 32.

Copy link
Collaborator Author

@UncleGrumpy UncleGrumpy Feb 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indeed. Although it could be argued that the next blocks should still be marked as free, using 16#ff, so there is a clear end to the segment, and any old data that may be present is not accidentally read as valid data. But the 0 case was not well thought out ;-)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I cleaned this up and expanded the comment to explain padding with 32 bytes if the segment is already 32-byte aligned.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I get a bad feeling with the implicit overpadding - of course it's hypotethical - but maybe I carefully craft a last segment that zeroes out everything, and then it's overpadded and img is too large, or some similar hypothetical..

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did have that very same thought, there really isn’t a way to do that from within the ESP-IDF. What we really need here is to know either the length of the partition, or the offset of any partitions in the partition that follow the final image segment.

As it is right now, worst case is that we overwrite the first 32 bytes of the application partition, if the libraries in the boot partition happen to fill every single byte of the boot.avm partition exactly.

I can back out if the extra padding, but it there is any data already written to flash following the end of the esp32boot.avm libraries this may be silently loaded and processed by the VM.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But… if we follow my suggestion of moving the NVS partition to follow the library partition, we may be overwriting values that the user intended to keep.

Copy link
Collaborator Author

@UncleGrumpy UncleGrumpy Feb 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I’m am inclined to rework the config, and use the offsets and partition lengths from the partitions.csv that the IDF is using for the partition table.

That should be our source of truth for all partition related data.

@UncleGrumpy UncleGrumpy force-pushed the mkimage-fix branch 3 times, most recently from c0604b3 to 9faef56 Compare February 10, 2026 22:14
* Fixes an FD leak, the image file was never closed properly after writing.
* Fixes a misleading error message when a binary overflows its partition.
* Avoids possibly reading possibly corrupt old data beyond the end of the last
segment written by padding the remainder of the last sector (flash sectors are
4096 blocks in length) with 0xff. Having this buffer marked as erased between
the end of the partition data and any bits left on the device from previously
flashed projects should prevent accidentally ace ping this as part of the
partition data. Also fixes and unaligned end of data problems that can prevent
flashing with a web-flasher tool.
* Now catches errors in file:write/2 operations, rather than blindly continue
when an error is returned.

Closes atomvm#2084
Closes atomvm#2094

Signed-off-by: Winford <winford@object.stream>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

mkimage should 4-byte align/pad last segment mkimage script error message is misleading

2 participants