Date: Thu, 6 Mar 2008 22:38:56 -0800 From: Michael Plass <mfp49_freebsd@plass-family.net> To: Robert Watson <rwatson@FreeBSD.org> Cc: freebsd-bugs@FreeBSD.org, Michael Plass <mfp49_freebsd@plass-family.net>, FreeBSD-gnats-submit@FreeBSD.org Subject: Re: kern/119079: [patch] DDB input routine reads/writes beyond end of buffer Message-ID: <6DC910A8-BA9A-4681-9D07-B87DE7261038@plass-family.net> In-Reply-To: <20080306095345.S12811@fledge.watson.org> References: <20071227224807.40C8F1702A@shuttle.plass-family.net> <20080306095345.S12811@fledge.watson.org>
next in thread | previous in thread | raw e-mail | index | archive | help
On Mar 6, 2008, at 2:01 AM, Robert Watson wrote: > > On Thu, 27 Dec 2007, Michael Plass wrote: > >>> Description: >> The ddb input routine db_readline() includes the terminating newline >> and NUL characters in the returned buffer, but it does not take this >> into account when checking against the caller-supplied limit. >>> How-To-Repeat: >> Enter DDB and type enough characters to fill the buffer >> (120 characters). Hit enter, and then use the up-arrow key to >> scroll back through history. Note that it picks up garbage past the >> end of the original line. >>> Fix: >> The patch checks the provided lsize and decreases by two to leave >> room for the newline and NUL; it also clears these two characters, >> because some of the code paths don't provide the terminating NUL. >> (The patch also corrects a problem in history redraw when the cursor >> is not at the end of the line while scrolling back though history.) > > Dear Michael, > > Thanks for this report; I'm able to reproduce both problems, and am > currently looking at your patch. I'm going to immediately commit > the redraw fix, but did have one question about the remainder of > the patch below. > >> @@ -302,6 +302,10 @@ >> char * lstart; >> int lsize; >> { >> + if (lsize < 3) >> + return (0); >> + lstart[lsize - 1] = lstart[lsize - 2] = 0; >> + lsize -= 2; /* allow space for newline and terminating NUL */ > > You comment that you need to leave room for two bytes here -- > terminating newline and nul. Could you clarify in what cases the > NL may be missing? It looks to me like the loop around db_inputchar > () effectively ensures that we never leave that loop without the > buffer containing an NL, and that the NL should fit in the buffer. > I concur on the nul termination reading. So my question is: is it > not sufficient to simply do the following: > > lstart[lsize - 1] = 0; > lsize--; > > Thanks, > > Robert N M Watson > Computer Laboratory > University of Cambridge > >> if (lsize != db_lhistlsize) { >> /* >> * (Re)initialize input line history. Throw away any >> --- db_input_bufoverflow.patch ends here --- >> >> >>> Release-Note: >>> Audit-Trail: >>> Unformatted: >> _______________________________________________ >> freebsd-bugs@freebsd.org mailing list >> http://lists.freebsd.org/mailman/listinfo/freebsd-bugs >> To unsubscribe, send any mail to "freebsd-bugs- >> unsubscribe@freebsd.org" >> Robert, I was able to reproduce the problem in a version rigged to run in userspace. Check what happens when you just type enough to fill the buffer (or until it beeps) and then hit return. The normal chars are handled in the default case of the big switch in db_inputchar(), and when the buffer is full (db_le == db_lbuf_end), db_inputchar() returns 0. The driving loop calls again, this time with the newline, and that gets stored this way: case '\n': /* FALLTHROUGH */ case '\r': *db_le++ = c; return (1); leaving the newline in db_lbuf_end[0] and db_le == db_lbuf_end + 1, so that the terminating nul is stored at db_lbuf_end[1]. I've only lightly tested it, but upon reexamination I think better fix is to leave lsize alone in do_readline() and initialize db_lbuf_end = lstart + lsize - 2; this will assure that each of the lines in the history buffer have proper termination and storing the nul in the initialization won't be needed. The patch against 1.38 then looks like this: --- db_input.c.orig 2008-03-06 22:09:04.000000000 -0800 +++ db_input.c 2008-03-06 22:25:24.000000000 -0800 @@ -302,6 +302,8 @@ char * lstart; int lsize; { + if (lsize < 2) + return(0); if (lsize != db_lhistlsize) { /* * (Re)initialize input line history. Throw away any @@ -316,7 +318,7 @@ db_force_whitespace(); /* synch output position */ db_lbuf_start = lstart; - db_lbuf_end = lstart + lsize; + db_lbuf_end = lstart + lsize - 2; db_lc = lstart; db_le = lstart; Thanks for the care you've taken in committing this. - Michael
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?6DC910A8-BA9A-4681-9D07-B87DE7261038>