This is an automated email from the ASF dual-hosted git repository. sjanc pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/mynewt-newt.git
commit 8ac3f3d9c8ca4ea7246e51648f9d61cbbdd1a867 Author: Szymon Janc <[email protected]> AuthorDate: Mon Oct 19 17:53:27 2026 +0200 newt/mfg: Detect overlaps of the meta region The mfg meta region is written at the end of its flash area on top of the image contents, but detectOverlaps() only checked raw and target parts against each other. A meta region overlapping a part silently overwrote the last bytes of that part. Include the meta region in overlap detection. Assisted-by: Claude:claude-opus-5-5 --- newt/mfg/build.go | 24 +++++++++- newt/mfg/build_test.go | 123 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 146 insertions(+), 1 deletion(-) diff --git a/newt/mfg/build.go b/newt/mfg/build.go index 648dc0d5..66886a27 100644 --- a/newt/mfg/build.go +++ b/newt/mfg/build.go @@ -372,6 +372,21 @@ func (mb *MfgBuilder) detectOverlaps() error { return err } + // The meta region is written over the image contents, so it must not + // overlap any other part either. + if mb.Meta != nil { + meta, err := mb.buildMeta() + if err != nil { + return err + } + parts = append(parts, Part{ + Name: fmt.Sprintf("meta (%s)", mb.Meta.Area.Name), + Offset: mb.metaOffset(meta), + Data: make([]byte, meta.Size()), + }) + parts = SortParts(parts) + } + for i, p1 := range parts[:len(parts)-1] { p1end := p1.Offset + len(p1.Data) for _, p2 := range parts[i+1:] { @@ -588,6 +603,13 @@ func (mb *MfgBuilder) buildMeta() (mfg.Meta, error) { return meta, nil } +// Calculates the offset of the meta region within the manufacturing image. +// The meta region is located at the end of its flash area. +func (mb *MfgBuilder) metaOffset(meta mfg.Meta) int { + return mb.Meta.Area.Offset + mb.Meta.Area.Size - meta.Size() - + mb.BaseAddress +} + // Builds a manufacturing image. func (mb *MfgBuilder) Build() (mfg.Mfg, error) { parts, err := mb.parts() @@ -608,7 +630,7 @@ func (mb *MfgBuilder) Build() (mfg.Mfg, error) { return mfg.Mfg{}, err } metap = &meta - metaOff = mb.Meta.Area.Offset + mb.Meta.Area.Size - meta.Size() - mb.BaseAddress + metaOff = mb.metaOffset(meta) } return mfg.Mfg{ diff --git a/newt/mfg/build_test.go b/newt/mfg/build_test.go new file mode 100644 index 00000000..5db104a2 --- /dev/null +++ b/newt/mfg/build_test.go @@ -0,0 +1,123 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package mfg + +import ( + "os" + "path/filepath" + "strings" + "testing" + + "github.com/apache/mynewt-artifact/flash" + "mynewt.apache.org/newt/newt/flashmap" + "mynewt.apache.org/newt/newt/pkg" +) + +var testAreaBoot = flash.FlashArea{ + Name: "FLASH_AREA_BOOTLOADER", + Id: 0, + Device: 0, + Offset: 0x0, + Size: 0x4000, +} + +var testAreaImage = flash.FlashArea{ + Name: "FLASH_AREA_IMAGE_0", + Id: 1, + Device: 0, + Offset: 0x4000, + Size: 0x8000, +} + +// testRaw creates a raw entry with a file of the given size placed in the +// given area. +func testRaw(t *testing.T, area flash.FlashArea, offset int, + size int) MfgBuildRaw { + + t.Helper() + + path := filepath.Join(t.TempDir(), "raw.bin") + if err := os.WriteFile(path, make([]byte, size), 0644); err != nil { + t.Fatal(err) + } + + return MfgBuildRaw{ + Filename: path, + Offset: offset, + Size: size, + Area: area, + } +} + +func testBuilder(meta *MfgBuildMeta, raws ...MfgBuildRaw) MfgBuilder { + fm := flashmap.FlashMap{ + Areas: map[string]flash.FlashArea{}, + } + for _, r := range raws { + fm.Areas[r.Area.Name] = r.Area + } + if meta != nil { + fm.Areas[meta.Area.Name] = meta.Area + } + + return MfgBuilder{ + Bsp: &pkg.BspPackage{FlashMap: fm}, + Raws: raws, + Meta: meta, + } +} + +// The meta region is located at the end of its area and must not overlap +// any part of the image. +func TestDetectOverlapsMeta(t *testing.T) { + meta := &MfgBuildMeta{ + Area: testAreaBoot, + Hash: true, + FlashMap: true, + } + + // Raw part followed by the meta region in the same area. + mb := testBuilder(meta, testRaw(t, testAreaBoot, 0, 0x100), + testRaw(t, testAreaImage, 0, testAreaImage.Size)) + if err := mb.detectOverlaps(); err != nil { + t.Errorf("unexpected error: %s", err) + } + + // Raw part filling the whole area of the meta region. + mb = testBuilder(meta, testRaw(t, testAreaBoot, 0, testAreaBoot.Size)) + err := mb.detectOverlaps() + if err == nil { + t.Fatal("overlap of meta region not detected") + } + if !strings.Contains(err.Error(), "meta (FLASH_AREA_BOOTLOADER)") { + t.Errorf("unexpected error: %s", err) + } + + // Meta region in an area which is fully used by a raw part. + meta = &MfgBuildMeta{ + Area: testAreaImage, + Hash: true, + } + mb = testBuilder(meta, testRaw(t, testAreaBoot, 0, 0x100), + testRaw(t, testAreaImage, 0, testAreaImage.Size)) + if err := mb.detectOverlaps(); err == nil { + t.Fatal("overlap of meta region not detected") + } +}
