Skip to content

Commit a94d194

Browse files
committed
o/i/apparmorprompting: fix bug in notice backend when re-recording notice
When re-recording a notice which was expired at the time, there was a bug where the new notice would be added to the ID map, then all previously-expired notice IDs were removed from the map, so the newly-added ID would be removed as well. Fix that bug and add a test for it. Additionally, return early if there are no notices to be added, and remove unnecessary debug log. Signed-off-by: Oliver Calder <oliver.calder@canonical.com>
1 parent f7cbe3c commit a94d194

2 files changed

Lines changed: 23 additions & 2 deletions

File tree

overlord/ifacestate/apparmorprompting/noticebackend.go

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -208,7 +208,9 @@ type addNoticesInfo struct {
208208
// The notice key equals the info's prompt/rule ID, and the notice ID and type
209209
// are derived from the receiver.
210210
func (ntb *noticeTypeBackend) addNotices(infos []addNoticesInfo) error {
211-
logger.Debugf("called addNotices() with %d notice infos", len(infos))
211+
if len(infos) == 0 {
212+
return nil
213+
}
212214
ntb.rwmu.Lock()
213215
defer ntb.rwmu.Unlock()
214216

@@ -319,13 +321,17 @@ func (ntb *noticeTypeBackend) doAddNotice(userID uint32, id prompting.IDType, da
319321
newUserNotices := appendNotice(userNotices, newNotice, existingIndex, expiredCount)
320322

321323
ntb.userNotices[userID] = newUserNotices
322-
ntb.idToNotice[noticeID] = newNotice
323324

325+
// Delete expired notices from the ID map before setting the new notice,
326+
// in case an expired notice shares the same ID as the new notice (i.e.
327+
// when re-recording an expired notice).
324328
expiredNotices := userNotices[:expiredCount]
325329
for _, expiredNotice := range expiredNotices {
326330
delete(ntb.idToNotice, expiredNotice.ID())
327331
}
328332

333+
ntb.idToNotice[noticeID] = newNotice
334+
329335
rollback = func() {
330336
ntb.userNotices[userID] = userNotices
331337
if existingNotice != nil {

overlord/ifacestate/apparmorprompting/noticebackend_test.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -409,6 +409,12 @@ func (s *noticebackendSuite) TestAddNoticeAllExpired(c *C) {
409409
// Check that the nesly (re-)recorded notice is the only one
410410
c.Check(notices, HasLen, 1, Commentf("trying to add notice %s", id))
411411
c.Check(notices[0].Key(), Equals, id.String())
412+
413+
// Check that the re-recorded notice is retrievable via BackendNotice,
414+
// which uses the ID map. This catches a bug where re-recording an
415+
// expired notice could leave it absent from the ID map.
416+
noticeID := "prompt-" + id.String()
417+
c.Check(promptBackend.BackendNotice(noticeID), NotNil, Commentf("could not find re-recorded notice with ID %s via BackendNotice", id))
412418
}
413419
}
414420

@@ -540,6 +546,15 @@ func (s *noticebackendSuite) TestAddNotices(c *C) {
540546
c.Check(afterNotices[i].LastData(), HasLen, 0)
541547
}
542548
}
549+
550+
// Check that all notices, including re-recorded expired ones, are
551+
// retrievable via BackendNotice (which uses the ID map). This catches
552+
// a bug where re-recording an expired notice could leave it absent from
553+
// the ID map if expired notice cleanup ran after setting the new notice.
554+
for _, id := range []uint32{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11} {
555+
noticeID := fmt.Sprintf("rule-%016X", id)
556+
c.Check(ruleBackend.BackendNotice(noticeID), NotNil, Commentf("could not find notice with ID %d via BackendNotice", id))
557+
}
543558
}
544559

545560
func (s *noticebackendSuite) TestAddNoticesErrors(c *C) {

0 commit comments

Comments
 (0)