Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider: - [Medium] fbcon: race in fbcon_del_cursor_work() lets fb_flashcursor() re-arm work --
commit e9056993ba6eca270987e2b77d6a3977beffec71 Author: Yao Yiqi <[email protected]> fbcon: Avoid cancel_delayed_work_sync() in atomic context This commit replaces cancel_delayed_work_sync() with cancel_delayed_work() in fbcon_del_cursor_work() to avoid sleeping in atomic console printing contexts, while adding fbcon_del_cursor_work_sync() for teardown in fbcon_release() and moving the delayed work requeue in fb_flashcursor() inside console_lock. > diff --git a/drivers/video/fbdev/core/fbcon.c > b/drivers/video/fbdev/core/fbcon.c > index 7720dfc80c165..066cd974ea3bb 100644 > --- a/drivers/video/fbdev/core/fbcon.c > +++ b/drivers/video/fbdev/core/fbcon.c > @@ -422,10 +422,14 @@ static void fb_flashcursor(struct work_struct *work) > par->bitops->cursor(vc, info, enable, > get_fg_color(vc, info, c), > get_bg_color(vc, info, c)); > - console_unlock(); > > + /* > + * Re-arm under console_lock so that a concurrent > + * fbcon_del_cursor_work() cannot race with this requeue. > + */ > queue_delayed_work(system_power_efficient_wq, &par->cursor_work, > par->cur_blink_jiffies); > + console_unlock(); > } > > static void fbcon_add_cursor_work(struct fb_info *info) > @@ -437,10 +441,30 @@ static void fbcon_add_cursor_work(struct fb_info *info) > par->cur_blink_jiffies); > } > > +/* > + * Stop the cursor blink. > + * > + * Does not wait for a callback that is already running, so it is safe to > + * call from any context. > + * > + * console_lock serializes this with the requeue in fb_flashcursor(); > + * otherwise a running callback could re-arm the work. > + */ > static void fbcon_del_cursor_work(struct fb_info *info) > { > struct fbcon_par *par = info->fbcon_par; > > + cancel_delayed_work(&par->cursor_work); > +} [Severity: Medium] Can fb_flashcursor() still race with fbcon_del_cursor_work() and re-arm par->cursor_work after blinking has been stopped? When par->cursor_work expires, process_one_work() clears WORK_STRUCT_PENDING_BIT before invoking fb_flashcursor(). If a caller holding console_lock (such as cursor_blink_store(), fbcon_blank(), fbcon_switch(), fbcon_deinit(), or fbcon_cursor() with CUR_SW) runs fbcon_del_cursor_work() after WORK_STRUCT_PENDING_BIT is cleared, but before the worker executes console_trylock() in fb_flashcursor(): CPU0 (holding console_lock) CPU1 (worker thread) process_one_work() /* clears WORK_STRUCT_PENDING_BIT */ fb_flashcursor() fbcon_del_cursor_work() cancel_delayed_work(&par->cursor_work) /* returns false without waiting */ console_unlock() ret = console_trylock(); /* succeeds */ Because fb_flashcursor() only checks: if (!vc || !con_is_visible(vc) || fbcon_info_from_console(vc->vc_num) != info || vc->vc_deccm != 1) { console_unlock(); return; } and does not verify whether blinking is still supposed to be active (for example fbcon_cursor_blink, vc->vc_cursor_type & CUR_SW,par->graphics / KD_GRAPHICS, par->blank_state, or par->initialized), won't it draw the cursor and call queue_delayed_work() to perpetually re-arm par->cursor_work? -- Sashiko AI review ยท https://sashiko.dev/#/patchset/[email protected]?part=1
