compose: prevent out of bounds access

Fix the following panic, seen while switching accounts:

runtime error: index out of range [4] with length 4

goroutine 6 [running]:
git.sr.ht/~rjarry/aerc/widgets.(*Composer).Focus(0xc005cfbe30?, 0x40?)
        git.sr.ht/~rjarry/aerc/widgets/compose.go:618 +0x51
git.sr.ht/~rjarry/aerc/widgets.(*Aerc).focus(0xc00034c000, {0x0?, 0x0})
        git.sr.ht/~rjarry/aerc/widgets/aerc.go:568 +0xec
git.sr.ht/~rjarry/aerc/widgets.(*Aerc).BeginExCommand.func2()
        git.sr.ht/~rjarry/aerc/widgets/aerc.go:590 +0x4c
git.sr.ht/~rjarry/aerc/widgets.(*ExLine).Event(0xc009453860, {0xbb6820?, 0xc009baa320?})
        git.sr.ht/~rjarry/aerc/widgets/exline.go:81 +0xbc
git.sr.ht/~rjarry/aerc/widgets.(*Aerc).Event(0xc009ab1950?, {0xbb6820?, 0xc009baa320?})
        git.sr.ht/~rjarry/aerc/widgets/aerc.go:285 +0x470
git.sr.ht/~rjarry/aerc/lib/ui.(*UI).ProcessEvents(0xc000327540)
        git.sr.ht/~rjarry/aerc/lib/ui/ui.go:117 +0x202
created by main.main
        git.sr.ht/~rjarry/aerc/aerc.go:244 +0x94c

Protect Composer.editable and Composer.focus with a mutex.

Reported-by: Bence Ferdinandy <bence@ferdinandy.com>
Signed-off-by: Robin Jarry <robin@jarry.cc>
Acked-by: Moritz Poldrack <moritz@poldrack.dev>
This commit is contained in:
Robin Jarry 2022-09-26 20:56:01 +02:00
parent 0e50f29bf3
commit bf2bf8c242

View file

@ -8,6 +8,7 @@ import (
"os" "os"
"os/exec" "os/exec"
"strings" "strings"
"sync"
"time" "time"
"github.com/emersion/go-message/mail" "github.com/emersion/go-message/mail"
@ -28,6 +29,7 @@ import (
) )
type Composer struct { type Composer struct {
sync.Mutex
editors map[string]*headerEditor // indexes in lower case (from / cc / bcc) editors map[string]*headerEditor // indexes in lower case (from / cc / bcc)
header *mail.Header header *mail.Header
parent models.OriginalMail // parent of current message, only set if reply parent models.OriginalMail // parent of current message, only set if reply
@ -131,6 +133,8 @@ func (c *Composer) SwitchAccount(newAcct *AccountView) error {
} }
func (c *Composer) setupFor(acct *AccountView) error { func (c *Composer) setupFor(acct *AccountView) error {
c.Lock()
defer c.Unlock()
// set new account and accountConfig // set new account and accountConfig
c.acct = acct c.acct = acct
c.acctConfig = acct.AccountConfig() c.acctConfig = acct.AccountConfig()
@ -524,6 +528,8 @@ func (c *Composer) readSignatureFromFile() []byte {
} }
func (c *Composer) FocusTerminal() *Composer { func (c *Composer) FocusTerminal() *Composer {
c.Lock()
defer c.Unlock()
if c.editor == nil { if c.editor == nil {
return c return c
} }
@ -587,6 +593,8 @@ func (c *Composer) Close() {
} }
func (c *Composer) Bindings() string { func (c *Composer) Bindings() string {
c.Lock()
defer c.Unlock()
switch c.editor { switch c.editor {
case nil: case nil:
return "compose::review" return "compose::review"
@ -598,6 +606,8 @@ func (c *Composer) Bindings() string {
} }
func (c *Composer) Event(event tcell.Event) bool { func (c *Composer) Event(event tcell.Event) bool {
c.Lock()
defer c.Unlock()
if c.editor != nil { if c.editor != nil {
return c.focusable[c.focused].Event(event) return c.focusable[c.focused].Event(event)
} }
@ -605,6 +615,8 @@ func (c *Composer) Event(event tcell.Event) bool {
} }
func (c *Composer) MouseEvent(localX int, localY int, event tcell.Event) { func (c *Composer) MouseEvent(localX int, localY int, event tcell.Event) {
c.Lock()
defer c.Unlock()
c.grid.MouseEvent(localX, localY, event) c.grid.MouseEvent(localX, localY, event)
for _, e := range c.focusable { for _, e := range c.focusable {
he, ok := e.(*headerEditor) he, ok := e.(*headerEditor)
@ -615,7 +627,9 @@ func (c *Composer) MouseEvent(localX int, localY int, event tcell.Event) {
} }
func (c *Composer) Focus(focus bool) { func (c *Composer) Focus(focus bool) {
c.Lock()
c.focusable[c.focused].Focus(focus) c.focusable[c.focused].Focus(focus)
c.Unlock()
} }
func (c *Composer) Config() *config.AccountConfig { func (c *Composer) Config() *config.AccountConfig {
@ -873,6 +887,8 @@ func (c *Composer) termEvent(event tcell.Event) bool {
} }
func (c *Composer) termClosed(err error) { func (c *Composer) termClosed(err error) {
c.Lock()
defer c.Unlock()
if c.editor == nil { if c.editor == nil {
return return
} }
@ -888,6 +904,8 @@ func (c *Composer) termClosed(err error) {
} }
func (c *Composer) ShowTerminal() { func (c *Composer) ShowTerminal() {
c.Lock()
defer c.Unlock()
if c.editor != nil { if c.editor != nil {
return return
} }
@ -910,6 +928,8 @@ func (c *Composer) ShowTerminal() {
} }
func (c *Composer) PrevField() { func (c *Composer) PrevField() {
c.Lock()
defer c.Unlock()
c.focusable[c.focused].Focus(false) c.focusable[c.focused].Focus(false)
c.focused-- c.focused--
if c.focused == -1 { if c.focused == -1 {
@ -919,12 +939,16 @@ func (c *Composer) PrevField() {
} }
func (c *Composer) NextField() { func (c *Composer) NextField() {
c.Lock()
defer c.Unlock()
c.focusable[c.focused].Focus(false) c.focusable[c.focused].Focus(false)
c.focused = (c.focused + 1) % len(c.focusable) c.focused = (c.focused + 1) % len(c.focusable)
c.focusable[c.focused].Focus(true) c.focusable[c.focused].Focus(true)
} }
func (c *Composer) FocusEditor(editor string) { func (c *Composer) FocusEditor(editor string) {
c.Lock()
defer c.Unlock()
editor = strings.ToLower(editor) editor = strings.ToLower(editor)
c.focusable[c.focused].Focus(false) c.focusable[c.focused].Focus(false)
for i, f := range c.focusable { for i, f := range c.focusable {
@ -939,6 +963,8 @@ func (c *Composer) FocusEditor(editor string) {
// AddEditor appends a new header editor to the compose window. // AddEditor appends a new header editor to the compose window.
func (c *Composer) AddEditor(header string, value string, appendHeader bool) { func (c *Composer) AddEditor(header string, value string, appendHeader bool) {
c.Lock()
defer c.Unlock()
var editor *headerEditor var editor *headerEditor
header = strings.ToLower(header) header = strings.ToLower(header)
if e, ok := c.editors[header]; ok { if e, ok := c.editors[header]; ok {