Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
# New in snapd 2.74.1
* FDE: measure DeployedMode and AuditMode variables if they appear as disabled in the event log to avoid a potential reseal-failure boot loop
* LP: #2141328 FDE: reuse preinstall check context during install to account for user-ignored errors
* LP: #2139611 FDE: fix db updates by allowing multiple payloads
* LP: #2139300 snap-confine: add CAP_SYS_RESOURCE to allow raising memory lock limit when required
* LP: #2139099 snap-confine: bump the max element count of the BPF map used to store IDs of allowed/matched devices to 1000
* LP: #2141607 Desktop: revert change that caused user daemons declaring the desktop plug to implicitly depend on graphical-session.target
* Interfaces: Added pidfd_open and memfd_secret to seccomp template
* Interfaces: camera | add locking permission for /dev/video

Expand Down
7 changes: 6 additions & 1 deletion mkversion.sh
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,12 @@ fi
# switch to the real source dir for the changelog parsing
: "${DPKG_PARSECHANGELOG=$(command -v dpkg-parsechangelog)}"
if [ -n "$DPKG_PARSECHANGELOG" ]; then
version_from_changelog="$(cd "$PKG_BUILDDIR"; "$DPKG_PARSECHANGELOG" --file packaging/ubuntu-16.04/changelog --show-field Version)";

changelog=debian/changelog
if [ ! -e "$changelog" ]; then
changelog=packaging/ubuntu-16.04/changelog
fi
version_from_changelog="$(cd "$PKG_BUILDDIR" && "$DPKG_PARSECHANGELOG" --file "$changelog" --show-field Version)"
fi

