In vi, the command 'B' allows to jump to the beginning of the previous
word. Due to insufficient checks, its handling can causes currently two
out-of-bounds memory accesses.

Generally, vi allocates a buffer for the text held by the current editor
session. The char pointer 'text' points to the start of the buffer, the
char pointer 'dot' is a pointer to the current cursor location within
the allocated buffer.

When handling the 'B' command (together with 'E' and 'W') in 'do_cmd',
the condition 'isspace(dot[dir])' is checked, where 'dir == -1' for
'B'. If 'dot' points to the start of the text buffer, so 'dot == text',
this access is out of bounds. Add a check to make sure that the
relative access of 'dot' is not before the start of the buffer.

During further handling of the 'B' command, a similar out of bounds
access happens when calculating the new 'dot'. The function 'skip_thing'
invokes 'st_test' with a current pointer 'p' and a direction 'dir'. In
the same way, 'p[inc]' is accessed without previously checking whether
this memory is inside the allocated text buffer. Move the bounds check
before the invocation of 'st_test' into the while condition, together
with other checks. This also shrinks the code a bit.

On CHERI systems with fine-grained memory protection through
architectural capabilities, these out of bounds accesses caused bounds
violations.

function                                             old     new   delta
do_cmd                                              4798    4806      +8
skip_thing                                           281     262     -19
------------------------------------------------------------------------------
(add/remove: 0/0 grow/shrink: 1/1 up/down: 8/-19)             Total: -11 bytes

Signed-off-by: Chris Hofer <christian.ho...@codasip.com>
---
 editors/vi.c | 17 ++++++++---------
 1 file changed, 8 insertions(+), 9 deletions(-)

diff --git a/editors/vi.c b/editors/vi.c
index ca8541eb5..f2d3e0ac5 100644
--- a/editors/vi.c
+++ b/editors/vi.c
@@ -3502,16 +3502,15 @@ static char *skip_thing(char *p, int linecnt, int dir, 
int type)
 {
        char c;
 
-       while (st_test(p, type, dir, &c)) {
+       while
+       (
+               (dir >= 0 ? p < end - 1 : p > text) &&
+               st_test(p, type, dir, &c) &&
                // make sure we limit search to correct number of lines
-               if (c == '\n' && --linecnt < 1)
-                       break;
-               if (dir >= 0 && p >= end - 1)
-                       break;
-               if (dir < 0 && p <= text)
-                       break;
+               (c != '\n' || --linecnt >= 1)
+       )
                p += dir;               // move to next char
-       }
+
        return p;
 }
 
@@ -4180,7 +4179,7 @@ static void do_cmd(int c)
                if (c == 'B')
                        dir = BACK;
                do {
-                       if (c == 'W' || isspace(dot[dir])) {
+                       if (c == 'W' || (&dot[dir] >= text && 
isspace(dot[dir]))) {
                                dot = skip_thing(dot, 1, dir, S_TO_WS);
                                dot = skip_thing(dot, 2, dir, S_OVER_WS);
                        }
-- 
2.47.1

_______________________________________________
busybox mailing list
busybox@busybox.net
https://lists.busybox.net/mailman/listinfo/busybox

Reply via email to