[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Minios-devel] [UNIKRAFT PATCH] plat/kvm: Solve some concurrency issues in _libkvmplat_vga_putc()



As terminal_row and terminal_column are static variables, concurrent or
interleaved execution of _libkvmplat_vga_putc can lead to inconsistent
values, which in the worst case can lead to writing outside the bounds
of the video buffer. By making local copies of those variables, working
on those copies, and copying them back at the end, we reduce the impact.
This can still lead to losing characters or full lines, but we should
never leave the buffer area.

For more robust printing, a solution should target higher up the call
chain, for example, inside coutk, so that buffers printed out cannot
interleave.

Signed-off-by: Florian Schmidt <florian.schmidt@xxxxxxxxx>
---
 plat/kvm/x86/vga_console.c | 76 ++++++++++++++++++++++++--------------
 1 file changed, 49 insertions(+), 27 deletions(-)

diff --git a/plat/kvm/x86/vga_console.c b/plat/kvm/x86/vga_console.c
index f565b1c..1e6e4be 100644
--- a/plat/kvm/x86/vga_console.c
+++ b/plat/kvm/x86/vga_console.c
@@ -155,53 +155,75 @@ static void vga_update_cursor(void)
        local_irq_restore(irq_flags);
 }
 
-static void vga_newline(void)
-{
-       if (terminal_row == VGA_HEIGHT - 1)
-               vga_scroll();
-       else
-               terminal_row++;
-}
-
 void _libkvmplat_vga_putc(char c)
 {
+#define NEWLINE()                           \
+       do {                                \
+               if (row == VGA_HEIGHT - 1)  \
+                       vga_scroll();       \
+               else                        \
+                       row++;              \
+       } while (0)
+
+       unsigned long irq_flags;
+       size_t row;
+       size_t column;
+
+       /* Make a consistent copy of the global state variables (row, column).
+        * This way, we can work on them consistently in this function and
+        * and prevent race conditions on them that could lead to writing
+        * outside the video memory. This doesn't make the function behave
+        * perfectly on reentrance (lines can still be overwritten by
+        * code paths running through this function concurrently), but at
+        * least we stay inside the video memory.
+        */
+       local_irq_save(irq_flags);
+       row = terminal_row;
+       column = terminal_column;
+       local_irq_restore(irq_flags);
+
        switch (c) {
        case '\a':
                break; //ascii bel (0x07) - ignore
        case '\b':
-               if (terminal_column > 0) {
-                       terminal_column--;
-               } else if (terminal_row > 0) {
-                       terminal_column = VGA_WIDTH - 1;
-                       terminal_row--;
+               if (column > 0) {
+                       column--;
+               } else if (row > 0) {
+                       column = VGA_WIDTH - 1;
+                       row--;
                }
                break;
        case '\n':
-               _libkvmplat_vga_putc('\r');
-               vga_newline();
-               break;
+               NEWLINE();
+               /* fall through */
        case '\r':
-               terminal_column = 0;
+               column = 0;
                break;
        case '\t':
                do {
-                       terminal_column++;
-               } while (terminal_column % TAB_ALIGNMENT != 0
-                               && terminal_column != VGA_WIDTH);
+                       column++;
+               } while (column % TAB_ALIGNMENT != 0
+                               && column != VGA_WIDTH);
 
-               if (terminal_column == VGA_WIDTH) {
-                       terminal_column = 0;
-                       vga_newline();
+               if (column == VGA_WIDTH) {
+                       column = 0;
+                       NEWLINE();
                }
                break;
        default:
                terminal_putentryat(c, terminal_color,
-                               terminal_column, terminal_row);
-               if (++terminal_column == VGA_WIDTH) {
-                       terminal_column = 0;
-                       vga_newline();
+                               column, row);
+               if (++column == VGA_WIDTH) {
+                       column = 0;
+                       NEWLINE();
                }
                break;
        }
+
+       local_irq_save(irq_flags);
+       terminal_row = row;
+       terminal_column = column;
+       local_irq_restore(irq_flags);
+
        vga_update_cursor();
 }
-- 
2.18.0


_______________________________________________
Minios-devel mailing list
Minios-devel@xxxxxxxxxxxxxxxxxxxx
https://lists.xenproject.org/mailman/listinfo/minios-devel

 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.