# select version based on priority
Expand Down
6 changes: 3 additions & 3 deletions overlord/devicestate/devicestate_install_api_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -864,7 +864,7 @@ func (s *deviceMgrInstallAPISuite) testInstallSetupStorageEncryption(c *C, isSup
gadgetSnapPath, kernelSnapPath, _, ginfo, mountCmd, _ := s.mockSystemSeedWithLabel(
c, label, seedCopyFn, seedOpts)

callCnt := mockHelperForEncryptionAvailabilityCheck(s, c, isSupportedHybrid, hasTPM)
callCnt := mockHelperForEncryptionAvailabilityCheck(s, c, isSupportedHybrid, hasTPM, label)

// Mock encryption of partitions
encrytpPartCalls := 0
Expand Down Expand Up @@ -934,7 +934,7 @@ func (s *deviceMgrInstallAPISuite) testInstallSetupStorageEncryption(c *C, isSup

// ensure the expected encryption availability check was used
if isSupportedHybrid {
c.Assert(callCnt, DeepEquals, &callCounter{checkCnt: 1, checkActionCnt: 0, sealingSupportedCnt: 0})
c.Assert(callCnt, DeepEquals, &callCounter{checkCnt: 0, checkActionCnt: 1, sealingSupportedCnt: 0})
} else {
c.Assert(callCnt, DeepEquals, &callCounter{checkCnt: 0, checkActionCnt: 0, sealingSupportedCnt: 1})
}
Expand Down Expand Up @@ -1170,7 +1170,7 @@ func (s *deviceMgrInstallAPISuite) testInstallSetupStorageEncryptionPassphraseAu
s.state.Lock()
defer s.state.Unlock()

callCnt := mockHelperForEncryptionAvailabilityCheck(s, c, true, true)
callCnt := mockHelperForEncryptionAvailabilityCheck(s, c, true, true, "")

restore := devicestate.MockInstallEncryptPartitions(func(
onVolumes map[string]*gadget.Volume,
Expand Down
22 changes: 11 additions & 11 deletions overlord/devicestate/devicestate_install_mode_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -360,7 +360,7 @@ func (s *deviceMgrInstallModeSuite) SetUpTest(c *C) {
})
s.AddCleanup(restore)

mockHelperForEncryptionAvailabilityCheck(s, c, false, false)
mockHelperForEncryptionAvailabilityCheck(s, c, false, false, "")

s.state.Lock()
defer s.state.Unlock()
Expand Down Expand Up @@ -648,7 +648,7 @@ func (s *deviceMgrInstallModeSuite) doRunChangeTestWithEncryption(c *C, grade st
})
defer restore()

callCnt := mockHelperForEncryptionAvailabilityCheck(s, c, false, tc.tpm)
callCnt := mockHelperForEncryptionAvailabilityCheck(s, c, false, tc.tpm, "")

if tc.trustedBootloader {
tab := bootloadertest.Mock("trusted", bootloaderRootdir).WithTrustedAssets()
Expand Down Expand Up @@ -1781,7 +1781,7 @@ func (s *deviceMgrInstallModeSuite) TestInstallBootloaderVarSetFails(c *C) {
})
defer restore()

callCnt := mockHelperForEncryptionAvailabilityCheck(s, c, false, false)
callCnt := mockHelperForEncryptionAvailabilityCheck(s, c, false, false, "")

err := os.WriteFile(filepath.Join(dirs.GlobalRootDir, "/var/lib/snapd/modeenv"),
[]byte("mode=install\nrecovery_system=1234"), 0644)
Expand Down Expand Up @@ -1823,7 +1823,7 @@ func (s *deviceMgrInstallModeSuite) testInstallEncryptionValidityChecks(c *C, er
restore := release.MockOnClassic(false)
defer restore()

callCnt := mockHelperForEncryptionAvailabilityCheck(s, c, false, true)
callCnt := mockHelperForEncryptionAvailabilityCheck(s, c, false, true, "")

err := os.WriteFile(filepath.Join(dirs.GlobalRootDir, "/var/lib/snapd/modeenv"),
[]byte("mode=install\n"), 0644)
Expand Down Expand Up @@ -2010,7 +2010,7 @@ func (s *deviceMgrInstallModeSuite) TestInstallWithEncryptionValidatesGadgetErr(
defer restore()

// pretend we have a TPM
callCnt := mockHelperForEncryptionAvailabilityCheck(s, c, false, true)
callCnt := mockHelperForEncryptionAvailabilityCheck(s, c, false, true, "")

// must be a model that requires encryption to error
s.testInstallGadgetNoSave(c, "secured")
Expand Down Expand Up @@ -2041,7 +2041,7 @@ func (s *deviceMgrInstallModeSuite) TestInstallWithEncryptionValidatesGadgetWarn
defer restore()

// pretend we have a TPM
callCnt := mockHelperForEncryptionAvailabilityCheck(s, c, false, true)
callCnt := mockHelperForEncryptionAvailabilityCheck(s, c, false, true, "")

s.testInstallGadgetNoSave(c, "dangerous")

Expand All @@ -2067,7 +2067,7 @@ func (s *deviceMgrInstallModeSuite) TestInstallWithoutEncryptionValidatesGadgetW
defer restore()

// pretend we have a TPM
callCnt := mockHelperForEncryptionAvailabilityCheck(s, c, false, true)
callCnt := mockHelperForEncryptionAvailabilityCheck(s, c, false, true, "")

s.testInstallGadgetNoSave(c, "dangerous")

Expand Down Expand Up @@ -2146,7 +2146,7 @@ func (s *deviceMgrInstallModeSuite) TestInstallCheckEncrypted(c *C) {
makeInstalledMockKernelSnap(c, st, kernelYamlNoFdeSetup)
}

callCnt := mockHelperForEncryptionAvailabilityCheck(s, c, false, tc.hasTPM)
callCnt := mockHelperForEncryptionAvailabilityCheck(s, c, false, tc.hasTPM, "")

encryptionType, err := devicestate.DeviceManagerCheckEncryption(s.mgr, st, deviceCtx, secboot.TPMProvisionFull)
c.Assert(callCnt.checkCnt, Equals, 0)
Expand Down Expand Up @@ -2314,7 +2314,7 @@ func (s *deviceMgrInstallModeSuite) doRunFactoryResetChange(c *C, model *asserts
})
defer restore()

callCnt := mockHelperForEncryptionAvailabilityCheck(s, c, false, tc.tpm)
callCnt := mockHelperForEncryptionAvailabilityCheck(s, c, false, tc.tpm, "")

if tc.trustedBootloader {
tab := bootloadertest.Mock("trusted", bootloaderRootdir).WithTrustedAssets()
Expand Down Expand Up @@ -3135,7 +3135,7 @@ func (s *deviceMgrInstallModeSuite) TestFactoryResetExpectedTasks(c *C) {
restore := release.MockOnClassic(false)
defer restore()

callCnt := mockHelperForEncryptionAvailabilityCheck(s, c, false, false)
callCnt := mockHelperForEncryptionAvailabilityCheck(s, c, false, false, "")

restore = devicestate.MockInstallFactoryReset(func(mod gadget.Model, gadgetRoot string, kernelSnapInfo *install.KernelSnapInfo, device string, options install.Options, obs gadget.ContentObserver, pertTimings timings.Measurer) (*install.InstalledSystemSideData, error) {
c.Assert(os.MkdirAll(dirs.SnapDeviceDirUnder(filepath.Join(dirs.GlobalRootDir, "/run/mnt/ubuntu-data/system-data")), 0755), IsNil)
Expand Down Expand Up @@ -3207,7 +3207,7 @@ func (s *deviceMgrInstallModeSuite) TestFactoryResetInstallDeviceHook(c *C) {
restore := release.MockOnClassic(false)
defer restore()

callCnt := mockHelperForEncryptionAvailabilityCheck(s, c, false, false)
callCnt := mockHelperForEncryptionAvailabilityCheck(s, c, false, false, "")

hooksCalled := []*hookstate.Context{}
restore = hookstate.MockRunHook(func(ctx *hookstate.Context, tomb *tomb.Tomb) ([]byte, error) {
Expand Down
33 changes: 27 additions & 6 deletions overlord/devicestate/devicestate_systems_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2983,6 +2983,7 @@ var preinstallAction = &secboot.PreinstallAction{

type suiteWithAddCleanup interface {
AddCleanup(func())
DeviceManager() *devicestate.DeviceManager
}

type callCounter struct {
Expand All @@ -2998,7 +2999,8 @@ type callCounter struct {
//
// isSupportedUbuntuHybrid: modify system release information and place current boot images to simulate supported Ubuntu hybrid install
// hasTPM: indicates if we should simulate having a TPM (no error detected) or no TPM (some representative error)
func mockHelperForEncryptionAvailabilityCheck(s suiteWithAddCleanup, c *C, isSupportedUbuntuHybrid, hasTPM bool) *callCounter {
// cacheLabel: system label to use to cache check context where "" means do not cache check context
func mockHelperForEncryptionAvailabilityCheck(s suiteWithAddCleanup, c *C, isSupportedUbuntuHybrid, hasTPM bool, cacheLabel string) *callCounter {
callCnt := &callCounter{}

releaseInfo := &release.OS{
Expand Down Expand Up @@ -3035,6 +3037,21 @@ func mockHelperForEncryptionAvailabilityCheck(s suiteWithAddCleanup, c *C, isSup
}
}

if cacheLabel != "" {
// populate the cache with encryption support information that
// mimics what would happen when a preinstall check takes place
// as per usual installer flow
encInfo := &install.EncryptionSupportInfo{}
if isSupportedUbuntuHybrid {
// hybrid installation flow populates the preinstall check context
encInfo.SetAvailabilityCheckContext(&secboot.PreinstallCheckContext{})
}
// non-hydrid install flow uses the simple availability check that does not produce
// a preinstall check context

s.DeviceManager().SetEncryptionSupportInfoInCacheUnlocked(cacheLabel, encInfo)
}

restore := install.MockSecbootPreinstallCheck(func(ctx context.Context, bootImagePaths []string) (*secboot.PreinstallCheckContext, []secboot.PreinstallErrorDetails, error) {
callCnt.checkCnt++
c.Assert(bootImagePaths, HasLen, 3)
Expand All @@ -3051,7 +3068,11 @@ func mockHelperForEncryptionAvailabilityCheck(s suiteWithAddCleanup, c *C, isSup
callCnt.checkActionCnt++
c.Assert(pcc, NotNil)
c.Assert(ctx, NotNil)
c.Assert(action, DeepEquals, preinstallAction)
if cacheLabel != "" {
c.Assert(action, DeepEquals, &secboot.PreinstallAction{Action: secboot.ActionNone})
} else {
c.Assert(action, DeepEquals, preinstallAction)
}
c.Assert(isSupportedUbuntuHybrid, Equals, true)

if hasTPM {
Expand Down Expand Up @@ -3160,7 +3181,7 @@ func (s *modelAndGadgetInfoSuite) TestSystemAndGadgetAndEncryptionInfoNotSupport
UnavailableWarning: "not encrypting device storage as checking TPM gave: cannot connect to TPM device",
}

callCnt := mockHelperForEncryptionAvailabilityCheck(s, c, isSupportedHybrid, false)
callCnt := mockHelperForEncryptionAvailabilityCheck(s, c, isSupportedHybrid, false, "")

// basic availability check - fill empty info cache
encInfoFromCache := false
Expand Down Expand Up @@ -3211,7 +3232,7 @@ func (s *modelAndGadgetInfoSuite) TestSystemAndGadgetAndEncryptionInfoSupportedH
}
expectedEncInfo.SetAvailabilityCheckContext(preinstallCheckContext)

callCnt := mockHelperForEncryptionAvailabilityCheck(s, c, isSupportedHybrid, false)
callCnt := mockHelperForEncryptionAvailabilityCheck(s, c, isSupportedHybrid, false, "")

// comprehensive preinstall check - fill empty info cache
encInfoFromCache := false
Expand Down Expand Up @@ -3298,7 +3319,7 @@ func (s *modelAndGadgetInfoSuite) testSystemAndGadgetAndEncryptionInfoPassphrase
PassphraseAuthAvailable: hasPassphraseSupport,
}

callCnt := mockHelperForEncryptionAvailabilityCheck(s, c, false, true)
callCnt := mockHelperForEncryptionAvailabilityCheck(s, c, false, true, "")

// refresh empty cache
encInfoFromCache := false
Expand Down Expand Up @@ -3431,7 +3452,7 @@ func (s *modelAndGadgetInfoSuite) TestSystemAndGadgetInfoBadClassicGadget(c *C)
isClassic := true
s.makeMockUC20SeedWithGadgetYaml(c, "some-label", mockGadgetUCYamlNoBootRole, isClassic, nil)

callCnt := mockHelperForEncryptionAvailabilityCheck(s, c, true, true)
callCnt := mockHelperForEncryptionAvailabilityCheck(s, c, true, true, "")

_, _, _, err := s.mgr.SystemAndGadgetAndEncryptionInfo("some-label", false)
c.Assert(callCnt, DeepEquals, &callCounter{checkCnt: 1, checkActionCnt: 0, sealingSupportedCnt: 0})
Expand Down
4 changes: 4 additions & 0 deletions overlord/devicestate/devicestate_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -510,6 +510,10 @@ func (s *deviceMgrBaseSuite) addKeyToManagerInState(c *C) {
c.Assert(err, IsNil)
}

func (s *deviceMgrBaseSuite) DeviceManager() *devicestate.DeviceManager {
return s.mgr
}

func (s *deviceMgrSuite) SetUpTest(c *C) {
classic := false
s.setupBaseTest(c, classic)
Expand Down
12 changes: 12 additions & 0 deletions overlord/devicestate/handlers_install.go
Original file line number Diff line number Diff line change
Expand Up @@ -1370,12 +1370,24 @@ func (m *DeviceManager) doInstallSetupStorageEncryption(t *state.Task, _ *tomb.T
return fmt.Errorf("reading gadget information: %v", err)
}

var checkAction *secboot.PreinstallAction
cachedEncInfo := m.readCacheEncryptionSupportInfoLocked(systemLabel)

if cachedEncInfo != nil && cachedEncInfo.CheckContext() != nil {
// If a cached context is available, the preinstall check was previously run
// and must be repeated. Set an action to signal reuse of the preinstall check
// context. Use the inert "ActionNone" to repeat the check without requesting
// any modifications.
checkAction = &secboot.PreinstallAction{Action: secboot.ActionNone}
}

constraints := installLogic.EncryptionConstraints{
Model: systemAndSeeds.Model,
Kernel: systemAndSeeds.InfosByType[snap.TypeKernel],
Gadget: gadgetInfo,
TPMMode: secboot.TPMProvisionFull,
SnapdVersions: systemAndSeeds.SystemSnapdVersions,
CheckAction: checkAction,
}

// do not use cached encryption information; perform a fresh encryption
Expand Down
7 changes: 6 additions & 1 deletion packaging/debian-sid/changelog
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,20 @@ snapd (2.74.1-1) unstable; urgency=medium
- FDE: measure DeployedMode and AuditMode variables if they appear
as disabled in the event log to avoid a potential reseal-failure
boot loop
- LP: #2141328 FDE: reuse preinstall check context during install to
account for user-ignored errors
- LP: #2139611 FDE: fix db updates by allowing multiple payloads
- LP: #2139300 snap-confine: add CAP_SYS_RESOURCE to allow raising
memory lock limit when required
- LP: #2139099 snap-confine: bump the max element count of the BPF
map used to store IDs of allowed/matched devices to 1000
- LP: #2141607 Desktop: revert change that caused user daemons
declaring the desktop plug to implicitly depend on graphical-
session.target
- Interfaces: Added pidfd_open and memfd_secret to seccomp template
- Interfaces: camera | add locking permission for /dev/video

-- Ernest Lotter <ernest.lotter@canonical.com> Fri, 06 Feb 2026 19:40:03 +0200
-- Ernest Lotter <ernest.lotter@canonical.com> Thu, 12 Feb 2026 21:27:23 +0200

snapd (2.74-1) unstable; urgency=medium

Expand Down
7 changes: 6 additions & 1 deletion packaging/fedora/snapd.spec
Original file line number Diff line number Diff line change
Expand Up @@ -1008,16 +1008,21 @@ fi
%endif

%changelog
* Fri Feb 06 2026 Ernest Lotter <ernest.lotter@canonical.com>
* Thu Feb 12 2026 Ernest Lotter <ernest.lotter@canonical.com>
- New upstream release 2.74.1
- FDE: measure DeployedMode and AuditMode variables if they appear
as disabled in the event log to avoid a potential reseal-failure
boot loop
- LP: #2141328 FDE: reuse preinstall check context during install to
account for user-ignored errors
- LP: #2139611 FDE: fix db updates by allowing multiple payloads
- LP: #2139300 snap-confine: add CAP_SYS_RESOURCE to allow raising
memory lock limit when required
- LP: #2139099 snap-confine: bump the max element count of the BPF
map used to store IDs of allowed/matched devices to 1000
- LP: #2141607 Desktop: revert change that caused user daemons
declaring the desktop plug to implicitly depend on graphical-
session.target
- Interfaces: Added pidfd_open and memfd_secret to seccomp template
- Interfaces: camera | add locking permission for /dev/video

Expand Down
2 changes: 1 addition & 1 deletion packaging/opensuse/snapd.changes
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
-------------------------------------------------------------------
Fri Feb 06 17:40:03 UTC 2026 - ernest.lotter@canonical.com
Thu Feb 12 19:27:23 UTC 2026 - ernest.lotter@canonical.com

- Update to upstream release 2.74.1

Expand Down
7 changes: 6 additions & 1 deletion packaging/ubuntu-16.04/changelog
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,20 @@ snapd (2.74.1) xenial; urgency=medium
- FDE: measure DeployedMode and AuditMode variables if they appear
as disabled in the event log to avoid a potential reseal-failure
boot loop
- LP: #2141328 FDE: reuse preinstall check context during install to
account for user-ignored errors
- LP: #2139611 FDE: fix db updates by allowing multiple payloads
- LP: #2139300 snap-confine: add CAP_SYS_RESOURCE to allow raising
memory lock limit when required
- LP: #2139099 snap-confine: bump the max element count of the BPF
map used to store IDs of allowed/matched devices to 1000
- LP: #2141607 Desktop: revert change that caused user daemons
declaring the desktop plug to implicitly depend on graphical-
session.target
- Interfaces: Added pidfd_open and memfd_secret to seccomp template
- Interfaces: camera | add locking permission for /dev/video

-- Ernest Lotter <ernest.lotter@canonical.com> Fri, 06 Feb 2026 19:40:03 +0200
-- Ernest Lotter <ernest.lotter@canonical.com> Thu, 12 Feb 2026 21:27:23 +0200

snapd (2.74) xenial; urgency=medium

Expand Down
2 changes: 2 additions & 0 deletions secboot/preinstall_nosb.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ import (
type PreinstallCheckContext struct{}
type PreinstallCheckResult struct{}

const ActionNone = ""

func PreinstallCheck(ctx context.Context, bootImagePaths []string) (*PreinstallCheckContext, []PreinstallErrorDetails, error) {
return nil, nil, errBuildWithoutSecboot
}
Expand Down
2 changes: 2 additions & 0 deletions secboot/preinstall_sb.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@ var (
sbPreinstallRunChecks = (*sb_preinstall.RunChecksContext).Run
)

const ActionNone = string(sb_preinstall.ActionNone)

// PreinstallCheck runs preinstall checks using default check configuration and
// TCG-compliant PCR profile generation options to evaluate whether the host
// environment is an EFI system suitable for TPM-based Full Disk Encryption. The
Expand Down
13 changes: 2 additions & 11 deletions snap/info.go
Original file line number Diff line number Diff line change
Expand Up @@ -1356,23 +1356,14 @@ type AppInfo struct {

// list of other service names that this service will start after or
// before
After []string
Before []string
BindsTo []string
PartOf []string
Requisite []string
After []string
Before []string

Timer *TimerInfo

Autostart string
}

// HasPlug returns true if this AppInfo has the requested plug.
func (app *AppInfo) HasPlug(plug string) bool {
_, ok := app.Plugs[plug]
return ok
}

// Runnable returns a Runnable for this app.
func (app *AppInfo) Runnable() Runnable {
return Runnable{
Expand Down
Loading
Loading