diff options
author | Robin Jarry <robin@jarry.cc> | 2022-06-20 21:49:05 +0200 |
---|---|---|
committer | Robin Jarry <robin@jarry.cc> | 2022-06-24 21:08:12 +0200 |
commit | 420f236d31da76df9982331c596b776ab3d0dd76 (patch) | |
tree | df1c0ee7612400aefd08f0ce260bbb421d50b491 /worker/imap/worker.go | |
parent | 389c0f82a7f5dc250500fcff6570308cc3bcbd88 (diff) | |
download | aerc-420f236d31da76df9982331c596b776ab3d0dd76.tar.gz |
imap: fix data race on seqMap array
There are concurrent threads that are accessing and modifying
IMAPWorker.seqMap (the mapping of sequence numbers to message UIDs).
This can lead to crashes when trying to add and remove a message ID.
panic: runtime error: index out of range [391] with length 390
goroutine 1834 [running]:
git.sr.ht/~rjarry/aerc/logging.PanicHandler()
logging/panic-logger.go:47 +0x6de
panic({0xa41760, 0xc0019b3290})
/usr/lib/golang/src/runtime/panic.go:838 +0x207
git.sr.ht/~rjarry/aerc/worker/imap.(*IMAPWorker).handleFetchMessages.func1()
worker/imap/fetch.go:214 +0x185
created by git.sr.ht/~rjarry/aerc/worker/imap.(*IMAPWorker).handleFetchMessages
worker/imap/fetch.go:209 +0x12b
Use a map which makes more sense than a simple array for random access
operations. Also, it allows better typing for the key values. Protect
the map with a mutex. Add internal API to access the map. Add basic unit
tests to ensure that concurrent access works.
Fixes: https://todo.sr.ht/~rjarry/aerc/49
Signed-off-by: Robin Jarry <robin@jarry.cc>
Acked-by: Moritz Poldrack <moritz@poldrack.dev>
Diffstat (limited to 'worker/imap/worker.go')
-rw-r--r-- | worker/imap/worker.go | 31 |
1 files changed, 11 insertions, 20 deletions
diff --git a/worker/imap/worker.go b/worker/imap/worker.go index e890bb8d..7debd883 100644 --- a/worker/imap/worker.go +++ b/worker/imap/worker.go @@ -61,8 +61,7 @@ type IMAPWorker struct { selected *imap.MailboxStatus updates chan client.Update worker *types.Worker - // Map of sequence numbers to UIDs, index 0 is seq number 1 - seqMap []uint32 + seqMap SeqMap idler *idler observer *observer @@ -212,12 +211,6 @@ func (w *IMAPWorker) handleMessage(msg types.WorkerMessage) error { func (w *IMAPWorker) handleImapUpdate(update client.Update) { w.worker.Logger.Printf("(= %T", update) - checkBounds := func(idx, size int) bool { - if idx < 0 || idx >= size { - return false - } - return true - } switch update := update.(type) { case *client.MailboxUpdate: status := update.Mailbox @@ -238,11 +231,12 @@ func (w *IMAPWorker) handleImapUpdate(update client.Update) { case *client.MessageUpdate: msg := update.Message if msg.Uid == 0 { - if ok := checkBounds(int(msg.SeqNum)-1, len(w.seqMap)); !ok { - w.worker.Logger.Println("MessageUpdate error: index out of range") + if uid, found := w.seqMap.Get(msg.SeqNum); !found { + w.worker.Logger.Printf("MessageUpdate unknown seqnum: %v", msg.SeqNum) return + } else { + msg.Uid = uid } - msg.Uid = w.seqMap[msg.SeqNum-1] } w.worker.PostMessage(&types.MessageInfo{ Info: &models.MessageInfo{ @@ -254,16 +248,13 @@ func (w *IMAPWorker) handleImapUpdate(update client.Update) { }, }, nil) case *client.ExpungeUpdate: - i := update.SeqNum - 1 - if ok := checkBounds(int(i), len(w.seqMap)); !ok { - w.worker.Logger.Println("ExpungeUpdate error: index out of range") - return + if uid, found := w.seqMap.Pop(update.SeqNum); !found { + w.worker.Logger.Printf("ExpungeUpdate unknown seqnum: %v", update.SeqNum) + } else { + w.worker.PostMessage(&types.MessagesDeleted{ + Uids: []uint32{uid}, + }, nil) } - uid := w.seqMap[i] - w.seqMap = append(w.seqMap[:i], w.seqMap[i+1:]...) - w.worker.PostMessage(&types.MessagesDeleted{ - Uids: []uint32{uid}, - }, nil) } } |